Skip to content

cli: os migrate meta --json answers an empty (--from 18) or inverted (--from 19) range like a clean replay; the "replayed nothing" warning exists only on the human path #28947

Closed

Description

@objectstack-fleet

domain:devx#1 · os-warren · session_01JQwxbWBtYWqaV8NhvUrShB

Filed by the dispatching PM seat session for the W4-C3a runner session_01JQwxbZsvATYMNMArStUo2M, whose own write was refused by its session permissions (RUNNER rule 2: staged text, posted verbatim through the same door).

立卡门类别: ① 产品缺陷 — 具名落点 (packages/cli/src/commands/migrate/meta.ts, emitJson / printEmptyRangeAnswer) + 复现
reach: 公开入口实测一次错误结果 — os migrate meta --from 18 --json and --from 19 --json exit 0 with a payload indistinguishable from a real replay (three runs at 7fa9873)
业务理由: an upgrade script or CI step that reads the JSON answer believes metadata was migrated when no step ran (a wrong --from in a pipeline), while the human path warns. Not doing it: automated upgrades can skip the migration silently and report green.
动手的读者: the domain:devx seat (CLI migrate); triage first touch confirms the lane, then os-dev.
查重: "migrate meta" json → 423 hits, 2 open (#26303 skills upgrade floor, #23181 cli test budget — unrelated); "replayed nothing" → 30 hits, 1 open (#25715, unrelated CI flake). #22116 (closed) is the --out variant. No duplicate.

QA-source: #28942 · cli.migrate-meta-codemod · observed beside the clauses

What happens. os migrate meta --json answers an empty range (--from 18 on an 18 runtime) or an inverted one (--from 19) exactly like a clean replay: applied: [], todos: [], schemaValid: true, exit 0, and no field saying that no step ran. The human path prints the #17134 warning ("this run replayed nothing — that is not a finding that the metadata is canonical"); the JSON path has no counterpart. A --from above the chain terminus is also not refused, while one below the floor is. Related: #22116 (closed, the --out variant of the empty range).

Mechanism. packages/cli/src/commands/migrate/meta.ts: printEmptyRangeAnswer runs only inside printMigrationReport (human path). The emitJson payload (around lines 1209–1240) carries hops only under --step and has no empty-range marker. Nothing bounds --from above CHAIN_TERMINUS_MAJOR (18).

Reproduction (a canonical source: a scratch showcase copy already written by --write; framework 7fa9873; reproduced three times):

  1. os migrate meta --from 18 --json; echo $? → {"from":18,"to":18,…,"applied":[],"todos":[],"absentTodos":[],"schemaValid":true,…}, exit 0.
  2. The same without --json → ⚠ No migration step exists for protocol 18 → 18, so this run replayed nothing — that is not a finding that the metadata is canonical.
  3. os migrate meta --from 19 --json → the same bare payload, exit 0 (inverted range 19 → 18).
  4. Control: --from 15 → exit 1 unsupported_from_major.
  • Expected: the JSON payload says no step ran (always emit hops, or an emptyRange: true / replayedSteps: 0 field), and --from above the terminus is refused like the floor, or answered with the same marker.
  • Actual: a green-looking replay.

Full evidence: QA run record #28942.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: cli.migrate-meta-codemod

    Triage: priority:p2 · domain:server · pm:queue · 2026-11-02T05:02Z

    ⛔ Not a claim.

    • Read fresh. The fix lands in packages/cli/src/commands/migrate/meta.ts: the emitJson payload has no counterpart to printEmptyRangeAnswer, and nothing bounds --from above CHAIN_TERMINUS_MAJOR. The CLI lane is domain:server (domain:cli is a retired alias). A green JSON answer for a replay that ran nothing is a user-visible behaviour defect, so it is p2 and goes to pm:queue. The claimant names the marker it adds in the PR.

    Generated by Claude Code

  2. added
    area:devpathThe road — create, dev, verify, publish/install, connect an agent, iterate
    domain:serverDomain lane — seat card indexed by label:pm:seat
    pm:queueReady for the PM dispatch loop
    on Nov 2, 2026
  3. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#3 · os-project-manager · session_01FEkQ5kE5BrPUPNMMyv6oCH

    Claim: PM loop round 1 · card thread · 2026-11-02T05:22Z
    Session: session_01FEkQ5kE5BrPUPNMMyv6oCH (the seat); the card thread is session_01FEkQ5pLAfce3rbrPKLECmj
    Account: os-project-manager (the seat's linked user; the card's assignee)
    Branch: claude/project-thread-sj98zz (the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)
    Worktree: the card thread's own container checkout
    Domain: domain:server
    Seat: domain:server#3
    Ruling-ref: the maintainer, 2026-11-01T03:46Z: 「server 车道现在就是你负责,任务有一大堆啊,都可以派,包括 p3」, with 2026-10-26T10:29Z 「p1 p2 处理完了,就可以处理 p3」, and 2026-11-01T06:39Z 「server 队列还是很多任务,再开一个席位吧」. Seat 1 split the server queue and gave this card to seat 3 under the maintainer's 2026-11-02T05:15Z 「队列进了很多任务,抓紧派发」.
    File surface: packages/cli/src/commands/migrate/meta.ts, a cli test, a changeset
    Container & model: card thread cmsg_01TenzsRopZj4jMbvjX8uz1XL7p7YBQtLSHX1r2qftEhKw, mode:thread, model: opus (--tier names no path mandate: PM's call, default), effort: high
    Clause-②: no (expected) — if the dev measures the --json answer shape as published, the dev declares yes (widening) and a CONTRACT_REVIEW_TIER review is owed
    Metadata-protocol: none (expected; the dev measures and declares)
    Responsibility: domain:server seat 3 owns the PR to merge
    Thread-read: 6569489218
    Premise: reported by domain:devx#1 (QA #28942), reproduced three times at 7fa9873f; the dev re-measures first
    Serial constraints cleared: none (no other claimed card touches migrate/meta.ts)
    Resume-from: none — a fresh branch from current origin/main

    State: → pm:dispatched, assigned to os-project-manager, recorded by this claim. Order: pm-orders/server/order-28947.md (project folder).

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

    @objectstack-fleet
    ContributorAuthor

    domain:server#3 · os-project-manager · session_01FEkQ5pLAfce3rbrPKLECmj

    os-dev-report
    {
    "issue": 28947,
    "status": "done",
    "branch": "claude/project-thread-sj98zz",
    "pr": "#29045",
    "session": "session_01FEkQ5pLAfce3rbrPKLECmj",
    "premise_still_valid": true,
    "summary": "Measured on main 135054e with the built CLI on a canonical stack: --from 18 --json, --from 19 --json and --from 18 --to 18 --json exited 0 with applied [], todos [], schemaValid true and no member saying no step ran, while the human runs warned replayed nothing (control --from 17 replayed 448 todos). printEmptyRangeAnswer is split into one reading, readReplayedNothing(stack, result), returning null when any step ran and otherwise { reason: empty-range | inverted-range, widestRange: { from, to, applied, manual } | null }, and one renderer printReplayedNothing; the --json payload spreads replayedNothing only when non-null. Real-replay payloads and every exit code are unchanged; an inverted range is answered, not refused, and the human line for an inverted range now names the inversion (one reading, two faces). Seat review round (head ec0c642): widestRange is now also null when --from is at or above the chain terminus (an inverted range such as --from T --to T-1 used to report a widest range T to T), pinned red first; the PR body tier sentence is corrected to the declaration face at TIER_DEFAULT and the skills note is removed (the seat files it).",
    "tests": "New packages/cli/test/migrate-meta-json-replayed-nothing.test.ts (unit tier, in-process, 8 cases, ranges derived from MIGRATION_MAJORS/PROTOCOL_MAJOR). Red first on main code: 5 failed / 2 passed. Review case --from T --to T-1 and --from T+1 --to T-1: red first at 4066e3a, 1 failed / 7 passed. Green at ec0c642: 8 passed (8). Ablations via scripts/ablation-replace.mjs (src imported directly): JSON spread emptied -> 4 failed / 3 passed; reason pinned to empty-range -> 2 failed / 5 passed; fromMajor >= terminus arm removed -> 1 failed / 7 passed; each restored to its HEAD blob with git diff HEAD empty. Byte identity: --from 17 --json main vs branch 2,624,990 bytes each, md5 6edb407e with duration masked. At head ec0c642: pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2 -> Test Files 369 passed (369), Tests 5204 passed (5204), lock VERDICT command-exit 0. pnpm --filter @objectstack/cli typecheck exit 0. Integration tier declared to CI.",
    "gates": "dispatch-gates --commands --repo objectstack-ai/objectstack at ec0c642: 61 commands, 60 exit 0, check:dual-build-cjs-loads exit 3 (PREREQUISITE NOT MET: client-react, studio and other out-of-closure packages have no dist in this worktree; exit 0 at 4066e3a in a worktree that had them; CI runs it). --ran: 61 accounted for, 60 run, 1 NOT-MEASURED. check:i18n-coverage exit 0 after the example closure build. eslint --no-inline-config packages/cli exit 0 at 4066e3a (review round touched no lint-relevant shape beyond one guard line and one test case). CI check-runs on PR 29045: in_progress at report time. Record: pm-orders/server/28947-gates-ran-v2.txt.",
    "dogfood_battery": "n/a: no ! in the PR title and no (narrowing) arm",
    "mcp_calls": "3: issue_read get, issue_read get_comments, pull_request_read get (all reads)",
    "api_writes": "4 relay writes: pr_create (PR 29045), the os-dev-report comment, and in the review round two edits of this report (comment_edit), within the seat grant of +3 since the PR body edit was not written. The PR body edit (issue_patch on 29045) was REFUSED by the relay with nothing written: its validator requires a signature line as the first non-empty line, and the PR body must open with the attribution marker. The corrected body is at pm-orders/server/28947-pr-body-v2.md for the seat to write. Plus 3 git pushes (not REST).",
    "deviations": "Clause-2 measured published (skills/objectstack-upgrade/SKILL.md documents reading .applied, .todos[].surface, .specChanges off os migrate meta --json; content/docs/upgrading.mdx documents todos/absentTodos): declared yes (widening), reviewed on the declaration face at TIER_DEFAULT per the seat. File surface widened by one cli test (normalized-call-sites.test.ts census row), within the claim.",
    "files_changed": [
    ".changeset/28947-migrate-meta-json-replayed-nothing.md",
    "packages/cli/src/commands/migrate/meta.ts",
    "packages/cli/test/migrate-meta-json-replayed-nothing.test.ts",
    "packages/cli/test/normalized-call-sites.test.ts"
    ],
    "line_budget": "+298 / -18 across 4 files",
    "open_questions": [],
    "out_of_scope_findings": [
    "skills/objectstack-upgrade SKILL.md section 3.3 acceptance replay (applied must be [] cannot fail when the target major is the chain terminus): carrier: seat 3, filing it as a skills card"
    ]
    }


    Generated by Claude Code

  6. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#3 · os-project-manager · session_01FEkQ5kE5BrPUPNMMyv6oCH

    Release: claim 6569748971 (session_01FEkQ5kE5BrPUPNMMyv6oCH, card thread session_01FEkQ5pLAfce3rbrPKLECmj, branch claude/project-thread-sj98zz) · 2026-11-02T07:38Z · seat 3 shift close (maintainer 2026-11-02T07:10Z 「尽快提交代码,然后收工」, 2026-11-02T07:33Z 「pm-dispatch 中车道下班的流程,都要走完」); handed back, no stay-behind
    Produce: PR #29045, ACCEPTed (6571466009, review PASS 6571444301), ready + auto-merge, in the merge queue
    Resume-from: claude/project-thread-sj98zz@ec0c6421

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

    @objectstack-fleet
    ContributorAuthor

    domain:devx#1 · os-warren · session_01JQwxbZx1yZXiTrFXKbSdFZ

    Same class, second door (QA run record #29124, W4-L1 at 7fa9873, observed beside cli.migrate-meta-write-stored-and-all): the --stored pass has the gap this card names for --from.

    os migrate meta --stored --json --database-url file:/tmp/x/nope/absent.db (no such directory) exits 0 and answers {"scanned":0,…,"clean":true}; no file is created. The human path over the same URL warns "⚠ No rows were examined, so this run attests nothing … also what running from the wrong project root looks like" and, per the comment in packages/cli/src/commands/migrate/meta.ts, deliberately does not claim a clean bill. The JSON path does claim one.

    • Expected: the JSON answer does not attest clean: true for a run that read nothing, or carries the same warning as a field, like the fix this card asks for on the --from path.
    • Landing: the --json branch of the stored pass in packages/cli/src/commands/migrate/meta.ts, and storedMigrationClean in packages/metadata-protocol/src/stored-migration.ts.

    Added here rather than as a new card because it is the same family (a "nothing was examined" warning that exists only on the human path); one fix should cover both doors.


    Generated by Claude Code

  9. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#3 · os-steve · session_01ADHkmQJdgarUjPLNgFBY67

    Landing record · #28947 · PR #29045

    domain:server seat 3 · session_01ADHkmQJdgarUjPLNgFBY67 · 2026-11-02T08:47Z (taken over from session_01FEkQ5kE5BrPUPNMMyv6oCH, sign-off brief on #26761)

    Reading Probe Result
    Queue membership git ls-remote origin 'refs/heads/gh-readonly-queue/main/pr-29045-*' 0 refs (released)
    Ancestry git merge-base --is-ancestor c749198530 origin/main yes
    Content git grep -n "replayedNothing" origin/main -- packages/cli/src/commands/migrate/meta.ts readReplayedNothing call at line 955
    • Landed: merged as c7491985 (2026-11-02T08:37Z). os migrate meta --json now says when no migration step ran (empty or inverted range) through replayedNothing.
    • Declaration review (TIER_DEFAULT) PASS 6571444301; seat ACCEPT 6571466009. Fixes #28947 closed this card on merge.
    • State: pm:queue (left by the shift-close hand-back) removed in the same round.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:devpathThe road — create, dev, verify, publish/install, connect an agent, iteratedomain:serverDomain lane — seat card indexed by label:pm:seatpriority:p2Medium: important, M3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions