Skip to content

runtime/service-job: jobKeyFor on the package coordinate, keeping the bare key readable (ADR-0150 child E7) #31679

Closed

Description

@objectstack-fleet

epic:#31527#1 · os-project-manager · session_01FEkQ5mLd7pR2PHjsPwiGkz

Filing gate: ④ coordination node: child E7 of the epic #31527 (parent and per-layer children), as ADR-0150 (docs/adr/0150-behavioural-metadata-addressed-by-package-objects-keep-the-literal-prefix.md, merged in #31586 at 5d1382a)'s execution plan lists it. Hand-over card: filed undispatched as the epic seat signs off (maintainer 2026-11-08T16:34Z 「专题卡当前任务处理完,合并后就下班」). ⛔ Not a claim.
Ruling-ref: per-type table A (6723462584); epic A 「立专题」 (6723499113); division of labour A 「留核心,其余交车道」 (6723571682); ADR-0150 accepted by merge (#31586, 2026-11-08T21:16Z).
Business reason: inherited from the parent #31527. Behavioural metadata is addressed by (package, name), so two installed packages can each ship an item of the same name and every reader answers the right one, or refuses with the candidates, instead of silently taking the first match.
Reader: the next domain:server seat picks it from the queue (pm-dispatch selection) and accepts its PR against the red lines below.
Dedupe (open AND closed): jobKeyFor (2; top hits #31527(open) #21602(closed), none on point: they are this epic, its parent #30806, or unrelated cards).
class: d · type: Feature
Grade: p1, inherited from the parent (self-graded under 「定级你可以自己决定」).
Blocked-by: #31575

维护者速读

定时任务的键带上软件包坐标,旧的裸键继续可读。

What to build

jobKeyFor reads the package coordinate; the first holder keeps the bare key and a second keeps its APPID:jobName, as today.

Surface

  • Expected file surface: runtime app-artifact-handlers.ts, service-job.
  • Clause-②: no.
  • Narrowing: no: the first holder keeps the bare key and a second keeps its APPID:jobName, as jobKeyFor gives them today.

Acceptance — the epic's three red lines (every child shares them)

  • Resolution never loosens. No first-registered fallback, no environment-wide guess for a caller with a package, no silent pick.
  • Ambiguity is reported, never guessed. Every refusal and every migration report names every candidate.
  • Independently releasable (red line 3). The PR proves it with ADR-0150's four readings: (a) the phase-1 one-holder tests stay green; (b) bare-name behaviour (URLs, MCP, SDK) is unchanged for every name with one holder, except this card's declared narrowing; (c) any migration is re-runnable and never guesses; (d) its Clause-②: declaration and, where it changes the protocol, its ADR-0087 migration entry.
  • The epic seat is off duty after hand-over: the accepting lane seat checks these lines itself, and a narrowing beyond the declared one goes to the maintainer before landing.

Parent: #31527.

Activity

  1. added
    enhancementNew feature or request
    priority:p1High: required for production / M2
    pm:blockedBlocked by another issue/PR — body carries Blocked-by: #N
    area:devpathThe road — create, dev, verify, publish/install, connect an agent, iterate
    domain:serverDomain lane — seat card indexed by label:pm:seat
    on Nov 8, 2026
  2. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: confirmed · domain:server · p1 · pm:blocked · 2026-11-08T21:58Z

    ⛔ Not a claim.


    Generated by Claude Code

  3. added
    pm:blockingDerived cache from the Blocked-by reverse index: open card with open dependents — never hand-set
    on Nov 8, 2026
  4. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#31527#1 · os-steve · session_01ADHkmP7o3uDTti21wLTfPf

    Unblocked 2026-11-08T23:02Z: Blocked-by: #31575 is closed. R merged in #31637 as 0ebb080 (resolvePackageScopedName, packageResolutionClosure and the three read-only SchemaRegistry package-scoped methods in objectql; packageDependencyClosure in metadata-protocol). pm:blocked → pm:queue in this stroke. Read the resolver through those exports only, and never re-implement the rule. The epic seat reviews this card's PR against the three red lines before it lands.

  5. added
    pm:queueReady for the PM dispatch loop
    and removed
    pm:blockedBlocked by another issue/PR — body carries Blocked-by: #N
    on Nov 8, 2026
  6. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#3 · os-steve · session_01ADHkmQXLfvyB5R53VDm3Ka

    Claim: PM loop round 1 · #31679 (E7, epic #31527) · 2026-11-08T23:09Z
    Session: session_01ADHkmQXLfvyB5R53VDm3Ka (seat); dev = card thread session_01ADHkmNp8ARUNvemy119dY1
    Account: os-steve
    Branch: claude/project-thread-znr079
    Worktree: the card thread's checkout of objectstack-ai/objectstack
    Domain: domain:server
    Seat: domain:server#3
    File surface: packages/runtime/src/app-artifact-handlers.ts (jobKeyFor and its callers), packages/services/service-job/src/**, their tests, .changeset/31679-*.md (stop on breach; ⛔ no packages/spec edit, no second resolver: R's exports only)
    Container & model: M, mode:thread, model: default tier; CONTRACT_REVIEW_TIER for the review owed before the queue (dispatch-gates --tier: a card that only NARROWS owes one contract-review-tier review, judged from the card content), effort: high
    Clause-②: no
    Metadata-protocol: none
    Responsibility: n/a — not a defect card
    Thread-read: 6728384417
    Premise: holds (R #31637 merged as 0ebb080; the card's Blocked-by: #31575 closed, moved to pm:queue by the epic seat in 6728384417; the dev confirms the resolver exports on main in step 0)
    Narrowing: no — the card declares none: the first holder keeps the bare key and a second keeps APPID:jobName, as today; the dev shows identical keys on base and head for the same inputs
    Serial constraints cleared: seat 4's E2 #31673 touches runtime action-execution.ts, not app-artifact-handlers.ts: disjoint. S3 #31684 follows E1, E5 and E7 on this seat.

  7. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 8, 2026
  8. objectstack-fleet commented on Nov 9, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#3 · os-steve · session_01ADHkmNp8ARUNvemy119dY1
    os-dev-report

    {
     "issue": 31679,
     "status": "done",
     "branch": "claude/project-thread-znr079",
     "pr": "https://github.com/objectstack-ai/objectstack/pull/31730",
     "session": "session_01ADHkmNp8ARUNvemy119dY1",
     "premise_still_valid": true,
     "summary": "jobKeyFor now takes the job's package coordinate {packageId, name} (ADR-0150 D2/D4). Before falling back to this run's scheduling order, it reads the job catalogue (DbJobAdapter.readArming, optional and adapter-local, already in service-job) for the coordinate's own PACKAGE_ID:NAME key, and keeps that key when it is active. The first holder keeps the bare key and a second its PACKAGE_ID:NAME, as before, and that now holds across a restart that rehydrates packages in another order. Before, the bare key and its run history moved to whichever package the boot scheduled first. A job stored under its bare key is scheduled under it again and fires (the card's pin). No resolver call site: E7 resolves no bare name to a package, because a job is scheduled by its own package. R's resolvePackageScopedName is not called and no second resolver is written. Head ffa24bd0. The PR has no assignee: os-dev forbids assignees on pr_create, and MCP writes are barred.",
     "tests": "Step 0 / Clause-② key table, the same scratch harness on base b126c130 and head fc99b124, 10 inputs: S1-S7 and S10 byte-identical; S8 (restart scheduling the second package first: base hands the bare key to the second package) and S9 (restart after the bare holder's uninstall: base moves the scoped holder to the bare key) differ, and head keeps the catalogue's key. Full table in the PR body. New describe 'ADR-0150 E7 ...' in app-artifact-handlers.jobs.test.ts, 6 cases; file total 41 passed on ffa24bd0. Ablation via ablation-replace.mjs (catalogued forced false = the first scheduler takes the bare key): anchor 1->0, blob 1fb1fd13059d->e0c1c52e8cc3; 3 failed | 38 passed; restored, blob == HEAD, git diff HEAD empty. Source-imported test, so no dist leg. On fc99b124: runtime test 563 files, 9045 passed / 19 skipped, VERDICT 0; runtime typecheck VERDICT 0; service-job test 11 files, 121 passed, VERDICT 0; service-job typecheck VERDICT 0. Forced test edit in ffa24bd0: a module-top import '@objectstack/metadata' that check:test-source-alias prescribes for the edited file.",
     "gates": "dispatch-gates --commands on final head ffa24bd0: 64 derived, 64 run, all exit 0. --ran reconciliation: 64 derived, 64 run, 0 NOT-MEASURED, 0 UNRUN (exit 0). Round 1 on fc99b124: check:test-source-alias exit 1 (fixed in ffa24bd0); check-plugin-teardown-shape --self-test exit 3 (fixture 621a4876 fetched, then exit 0); check:dual-build-cjs-loads exit 3 (packages built, then exit 0). Lint: npx eslint --no-inline-config packages/runtime exit 0 (runtime and service-job have no lint script; service-job is not edited). CI check-runs: not awaited, in_progress at report time.",
     "dogfood_battery": "fc99b124 · VERDICT command-exit 0 (1855s under lock) · Test Files 440 passed | 3 skipped (443) · Tests 3652 passed | 41 skipped (3693) · no failures. Run because the delta names dogfood; the title has no ! and there is no (narrowing) arm.",
     "mcp_calls": "4 — mcp__github__issue_read x4 (get #31679, get_comments #31679 x2, get #31527); no write tool",
     "api_writes": "2 — fleet relay pr_create (objectstack-ai/objectstack#31730, 11041 bytes sent and stored, identical); this os-dev-report comment via post-stamped.mjs. Plus git push x2 (not REST).",
     "open_questions": [
      {
       "question": "The claim says 'show identical keys on base and head for the same inputs'. 8 of 10 measured inputs are identical. S8 and S9 (restarts) differ, because base moves a key that the catalogue holds for one package to another package or name. Is keeping it within the card's no-narrowing declaration?",
       "options": [
        "A: keep the catalogue read. It is what 'the old bare key and APPID:jobName keys stay readable for stored jobs' and ADR row D4 ('the bare key stays readable for the one holder that has it today') ask; nothing is refused, so it is no narrowing",
        "B: drop the catalogue read and ship a pure re-key (S8/S9 stay as on base); file the restart key move as its own card"
       ],
       "recommendation": "A, because without it the card's own pin (stored keys stay readable) holds only when the rehydrate order equals the install order, which the #21602 integration test arranges by choosing ids that sort that way."
      }
     ],
     "security_findings": "none",
     "out_of_scope_findings": [
      "carrier: none · noted, not filed: two scheduleAppArtifactJobs calls for the same job name that interleave (both before either claimJobKey) can both take the bare key. This is pre-existing (base checks in-process synchronously, then awaits schedule). This change awaits the catalogue read BEFORE that check, so the window is no wider.",
      "carrier: S3 #31684 · noted: R14 (sys_job.name follows the aligned jobKeyFor key) can now rely on the key being stable across restarts."
     ],
     "files_changed": [
      ".changeset/31679-job-key-package-coordinate.md (+12)",
      "packages/runtime/src/app-artifact-handlers.ts (+117 -31)",
      "packages/runtime/src/app-artifact-handlers.jobs.test.ts (+160)"
     ],
     "line_budget": "289 added, 31 deleted, 3 files (Effort M). Source: +117/-31; tests: +160; changeset: +12.",
     "deviations": [
      "service-job (named in the card's file surface) is not edited: readArming already exists on DbJobAdapter, and the runtime reads it structurally, as trigger-schedule does.",
      "Suites ran on fc99b124. The final head ffa24bd0 adds only a module-top test import, re-run on the edited file (41 passed); the 64 gates ran on ffa24bd0.",
      "No subscription to PR activity and no CI wait, per the os-dev contract."
     ]
    }

    Stamped 2026-11-09T00:29Z.

  9. objectstack-fleet commented on Nov 9, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#3 · os-steve · session_01ADHkmQXLfvyB5R53VDm3Ka

    Landed · #31679 (ADR-0150 E7) · 2026-11-09T01:18Z

    Reading Value
    PR #31730, merged 2026-11-09T01:18:00Z, merge commit f2abd476, from head ffa24bd0 (seat ACCEPT 6729641661, epic PASS 6729440308)
    Card closed completed by Fixes #31679

    State: pm:dispatched removed in this stroke. S3 #31684 still waits on E1 #31672 and E5 #31677.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:devpathThe road — create, dev, verify, publish/install, connect an agent, iteratedomain:serverDomain lane — seat card indexed by label:pm:seatenhancementNew feature or requestpm:blockingDerived cache from the Blocked-by reverse index: open card with open dependents — never hand-setpriority:p1High: required for production / M2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions