Skip to content

fix(spec,cli): os migrate meta lists and writes load-path conversions inside composed package bodies - #22393

Merged
objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/issue-22256-composed-load-conversions
Oct 9, 2026
Merged

objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/issue-22256-composed-load-conversions

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #22256
Clause-②: no (a seam off the public entry: an internal Symbol.for-keyed parameter, undeclared on DefineStackOptions and not exported; the seat's answer A in its review on #22256)

The composed half of #22256 (the one-package half landed in PR #22351). Inside a composeStacks([…], { manifest: 'preserve' }) project, os migrate meta now lists a conversion the load already applies inside a package body (for example datasources[].driver: 'mongo'), --write writes it into the file that authored that input, and the next load converts nothing. This holds whether the input's defineStack call accepted its argument or was refused and produced again in strict: false mode.

The seam (H2), and why it is off the public entry

composeStacks assembles each packages[i].manifest body from the stack its input's producer RETURNED, and defineStack runs the load-time ADR-0087 D2 conversion pass in both modes. No existing switch skips that pass for a defineStack call (no option, env or global; applyConversions has none either), so a packages/spec change is required.

Candidates measured:

carrier api-surface rows export-origins package exports DefineStackOptions type verdict
C1 an internal parameter: a spec-owned Symbol.for key read off defineStack's options, honoured only with strict: false, that skips the D2 pass +0 +0 +0 unchanged chosen
C2 a symbol-keyed composeStacks option assembling bodies from per-input authored sources +0 +0 +0 unchanged rejected: bodies would be authored while the flattened half stays converted and parsed, so the option-B strip comparison fails on parse defaults and the artifact shape changes even on canonical projects
C3 a declared DefineStackOptions member (a convert: false twin of NormalizeStackInputOptions.convert) +0 (rows are name (kind) only) +0 +0 widened public option, Clause-②: yes
C4 a new export (preservePackageEntries / assemblePackageBody, or a composeAuthoredStacks) +1 +1 entry n/a public export, Clause-②: yes
C5 CLI-only: fill the marked empty object composeStacks([]) returns with authored content n/a n/a n/a n/a rejected: it would attach a producer's mark to content no producer judged (ruling B)
C6 CLI-only: rebuild the bodies in packages/cli n/a n/a n/a n/a refused by the domain:cli seat's cut

C1 is the conversion-free twin of the strict: false re-produce the shim already performed for refused inputs (PR #22326), so the semantic delta is exactly "the D2 pass is skipped". A strict call ignores the key, so it can never let an old spelling reach the strict parse unconverted; the output is marked as every non-strict output is. The key is documented beside defineStack (AUTHORED_INPUT_OPTION, packages/spec/src/stack.zod.ts) and is neither declared on DefineStackOptions nor exported. check:api-surface and check:export-origins stay green with no regenerated artifact.

The consumer (H3)

packages/cli/src/utils/config.ts, the authored-source shim's composeStacks wrap: every input whose defineStack call the shim saw is produced again from what the author wrote, through the real defineStack in strict: false mode with the key. A refused input is its recorded hand-through (as before); an accepted one is the argument PR #22351 keeps under AUTHORED_ARGUMENT_KEY, followed to the innermost literal. Inputs the shim never saw a defineStack call build (a nested composition, a plain object) reach the real composeStacks untouched, so STACK_PROVENANCE_MISSING for an unwrapped input is unchanged. Composition then runs its own rule over the authored inputs. No composition logic is copied into packages/cli.

meta.ts and the --write planner are unchanged in code (comment only): the planner already traces packages[i].manifest.KEY to input i's literal (PR #22326). Measured: the H4 site packages[0].manifest.datasources[0].driver is written into src/service.stack.ts, the input's own module, and nothing else moves.

Measurements

H1 on origin/main 16096e8d7 (BASE), built CLI, composed fixture (composeStacks([ServiceStack, AppStack], { manifest: 'preserve' }), the service body carrying driver: 'mongo'):

case applied write.files source os validate reload
A: accepted input [] [] keeps 'mongo' prints converted at load … datasource-driver-mongo-to-mongodb
B: refused input ('10:00Z'), re-produced strict: false only time-default-utc-suffix-dropped src/service.stack.ts 1 site keeps 'mongo' prints converted at load
C: canonical [] [] unchanged —

After the fix (same fixtures, --from 16):

case applied write.files verification re-run converted at load lines on reload
A datasource-driver-mongo-to-mongodb @ packages[0].manifest.datasources[0].driver src/service.stack.ts 1 site ok applied [] 0
B mongo then time, both under packages[0].manifest… src/service.stack.ts 2 sites ok applied [] 0
C [] [] — [] 0

Corpus control, BASE vs fix (both trees built, the four example apps, app-multi-package being the composed one; --from 16 and --from 17; --json dry and --write on same-depth copies; duration removed): 16 of 16 documents byte-identical by md5, the written-file sets identical, and the written bytes identical (app-showcase src/automation/flows/index.ts 2aec23aad9d3, app-todo src/flows/task.flow.ts 4ffb6d9aa6ce, in both trees).

The --out snapshot of a composed project moves, as ruling A (REWORK 6068295985) already accepted for one-package stacks: on app-multi-package at --from 17, 53 differences, every one a key the schema's parse fills in that exists only on BASE (externalId, hidden, multiple, readonly, searchable, sortable, unique, required, scope, defaultDatasource, datasource, isSystem, priority, deleteBehavior, active, isDefault, expanded). No value changed and nothing was added. The changeset states it.

Pins

  • packages/spec/src/stack-authored-input.test.ts (3): the key keeps the authored spelling, the output is marked, and no conversion is recorded, while the control without it converts; a strict call ignores it; composeStacks over such inputs assembles the body from the authored spelling, and over plain non-strict inputs from the converted one.
  • packages/cli/test/migrate-meta-composed-load-conversions.test.ts (5, unit tier, in-process): the H4 accepted case listed, written into the input's file and nothing else, a clean re-run, and a strict reload with an empty stackConversions; the authored-source load's body carries 'mongo' while every other load's carries 'mongodb'; an input defined twice is listed once; the refused input re-produced by the shim lists and writes both conversions; the canonical control writes nothing and its --json summary equals the one the built composition gives.
  • PR fix(cli): os migrate meta runs on a composeStacks project and migrates its package bodies #22326's 8 composed pins and PR fix(cli): os migrate meta lists and writes a conversion the load already applies, on a stack the schema accepts #22351's 5 one-package pins stay green beside them.

Ablations

Both committed first, mutated through scripts/ablation-replace.mjs (anchor hit once, mutation proven on disk, restore proven: blob equal to HEAD and git diff HEAD empty, whole-tree git status --porcelain empty).

  1. The seam (dist leg, because the CLI suite resolves @objectstack/spec through its exports): deleted convert: strict || !asksForAuthoredInput(options), from buildDefinedStack. Presence reading on the pristine build: the marker in 4 built files. After the spec rebuild, ablation-dist-preflight --absent: absent from all 232 built files, tree reading "mutate leg". Predicted spec 2 red / 1 green and the new CLI file 4 red / 1 green (control green), the 13 neighbouring pins green; got exactly that (Tests 2 failed | 1 passed (3); Tests 4 failed | 14 passed (18)). Restore leg: rebuild, marker present in 4 built files, tree clean, 3 passed (3) and 18 passed (18).
  2. The accepted arm of the shim (no dist leg: the CLI suite imports src): made __composable hand an accepted input through as built. Predicted 3 red (the three accepted-input pins) / 15 green; got Tests 3 failed | 15 passed (18).

Deliberate correction of two pending changesets

This change makes the "Known limit" bullet of two pending, unreleased changesets false, and all three ship in the same @objectstack/cli release: .changeset/22289-migrate-meta-composed-project.md (PR #22326) and .changeset/22256-migrate-meta-load-path-conversions.md (PR #22351). Each loses exactly that one bullet; nothing else in either file moves. check-empty-changeset's foreign-changeset rule is therefore red by design, in its DELIBERATE CORRECTION class (precedent PR #21683 and PR #22351); it asks for the correction to be confirmed on the PR, not restored.

Tests and gates

All at a0d0a6253 (this branch merged with origin/main 117d34de3), after rebuilding the CLI closure:

  • pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2 (the whole unit tier): Test Files 271 passed (271), Tests 3973 passed (3973). The integration tier is declared to CI: the diff touches no integration-tier file and no spawn entry.
  • pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2 (the package's test script): Test Files 627 passed (627), Tests 18745 passed | 1 todo (18746).
  • pnpm --filter @objectstack/spec --filter @objectstack/cli run typecheck: exit 0. Both check:test-typecheck ledgers hold unchanged (spec 52 files / 246 errors / 135 signatures; cli 3 / 28 / 6), so both new test files compile clean.
  • eslint, narrowed, as a measurement rather than a skip. Population: the 5 changed .ts files, all inside eslint's linted set (no "file ignored" notice in the JSON output). Count from --format json: 5 files, 0 errors, 0 warnings. Invariance: eslint.config.mjs enables no type-aware linting (no parserOptions.project, no projectService), so this diff cannot move the verdict of any untouched file. The full pnpm lint is CI's.
  • pnpm --filter @objectstack/spec check:generated: all 15 generated artifacts up to date, tree clean.

node scripts/pm/dispatch-gates.mjs --commands (no paths) at a0d0a6253 derives 90 commands from the change set (8 paths against merge base 117d34de3). All 90 ran with their exit codes recorded, and --ran answers 90 derived famil(ies) accounted for — 90 run, 0 NOT-MEASURED.

  • 89 exit 0, among them check:api-surface ("public API surface + factory signatures unchanged"), check:export-origins ("5363 exports across 19 entry points resolve exactly as recorded"), check:dual-source-exports, check:exported-any, check:docs, check:nul-bytes, check:cross-package-test-inputs, check-changeset-no-major and check-adr-0087-registration.
  • One exit 1, by design: check-empty-changeset --base origin/main, the foreign-changeset rule on the two corrected changesets above.
  • check:dual-build-cjs-loads first exited 3 (PREREQUISITE NOT MET: 8 packages without dist/). After those 8 were built it exited 0, and that is the code recorded.

Acceptance notes

  • The 22289 changeset's "What changed" bullet and the stack-provenance.ts module header described the conversion as applied in both modes. The text-only round 9abe8861b (seat review 6072731732) made both exact. The bullet now says a refused input "is produced again by defineStack(input, { strict: false }) with the load-time conversions skipped ([finding] cli(migrate meta): --write never writes a conversion the authored-source load already applied (e.g. driver: 'mongo') — the chain sees a pre-converted stack and reports applied: [] #22256) before it is handed to composeStacks", and the header names the one exception.
  • A single-input composeStacks([x]) under os migrate meta now hands the chain x produced again as authored (normalised, bound actions merged) instead of the raw argument. Only the --out snapshot of such a project can differ, and only when it has bound standalone actions. This edge's --out now differs from a one-package stack's snapshot shape, which keeps bound actions unmerged (contract review 6073322979, finding 6). Carrier: the domain:cli seat, at the next change to --out.
  • C5 above rests on an observation, not a filed defect: a built stack is an ordinary mutable object, so the mark never covered later mutation; composeStacks([]) is one instance of that.
  • Docs: no hand-written page under content/docs describes the composed limit (searched), and no skills/** text states it.

Generated by Claude Code

claude added 4 commits October 9, 2026 00:40
… skipped

defineStack gains one internal parameter, a Symbol.for key read off its
options and honoured only with strict: false, that skips the load-time
ADR-0087 D2 conversion pass. Neither declared on DefineStackOptions nor
exported. The CLI's authored-source composeStacks wrap now produces every
wrapped input again from what the author wrote (refused and recorded, or
accepted and kept) in that mode, so composition's own rule assembles each
package body from the authored source and the chain lists and --write
writes a conversion the load still applies inside a package body.

Claude-Session: https://claude.ai/code/session_01DhTqaEHqPVSVnAkjG3jywn
Co-authored-by: Claude <noreply@anthropic.com>
… migrate meta half

Claude-Session: https://claude.ai/code/session_01DhTqaEHqPVSVnAkjG3jywn
Co-authored-by: Claude <noreply@anthropic.com>
…own-limit bullets it falsifies

Claude-Session: https://claude.ai/code/session_01DhTqaEHqPVSVnAkjG3jywn
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tests tooling labels Oct 9, 2026
@github-actions

github-actions Bot commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/cli, @objectstack/spec, touching 8 documentable anchor(s). ⚠️ 1 changed file(s) yielded no anchor (packages/spec/src/stack-provenance.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

16 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json e030d436bf0a3244502e021fe53035fc6b7b28da.

⛔ 8 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/spec/src/stack-provenance.ts) — pages documenting those are invisible to this run
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 145 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json e030d436bf0a3244502e021fe53035fc6b7b28da → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 08ebfd94f93c96c37ae63cbdc985ee10fe572931 — the merge of head 9abe8861b07522100017ce37e255a466f25f1f8d into base e030d436bf0a3244502e021fe53035fc6b7b28da, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 08ebfd94f93c96c37ae63cbdc985ee10fe572931 && git checkout 08ebfd94f93c96c37ae63cbdc985ee10fe572931
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin e030d436bf0a3244502e021fe53035fc6b7b28da 9abe8861b07522100017ce37e255a466f25f1f8d && git checkout -B drift-repro e030d436bf0a3244502e021fe53035fc6b7b28da && git merge --no-ff 9abe8861b07522100017ce37e255a466f25f1f8d

node scripts/docs-audit/affected-docs.mjs --json e030d436bf0a3244502e021fe53035fc6b7b28da

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs e030d436bf0a3244502e021fe53035fc6b7b28da → pass the list as
args.docs, on the commit named under Which tree this was computed on.

…t still said always

The stack-provenance header now names the one non-strict call that skips the
D2 pass, and the pending composed-project changeset says a refused input is
produced again with the load-time conversions skipped.

Claude-Session: https://claude.ai/code/session_01DhTqaEHqPVSVnAkjG3jywn
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Check Changeset is red by design on this PR: the DELIBERATE CORRECTION of two pending release notes · domain:spec seat 2 (#18549) · session session_01DhTqaEHqPVSVnAkjG3jywn · 2026-10-09T02:43Z.


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 9abe8861b07522100017ce37e255a466f25f1f8d
Local-runs: none

Record written 2026-10-09T02:56Z by the isolated contract reviewer for PR #22393 (card #22256). Inputs, and nothing else: the card body with its 13 comments (rulings and earlier records included), the PR body, its 9-file list, the net diff against main from merge base 117d34de3 (9 files, +617 / -37, read through a private fetched ref), and the 35 check-runs on this head. No worktree, build, test, gate or ablation was run on the tree. The only code executed was the fleet's own Clause-② line reader over the fetched PR body text (a reading of the body, not of the tree) and the posting tool.

Check-runs on this head, read at 2026-10-09T02:49Z: 35, all completed — 31 success, 3 skipped (Build Docs, Console Pin Gate, Packed-tarball smoke (opt-in): path and opt-in skips, none in the required set), 1 failure (Check Changeset, judged under ②). All seven required contexts are green: Lint & Repo Gates, TypeScript Type Check, Test Core, Dogfood Regression Gate, Build Core, Temporal Conformance (live PG + MySQL), Governed Surface Queue Guard. The aggregate Test Core was still in_progress on my first read and had concluded success (completed 2026-10-09T02:45Z) on the re-read above; nothing was read as green while pending. The file list touches no governed surface.

① Derived judgments

Each accept-set or public-surface change the diff implies, with my verdict on it.

  1. @objectstack/spec · defineStack(config, options) honours one undeclared symbol key — Symbol.for('objectstack.stack.authoredInput') (AUTHORED_INPUT_OPTION, stack.zod.ts), read only when strict: false; its whole effect is convert: strict || !asksForAuthoredInput(options) on the normalizeStackInput call, so the ADR-0087 D2 pass is skipped while map-to-array normalisation, the bound-action merge and the provenance mark still happen. By the repo's own definition of the published accept-set (reachable from the entry type graph and addressable through exports): DefineStackOptions is unchanged, nothing new is exported from src/index.ts (the only spellings in the tree are the constant in stack.zod.ts, prose in stack-provenance.ts, and the CLI's own SPEC_AUTHORED_INPUT_OPTION), and check:api-surface / check:export-origins are green inside TypeScript Type Check with no regenerated artefact. A TypeScript caller reaches it only through a cast. Right: off the public entry; Clause-②: no stands. Four things checked in the diff rather than taken from the report: (a) the strict arm is unreachable — strict || short-circuits, so a strict call converts regardless of the key (pinned in stack-authored-input.test.ts); (b) it adds no capability a public caller lacks — normalizeStackInput(…, { convert: false }) is exported from index.ts and NormalizeStackInputOptions.convert documents exactly this consumer; (c) a non-strict output is already judged by nothing, so an unconverted non-strict stack sits in the trust class non-strict output already had; (d) the Symbol.for crossing is the provenance mark's own precedent in stack-provenance.ts, and the CLI's AUTHORED_ARGUMENT_KEY from the one-package half uses it too.
  2. The provenance mark's meaning gains one exception: a marked stack may carry an unconverted spelling when, and only when, it was produced through item 1. Declared at both sites (the stack-provenance.ts module header, the AUTHORED_INPUT_OPTION docblock), and the doors that report conversions already re-run their own pass over what they load. Right — a declared producer mode, not a consumer accommodation; Prime Directive Add comprehensive test suite for Zod schema validation #12 is not touched.
  3. composeStacks accept-set: unchanged. An input produced through item 1 carries the mark, so it passes the STACK_PROVENANCE_MISSING gate exactly as any non-strict output does; a plain object or a spread still fails it, because the shim's __composable hands every input it did not see a defineStack call build through untouched. Body assembly stays composition's rule in packages/spec; the CLI prelude only re-produces inputs and calls the real composeStacks. Right, and the domain:cli seat's cut (no second copy of composition's rule) holds in the diff.
  4. @objectstack/cli · os migrate meta on a composeStacks([…], { manifest: 'preserve' }) project now lists a load-path conversion under its body path (packages[i].manifest…) and --write writes it into the file that authored input i. The key is passed by the authored-source load (loadConfig({ authoredSource: true })) alone, and the strict load every other command uses is pinned to keep converting ('mongodb' in its body). A canonical composed project is pinned to write nothing and to give the summary the built composition gives. Right — this is the card's stage-2 accept-set as triage routed it (card comment 6070653436, with the dev's H4 case as the pin); it widens what the command lists, not what it accepts.
  5. The --out snapshot of a composed project drops the keys the schema's parse fills in from each package body (the dev's reading on app-multi-package: 53 keys present only on BASE, none added, no value changed). Right — the shape ruling A (REWORK 6068295985) already accepted for one-package stacks, extended to the bodies; the new changeset states it.
  6. Single-input composeStacks([x]) (PR acceptance note 2): because composeStacks returns a single input untouched and the re-produced x no longer carries the CLI's AUTHORED_ARGUMENT_KEY, the chain now receives x normalised with its bound actions merged instead of the raw literal. applied and --write are unaffected (meta.ts normalises before the chain either way); only --out can differ, and only with bound standalone actions. Right as declared, with one note for the domain:cli carrier: on that edge the single-input snapshot now differs from the one-package snapshot (actions merged vs not), which ruling A's one-snapshot-shape intent did not foresee. It is an acceptance note, not in the changeset. No accept-set or key moves; not blocking.
  7. SPEC_AUTHORED_INPUT_OPTION duplicates the registry name in packages/cli. Drift is not silent — the producer then converts as before and the composed pins go red on the very conversion — and the docblock says so. Right.
  8. meta.ts: comment only, verified in the diff. Right.

Not implied by the diff: no REST route, no schema key, no retired key, no ADR-governed decision reversed (docs/adr/** untouched; the ADR-0087 id is left in the code at the seam).

② Semver level

  • The new changeset .changeset/22256-composed-load-path-conversions.md: @objectstack/cli: patch, @objectstack/spec: patch, Clause-②: no, no arm. Under the repo's WHICH LEVEL rule a fix( that changes no public surface stays patch; ① item 1 is why the spec change is no public surface (no exported symbol, no declared key, no accepted value on a published payload). Both packages publish the touched source, so skip-changeset would have been wrong and is not applied (PR labels: documentation, size/l, tests, tooling). Right.
  • The Clause-②: line. The PR body's line reads declared · no · arm: none through scripts/pm/clause2-line.mjs, the fleet's one reader, and matches the changeset's own line. The level axis therefore stands down (not-declared). Right.
  • Mechanical confirmation from the head's own Check Changeset job (steps read through the REST jobs endpoint; the job log itself was not retrievable from this session): step 11 "Require a changeset" success; step 13 "Require an ADR-0087 disposition on a declared-breaking changeset" success; step 15 "Guard against accidental major bumps (launch window)" — the no-major guard and the level axis — success. The single failing step is 12, the foreign-changeset rule, and the run's two ::error annotations name exactly .changeset/22256-migrate-meta-load-path-conversions.md and .changeset/22289-migrate-meta-composed-project.md with the two-class text. So the red is the deliberate correction and nothing else.
  • The two corrections, read at the merge base and in the diff: 22256-migrate-meta-load-path-conversions.md loses exactly its "Known limit, unchanged" bullet (one line); 22289-migrate-meta-composed-project.md loses exactly its "Known limit" bullet and has one sentence made exact ("is produced again by defineStack(input, { strict: false }) with the load-time conversions skipped ([finding] cli(migrate meta): --write never writes a conversion the authored-source load already applied (e.g. driver: 'mongo') — the chain sees a pre-converted stack and reports applied: [] #22256) before it is handed to composeStacks" — true of the diff's __composable). Both bullets state the limit this diff removes; restoring either would publish a false note in the same @objectstack/cli release as the new one. Right, and the gate's own remedy for this class is "do NOT restore it -- say so on the PR and get it confirmed".
  • Check Changeset red by design — judged against the repo's rule (pm-dispatch SKILL.md, queue-eligibility: a by-design red may enter the queue only when all three hold — the source's self-description says it is red by design on a pushed branch, the check does not run on merge_group, and a PR comment records the gate and the cause). (i) scripts/check-empty-changeset.mjs names the DELIBERATE CORRECTION class and says "this gate stays red either way, and staying red is what puts the decision in front of a person instead of routing around it" — holds. (ii) pr-automation.yml triggers on pull_request only (opened, synchronize, reopened, labeled, unlabeled, edited), carries no merge_group key, and Check Changeset is not among the seven required contexts in main's ruleset — holds. (iii) PR comment 6073186983 names the gate, both files, the exact bullets lost, why restoring would be false, the confirming review (card comment 6072731732) and the queue reach — holds. All three hold; the declaration is right. Two residuals, named rather than hidden: the confirmation is written by the same session that dispatched the dev (the repo's precedent on this very card, PR fix(cli): os migrate meta lists and writes a conversion the load already applies, on a stack the schema accepts #22351's landing note 6069820663, is the same class confirmed by a seat); and the corrected notes belong to the domain:cli lane — the comment records a declaration to that seat on [PM seat] domain:cli — ⏳ vacant #6024, which is outside this review's inputs and is taken as stated, not verified; that seat or the maintainer may still overrule on the PR, as the comment itself says.

③ Boundary flags

Every dev flag and open_questions entry on the card for this PR, answered or escalated.

  • Round 1 open_questions[0] (symbol-keyed internal parameter vs a declared DefineStackOptions member; no/patch vs yes/minor): the seat answered A (card comment 6072731732). Answered: A is right — ① item 1; the claim's own criterion (a seam that stays off the public entry keeps Clause-②: no) is met by the repo's exports-and-type-graph definition, not by prose. Not escalated.
  • Round 1 deviation 1 (file surface: two foreign changesets edited): answered — the seat amended the surface in the same review, and the edits are exactly the falsified bullets (②).
  • Round 1 deviation 2 (origin/main merged as merge commit a0d0a6253, no rebase, no force-push): answered — the branch's five commits include it; the net diff against the merge base is the 9 files reviewed.
  • Round 1 deviation 3 (the seam landed in stack.zod.ts, no other package): answered — matches the claim's expected surface.
  • Round 1 deviation 4 (attribution): answered — no model identifier in any added line of the diff (scanned), the PR body carries the session-URL footer, and this record carries none.
  • Round 1 deviation 5 (a queue-timeout run not counted; a temporary BASE worktree removed): no bearing on the diff. Answered.
  • Round 1 out_of_scope_findings (the 22289 "What changed" sentence; the stack-provenance.ts "in both modes" header): both were taken up in the seat's text-only patch round and are in the diff at this head. Closed in-diff.
  • Round 2 (card comment 6073164819, head 9abe8861b): open_questions: [], out_of_scope_findings: []; the stated deviations are a superset of gate families run, no conflict. Nothing to answer.
  • PR body acceptance notes: (1) the 22289 "What changed" sentence "was left as written" — stale at this head: round 2 edited that sentence and the body was not updated; the seat writes body edits, so this is the seat's one-line amendment, not a diff defect. (2) single-input composeStacks([x]) — ① item 6. (3) C5 rests on an observation about the mark (a built stack is mutable) — outside this diff, nothing to file from here. (4) docs: the per-sentence docs check belongs to the seat's ACCEPT under the contract-review reference; the docs-drift advisory on the PR is precision-first and names 16 pages by anchor, which the seat's ACCEPT should receipt as the one-package half did.
  • The shim's accepted arm re-produces an accepted input with { strict: false, key } and not the original call's options: artifactObjects is read only on defineStack's strict path, which strict: false skips, and never stored on the stack, while composition resolves against every package's objects — so nothing is lost on the authored-source load. Answered, no finding.
  • Pending checks: none at the re-read. Escalations: none.

Implemented-by: claude/issue-22256-composed-load-conversions
Reviewed-by: session_01DhTqaEHqPVSVnAkjG3jywn

VERDICT: PASS


Generated by Claude Code

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

Labels

documentation Improvements or additions to documentation size/l tests tooling

Projects

None yet

2 participants