feat(apiserver): surface human-readable job ID (fmt_jobid form) in responses

The MCP/apiserver returned the raw 64-bit job ID (e.g. 73930964699447296)
while xce_test/xcecli display the dash form (e.g. 1-0-1786585485-0) that
parse_jobid accepts. A user taking the numeric form into xce_test fails to
parse it. The apiserver is the data-correctness owner (MCP only reads), so
it now emits the controller's fmt_jobid form alongside the numeric ID:

- models: JobSubmitResponse gains job_id_human; job list rows gain
  job_id_human (processJobData), using the existing backend.FormatJobID.
- schedulerclient: URMJob.HumanID + SubmitJobResult.JobIDHuman parse it.
- Tests: backend.FormatJobID (real t1 job, pre-cluster1 job, array child);
  processJobData adds job_id_human.

Also rename the SSE permission event:subscribe → event:view so mcp_readonly
stays strictly *:view (the read-only invariant test enforces).

Verified live on t1: scheduler_submit returns job_id_human 1-0-1786586474-0
and scheduler_get_job returns human_id.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
lei.chen 2026-08-13 02:01:52 +00:00
parent 3d1560d69c
commit 673f2eb792
9 changed files with 135 additions and 10 deletions

View file

@ -1515,9 +1515,10 @@ func (j JobController) CreateJob(ctx *gin.Context) {
logger.Info("Job submission successfully from ", zap.String("client_ip", ctx.ClientIP()), zap.Int64("jobID", primaryJobID))
response := models.JobSubmitResponse{
Message: fmt.Sprintf("Job <%d> is submitted to default queue <%s>.", primaryJobID, req.QueueName),
JobID: primaryJobID,
QueueName: req.QueueName,
Message: fmt.Sprintf("Job <%d> is submitted to default queue <%s>.", primaryJobID, req.QueueName),
JobID: primaryJobID,
JobIDHuman: backend.FormatJobID(primaryJobID),
QueueName: req.QueueName,
}
ReturnSuccess(ctx, common.CREATED, common.SUCCESS_MSG, response)
}
@ -3144,6 +3145,28 @@ func jobArrayIdx(row utils.OrderMap) int {
func processJobData(rows []utils.OrderMap, pagetoken string) utils.QueryResult {
logger.Debug("Enter processJobData() function", zap.Any("rows", rows))
for _, row := range rows {
// Human-readable job ID (cluster-apiserver-timestamp-seq) so clients
// (MCP server, CLI) can cross-reference with the xce_test/xcecli dash
// format, which parses only that form. Data correctness lives here,
// not in the clients. FormatJobID is the apiserver's own C fmt_jobid
// equivalent (backend.FormatJobID).
if uid, exist := row.Data["unique_job_id"]; exist {
var jobID int64
switch v := uid.(type) {
case int64:
jobID = v
case int:
jobID = int64(v)
case int32:
jobID = int64(v)
case float64:
jobID = int64(v)
}
if jobID != 0 {
row.Data["job_id_human"] = backend.FormatJobID(jobID)
}
}
if v, exist := row.Data["job_status"]; exist {
if v != nil {
jobStatus, _ := utils.IntOrInt64(v)