Skip to content

[finding][devx] os migrate meta --from 17 buries a project's real findings under 240 generic protocol-18 notices, and marks default-flip conversions as "Applied" when the right action is usually no edit #20620

Description

@hotlong

Observed on a real upgrade: HotCRM 17.4.0 → 17.5.0, 2026-09-29

os migrate meta --from 17 printed 874 lines for HotCRM. It ended with "Migrated stack does not yet pass schema validation". The output contained:

  • 41 refusals (the actionable part);
  • 57 mechanical edits;
  • 240 "manual change" notices. These are the entire protocol-18 catalogue (WebSocket durations, kernel.advancedPluginLifecycle, …). None of them applies to this stack.

Three problems for an upgrader:

  1. Noise. The 41 refusals that block boot are hard to find among 240 notices about surfaces the stack does not use.
  2. Default-flip conversions are presented as edits to apply. The 13 flow-decision-mode-inclusive-explicit edits appear as "Applied". But for a decision whose branch conditions partition, the right action is no edit. Applying the offered mode: 'inclusive' literally then triggers the flow-decision-inclusive-overlap lint on every one. All 13 of HotCRM's decisions partition.
  3. No write-back. The mechanical edits say "apply them by hand", so lossless edits still have to be transcribed manually.

Asks

  1. Filter the notices to the surfaces the stack actually uses, or at least group them after the actionable findings.
  2. Mark semantic default flips as review items, not "Applied".
  3. Offer a source-writing mode for lossless mechanical edits.

Related, different concern: #20233 (tracker numbers in the migration text).


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    Contributor

    ACCEPT: PR #20691 at 8304fb9e, direction 1 of #20620 (order)

    domain:cli execution PM seat #6024 · session local_1d2a197c-c20e-4e90-9be8-413d4d432289 · review of record, written 2026-09-29T18:12Z

    Reviewed against GitHub and main, not against the report.

    What this PR executes: triage's direction 1 (5888087153). os migrate meta leads with the verdict and the refusals, then the applied mechanical edits, then the semantic notices, with one header line counting each group. ⛔ No notice is dropped. Direction 2 is split to #20697, and direction 3 is out (5895765767).

    Checklist:

    • PR shape: draft, base main. The first line is Part of #20620, followed by a line-initial Clause-②: no. There is no closing keyword, because the card stays open for direction 2.

    • Scope: 3 files, all inside the claim: packages/cli/src/commands/migrate/meta.ts, one new unit test beside it, and one changeset (@objectstack/cli patch). check-governed-merges --pr 20691: NOT governed, +462 −78.

    • Review route: changeset prose is a contract-review surface. The at-tier record on the PR is PASS at this head. It found:

      • order and headers only: every applied-edit and notice line keeps its bytes and its in-group order, and the set pins compare against chain-derived arrays, so nothing can drop unseen;
      • the one addition is right: the refusal group lists the MIGRATED stack's parse issues, the ones that actually block it, rendered by the loader's own formatZodIssue. It is the smallest execution of "lead with the refusals", since the printer had no refusal list;
      • other surfaces unchanged: --json, the exit codes, --out and the loader's stderr;
      • the reworded sentences: both are true in the new order;
      • patch and Clause-②: no: right;
      • the ablation: it reddens exactly the 3 order pins, while the header-count and 4 set pins stay green.

      The seat verified its transcript: served at CONTRACT_REVIEW_TIER, read-only, no GitHub write.

    • Evidence: real stacks were measured before and after through bin/run.js:

      • the h1 stack's verdict moved from line 780 to line 9;
      • the applied and notice counts are equal and in the same order (744 = 744, 996 = 996);
      • --json is identical apart from duration;
      • --out and stderr are byte-identical, and every exit code is 0 both times.
    • Commits: every non-merge commit carries the model-free trailer pair.

    Out-of-scope findings and the review's escalations, one line each:

    • os migrate meta aborts at load on an ObjectSchema.create object that carries a retired key: filed as [finding] os migrate meta aborts at load on an object built with ObjectSchema.create that carries a retired key, although the refusal it prints tells the author to run os migrate meta #20696.
    • docs/qa/platform-checklist/areas/cli.json, item cli.migrate-meta-codemod (revision 3), step 7 quotes the old verdict sentence verbatim. It is stale once this lands, although its acceptance clause still holds. It needs a revision-4 bump by the checklist's author, naming the new refusal group: Acceptance notes, for the checklist's owner.
    • skills/objectstack-upgrade/SKILL.md:139-147 lists the groups in the old order, with no order claimed. It is Tier H governed, so a one-line reorder waits for that surface's next queue entry: Acceptance notes.
    • check:platform-checklist is red on main (twoFactor). It is pre-existing and maintainer-run, not this PR's.
    • The macOS TMPDIR pins are host-only.
    • Noted for the lane: on one terminal an upgrader now sees two refusal lists, the loader's as-authored list on stderr and the report's post-chain list, with different counts. The [authored-source] prefix and "left after the chain" keep them apart.

    Next: landing once CI is green, as markPullRequestReadyForReview + enablePullRequestAutoMerge (SQUASH) as objectstack-fleet[bot].

  2. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    Contributor

    Landed: PR #20691 → 3b47a693c7 (direction 1). State change: pm:dispatched → pm:blocked

    domain:cli execution PM seat #6024 · session local_1d2a197c-c20e-4e90-9be8-413d4d432289 · landing record, written 2026-09-29T18:29Z · the claim 5894939836 stands; the assignee stays; this card is followed to MERGED

    Blocked-by: #20697

  3. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    Contributor

    Release: the block is met, so the card goes back to pm:queue for direction 2 (the printer's pairing)

    domain:cli seat · session_01VvcEokUG1tvVxkceYfR5XB · 2026-09-30T17:43Z

    Release: session session_01VvcEokUG1tvVxkceYfR5XB, for the claim 5894939836. That claim is the predecessor seat's (session local_1d2a197c-c20e-4e90-9be8-413d4d432289), carried to this seat by its closing brief 5903478470 on the seat post #6024.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop round 1 of the domain:cli seat's session session_01VvcEokUG1tvVxkceYfR5XB (batch 3): priority:p3 tooling (the published os migrate meta surface), direction 2 of triage's first grade (the pairing), filling a free slot under the maintainer's instruction 「任务很多,并发保持3」
    Session: session_01VvcEokUG1tvVxkceYfR5XB
    Account: huangyiirene
    Branch: claude/issue-20620-migrate-meta-pairing
    Worktree: objectstack-issue-20620
    Domain: domain:cli
    Seat: domain:cli#1
    File surface:

    • packages/cli/src/commands/migrate/meta.ts, the human printer only (printMigrationReport, about :326–:395 on dfe5a0863f):
    • Tests in packages/cli/, beside the printer's existing unit test.
    • One .changeset/20620-*.md for @objectstack/cli (patch).
    • Read only: packages/spec/** (chain.ts, types.ts, the two authored links).
    • ⛔ Not here: write-back (direction 3, not taken by triage); authoring new conversionIds links (spec lane).
      (stop on a breach outside these; explain in the report)
      Container & model: S, mode:subagent, model: opus (dispatch-gates --tier: no path-derived mandate, floor sonnet · default opus · ceiling fable).
      Clause-②: no
      Thread-read: 5916560364
      Serial constraints cleared: No open PR touches packages/cli/src/commands/migrate/ (the file lists of all 21 open PRs were read in this act, main dfe5a0863f). PRs #20988, #20990 and #20998 (spec lane) add semantic entries under packages/spec/src/migrations/, which is read-only here; none of them authors a conversionIds link. In flight on this seat: #20825 (packages/cli compile / validate / utils, packages/lint) and #20963 (packages/rest error-response.ts), both disjoint. No other card on area:devpath is in flight (#20892 is pm:blocked).

    Taken 2026-10-01, main dfe5a0863f. The block #20697 was met at the release 5916560364.

    • On main: SemanticMigration.conversionIds is declared at packages/spec/src/migrations/types.ts:73. Two links are authored: flow-decision-edge-branching-first-match → flow-decision-mode-inclusive-explicit, and time-default-zone-refused → time-default-utc-suffix-dropped.
    • The gap: printMigrationReport reads neither link.
    • The PR closes the card with Fixes #20620.

    Generated by Claude Code

  5. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    Contributor

    os-dev-report

    {
      "issue": 20620,
      "status": "done",
      "branch": "claude/issue-20620-migrate-meta-pairing",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/21025",
      "session": "session_01VvcEokUG1tvVxkceYfR5XB",
      "premise_still_valid": true,
      "summary": "Direction 2 (pairing) is implemented in the human printer only. A new printAppliedEdits in packages/cli/src/commands/migrate/meta.ts prints, under each run of applied edits by one conversion, one review line per semantic entry that declares that conversion in conversionIds: '↳ review the N edits above against the manual change [protocol M] surface → replacement'. The join reads declared links only, never prose. Group 3 keeps every entry byte-identical with its count unchanged (a copy, not a move). --json, --out, exit codes, loader stderr and --stored are untouched. H1 holds: todos are { ...s, toMajor }. Two links exist on main (flow-decision-edge-branching-first-match -> flow-decision-mode-inclusive-explicit, time-default-zone-refused -> time-default-utc-suffix-dropped), both same-hop (18 -> 18). Cross-hop pairs by construction over the flat arrays, and a pin proves it with an injected 18 -> 17 link. H2 holds as a copy, and no move is proposed. H3: one line per contiguous run, counting the edits, because a conversion's edits are contiguous by construction; per-edit would repeat a paragraph 13 times on HotCRM. H4: --stored prints formatStoredMigrationReport, not this printer, so it is not touched. The branch merged origin/main (c6622657f2) to clear the dispatch-gates STALE TREE warning, and every verification was re-run on that head. The harness attribution reminder asked for a model-named Co-Authored-By trailer and a different PR footer. AGENTS.md wins: the commits carry the model-free pair, and the PR body ends with the session-URL footer.",
      "tests": "At c6622657f2: vitest --project unit meta.report-order.test.ts -> 'Tests 16 passed (16)' (8 existing + 8 new PAIR pins). The unit neighbours (doctor-deprecation-hint-commands, meta.stored-flags, migrate-meta-strict-factories, report-order) -> 'Test Files 4 passed (4) / Tests 41 passed (41)'. The integration-tier migrate-meta tests (default-range, engine-guidance, e2e) are declared to CI; their group-2 assertions are toContain only. pnpm --filter @objectstack/cli typecheck -> exit 0, and the test file is in the tsc --noEmit program (--listFiles 1 hit). Ablation: the fix was committed first (19563f6c4e). node scripts/ablation-replace.mjs swapped 'const judges = judgesByConversion(result.todos);' for an empty Map: anchor x1->x0, replacement x0->x1, blob 868eef7e8983 -> 542c67cdfca2. The subject is imported relatively from source, so no dist was involved. Result 'Tests 5 failed | 11 passed (16)'. Red: judge-under-run, run-of-one, nothing-but-groups+reviews, every-declared-link, cross-hop. Green: 8 order/count/set pins plus 3 controls (unjudged edit unchanged, no-link stack has zero review lines, group 3 intact). Restore proven: blob 868eef7e8983 == HEAD and git diff HEAD empty. Expected direction (red) observed.",
      "mcp_calls": "0",
      "api_writes": "3 — all through scripts/pm via the fleet-write relay (each one POST /repos/objectstack-ai/objectstack/dispatches): (1) pr_create -> POST /repos/objectstack-ai/objectstack/pulls (draft #21025, 10836 bytes sent = stored); (2) label-write --assign -> POST /repos/objectstack-ai/objectstack/issues/21025/assignees (read-back: assignee huangyiirene, labels size/m from the size labeler); (3) post-stamped --comment=20620 -> POST /repos/objectstack-ai/objectstack/issues/20620/comments (this os-dev-report). git push is not REST. Reads: REST GET of issues 20620/20697, their comments, PR 21025 and its check-runs.",
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: the next Tier H queue entry for skills/objectstack-upgrade/SKILL.md (the group-order reorder the prior ACCEPT 5895943032 already left there) · noted, not filed — lines 139-147 describe 'Applied N mechanical change(s)' as one line per rewritten site; a judged run now also carries one review line",
        "carrier: 承接者:无 · noted, not filed — the printMigrationReport docblock cites '242 notices for protocol 18' as direction 1's measurement; today's chain prints 253 (a dated reading, nothing depends on it)"
      ],
      "gates": "dispatch-gates --repo objectstack-ai/objectstack --commands (no paths) at c6622657f2, merge base 9b0de7de73: 62 commands, each run with its exit code recorded before any pipe; 62/62 exit 0. --ran: 'Run reconciliation — 62 derived, 62 run, 0 NOT-MEASURED, 0 UNRUN.' Round 1 at dab37c22d0 ran into PREREQUISITE NOT MET (exit 3) on check:dual-build-cjs-loads and check:i18n-coverage. They went green after pnpm turbo run build --filter=!@objectstack/docs. Round 1's tree was then 6 commits stale, so the branch merged main, and round 2 re-derived the identical 62 and ran them all on c6622657f2. At reconciliation origin/main was again at least 3 commits ahead, with one family file (scripts/check-platform-checklist.mjs) changed upstream. That family reads docs/qa/platform-checklist, which this diff does not touch. Lint, as a proven narrowing at c6622657f2. (1) Population, read from eslint.config.mjs through the ESLint API (isPathIgnored + calculateConfigForFile, inline config off): meta.ts LINTED, 6 rules; meta.report-order.test.ts LINTED, 5 rules; the changeset is in no population. (2) json format count: 2 files, 0 errors, 0 warnings. (3) Invariance: no parserOptions.project in any of the 7 parserOptions blocks (no type-aware linting), and the only disk reads at config load are two baselines this diff does not touch, so no untouched file's verdict can move. CI on the PR at report time: 31 check runs, 9 success, 3 skipped, 19 in_progress, 0 failed (not awaited).",
      "deviations": [
        "Merged origin/main into the branch (merge commit c6622657f2) after dispatch-gates flagged a STALE TREE. The union, the pins, the typecheck, the lint narrowing and the real-stack runs were all repeated on that head.",
        "pnpm lint itself was not run. The os-dev proven narrowing (population, json count, invariance) is reported instead; the full scan is CI's.",
        "The pins extend meta.report-order.test.ts rather than adding a new file, to reuse its run, appliedLines and noticeLines helpers and keep every printer pin in one file.",
        "The PM's claim and release scratch files live in the same scratchpad issue-20620/ subdirectory this run used. This run's fullbuild.pid file disappeared mid-run, so the PID was re-read from the process table and the lock's --status. No work was lost.",
        "The first full build was wrapped as a ';'-joined -c command, so the lock's VERDICT read 'batch-last-exit'. The build's own exit was captured to a file (BUILD-EXIT=0). The post-merge rebuild used a single -c command, with VERDICT command-exit 0."
      ],
      "files_changed": [
        "packages/cli/src/commands/migrate/meta.ts (+judgesByConversion, +printAppliedEdits, printMigrationReport docblock; +77/-10)",
        "packages/cli/src/commands/migrate/meta.report-order.test.ts (PAIR pins, run() gains an optional amend; +243/-5)",
        ".changeset/20620-migrate-meta-review-pairing.md (@objectstack/cli patch, Clause-②: no; +24)"
      ],
      "repro": {
        "stacks": "two scratch configs (export default object literals), run as node packages/cli/bin/run.js migrate meta --from 17 [--json] [--out]",
        "before_315888d660": "decision stack: line 13-14 '• flows[0|1].nodes[1].config.mode: mode unset (2 conditioned out-edges; every one whose condition held was taken) → inclusive (flow-decision-mode-inclusive-explicit)'; the judging entry '⚠ [protocol 18] flow.nodes[].config.mode (decision) — …' printed at line 351; 813 lines",
        "after_c6622657f2": "the same two edit lines, then line 15 '      ↳ review the 2 edits above against the manual change [protocol 18] flow.nodes[].config.mode (decision) — an OMITTED mode … → nothing, where the out-edge conditions partition …'; 814 lines. Time stack: the same shape under the 2 time-default-utc-suffix-dropped edits, '↳ review the 2 edits above against the manual change [protocol 18] a literal `defaultValue` with a `Z` or a UTC offset on a `time` field …'",
        "invariants": "for both stacks the stdout diff, with timing and the --out path normalised, is exactly that one added line; group 3 has 253 entries = header 253 before and after; --json is identical apart from duration; the --out snapshot is byte-identical; stderr is 0 bytes in both runs; exit codes are 0 in both runs"
      }
    }

    Generated by Claude Code

  6. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    Contributor

    ACCEPT: PR #21025 at c6622657 (direction 2: os migrate meta prints the semantic entry that judges an applied conversion beside its edits, marked review)

    domain:cli seat · session_01VvcEokUG1tvVxkceYfR5XB · 2026-10-01T02:40Z

    • Contract review of record: 5923632034 on the PR, CONTRACT_REVIEW_TIER, head c6622657, PASS.
      • Declared links only:
        • judgesByConversion reads todo.conversionIds and no prose.
        • Both links on main pair, and a pin enumerates every declared link at test time, so a link authored later is covered the day it lands.
      • One review line per contiguous run of one conversion's edits:
        • The runs are contiguous by construction: one apply per conversion per step, and conversion ids are unique to their own major.
        • A split run would still get its own line, so nothing depends on it.
      • ⛔ No notice dropped:
        • Group ③ keeps every entry byte for byte, under an unchanged header count (253 on today's chain).
        • The review line is a copy of the entry's headline, not a move, which is what the claim 5922717794 requires.
      • Unchanged faces:
        • --json returns before the human branch.
        • --out, the exit codes and the loader's stderr are untouched.
        • --stored prints formatStoredMigrationReport and never reaches this printer.
        • No test on main asserts group ② line by line.
      • Semver: patch with Clause-②: no is right.
    • Seat verification on adoption:
      • At the head, judgesByConversion is at meta.ts:313 and printAppliedEdits at :343, both module-private.
      • A local git merge-tree against the current origin/main (2f2fa11d75) is clean.
    • Checklist:
      • Draft, base main, first line Fixes #20620, Clause-②: no.
      • 3 files, +344 / −15: the printer, its existing pin file, and one changeset. That is exactly the claim's surface. NOT governed.
      • 34 check-run names on the head: 31 success, 3 skipped (path-filtered or opt-in), 0 red. All seven required contexts are green.
      • The non-merge commits carry the model-free trailer pair.
    • Out-of-scope findings and the record's notes:
      1. skills/objectstack-upgrade/SKILL.md:139-140 reads "one line per rewritten site" under Applied N mechanical change(s). A judged run now carries one review line too. Carrier: the Tier H entry that already holds the group-order reorder from ACCEPT 5895943032. It is Acceptance notes; this seat touches no governed surface.
      2. The printMigrationReport docblock cites "242 notices for protocol 18", a dated reading; today's chain holds 253. Carrier: the next touch of meta.ts. Nothing depends on it.
      3. Test-side nits (non-blocking):
        • the review-line helper assumes a one-line replacement;
        • the existing SET pin over FINDINGS_STACK would red the day the spec lane links one of that stack's conversions.
          The pins explain both; noted.
      4. A cross-hop link pairs only on a run that crosses both hops. That is by the data model, and no such link exists today. Noted.
    • Next: land through the queue. At the merge, this card's last direction is in. The seat closes the card if the Fixes does not, and removes pm:dispatched.

    Generated by Claude Code

  7. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    Contributor

    Landed: PR #21025 → 6073bb96b8 (direction 2: os migrate meta prints the semantic entry that judges an applied conversion beside its edits, marked review). The card is closed by the Fixes, and the seat removes pm:dispatched

    domain:cli seat · session_01VvcEokUG1tvVxkceYfR5XB · 2026-10-01T04:12Z

    • Landing reading: 6073bb96b8 is on origin/main as a single-parent squash over 5d5e679873.
      • git diff --stat of the PR head c6622657f2 against 6073bb96b8, over the PR's 3 files, is empty.
      • judgesByConversion appears on 2 lines of packages/cli/src/commands/migrate/meta.ts on origin/main, against 0 at the parent.
    • The card's three directions, all settled:
      • direction 1 (order) landed as 3b47a693c7;
      • direction 2 (pairing) is this landing;
      • direction 3 (write-back) was not taken, by triage 5888087153.
    • State: the merge closed this card as completed. pm:dispatched is removed in the same act as this note. domain:cli, area:devpath, tooling and priority:p3 stay.
    • Carried on, with no carrier owed by this card:
      • skills/objectstack-upgrade/SKILL.md:139-140 rides with the Tier H entry that already holds the group-order reorder.
      • The docblock's "242 notices" reading rides with the next touch of meta.ts.

    Generated by Claude Code

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:cliRetired 2026-10-22, alias of domain:server (cli surface); in-flight cards keep it until they landpriority:p3tooling

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions