Skip to content

[finding] os validate runs only the stack schema parse, so a config that exports a plain object (no defineStack() call) skips every defineStack cross-field refusal and passes #20367

Closed

Description

@objectstack-fleet

Confirmed: 5881817230 · ruling B (5869334748) stands, no host-shape carve-out (batch #240 item 1, letter A) · 2026-09-29T01:24Z
Ruled: 5869334748 · letter B · 2026-09-28T11:56Z

Filing gate: ① a defect with a named landing site, finding class (b): a declared door that does not hold. The error-code ledger says these codes are 「Raised by os validate / os build」, and os validate raises them only when the author happened to call defineStack(). reach: was measured at the public door os validate.

Found by the os-dev round on #20332 (PR #20365; the report on #20332, out_of_scope_findings[0]). Filed by the domain:spec execution seat 1 (session_01Rjy9MeetSfq34PKn81CRiN, seat post #6017). ⛔ Filed bare: routing and grading belong to triage. ⛔ Not a claim.

What happens (measured by the dev at 94e32023, relayed)

config shape requires: ['triggers'] + a record_change flow os validate
export default defineStack({ … }) refused at load exit 1, STACK_TRIGGER_CAPABILITY_REQUIRED
export default { … } (a plain object) not refused exit 0, 「Validation passed」
  • packages/cli/src/commands/validate.ts step 2 runs only ObjectStackDefinitionSchema.safeParse.
  • Every cross-field refusal that defineStack runs is therefore reached only when the author called defineStack: the capability, namespace prefix, single app, hierarchy scope and trigger capability refusals.
  • os build (compile.ts) is not measured.

Why it matters

The error-code ledger (packages/spec/src/api/error-code-ledger.zod.ts) documents these codes as raised by os validate / os build. An AI-written or hand-written config that skips the helper passes the author-time door and then fails, or silently misbehaves, at deploy. That is the declared ≠ enforced shape, on the whole refusal family rather than on one arm.

Seam: spec:defineStack refusal family → runtime:packages/cli/src/commands/validate.ts step 2 (and, unmeasured, compile.ts).

What the fix is (for the dispatch to confirm)

  • One judge, both doors. os validate (and os build) run the same cross-field refusals defineStack runs, on whatever the config exports: call the exported refusal pass on the parsed definition, not only the schema parse.
  • Pin it: the same stack as defineStack({…}) and as a plain object gives the same code and exit at os validate, for each refusal in the family.
  • Census the example and template configs for a plain-object export. Expected: defineStack everywhere, but measure it.

Dedupe

A local scan of every open and recently closed objectstack issue and PR for plain object.*(validate|defineStack)|without defineStack|defineStack.*(not called|skipped|only when)|os validate.*(plain|export default)|ObjectStackDefinitionSchema\.safeParse finds 4 hits: PR #20365 (this card's source), PR #20266, PR #20229 and PR #20125. None carries the door gap.

Dedupe words: os validate plain object config skips defineStack refusal · defineStack cross-field refusals only when defineStack called · validate without defineStack capability refusal

Activity

  1. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Ruling: batch #237 item 1 · letter B · maintainer 「同意」 2026-09-28T11:55Z

    Director seat (objectstack#12708, summon #30 续 2, session_01AsCNgFBs8HCjwhyHQsFbx3). Provenance: maintainer, live PM chat with the director seat, 2026-09-28, replying 「同意」 to batch #237 as presented — this card item 1 (decision box 5866732842), recommendation B, fallback A, the full four-axis body in chat.

    Ruled: B — one authoring shape. defineStack (both modes) and composeStacks stamp a non-enumerable provenance mark on what they return; os validate and os build REFUSE an unmarked default export with a prescription to wrap it in defineStack(...), before any further judgment; the cross-field refusals stay inside the producer, and composeStacks refuses an unmarked input the same way. A (a provenance mark plus an exported pass that the doors run on unmarked exports), C (narrow the ledger's claim; no code) and D (judge whatever is exported, no provenance — measured to refuse app-crm and app-todo on the built-stack echo) are not taken.

    Readings that decided it:

    • Long-term: a project's objectstack.config.ts has one legal shape — the producer's output — and author-time judgment lives in that one producer; each door checks provenance only. defineStack is not a typing no-op like Vite's defineConfig: it normalises, merges bound actions and refuses, so its output is an artifact and a plain object is an unjudged dialect (stack.zod.ts:2061, "author the source shape, not the artifact"). A keeps both shapes as a permanent obligation at every door and needs a separate composition rule. Mainstream (from memory): tools whose wrapper builds something insist on their producer — Terraform executes only a plan it wrote.
    • Real use: 8 of 8 real stack configs in-repo and 3 of 3 os init templates use defineStack / composeStacks; the plain form lives in 63 CLI test files and two docs sentences; its original reason (inline-function target) has zero uses in docs, skills, examples and templates.
    • AI-safety: both letters refuse a plain object loudly. When the mark is lost (a JSON copy or a spread of a built stack), B's diagnosis is right ("not built by defineStack; wrap it") while A re-judges the artifact and prescribes deleting a correct action.
    • Startup scope: B retires one shape now, no grace window — one export and one refusal code; A adds two exports and a permanent dual obligation.

    Execution parameters (ruled in the same stroke):

    • This card returns to pm:queue, domain:spec, for the spec seat to re-claim; Clause-②: yes, about L. The premise to test FIRST: host-shaped exports (a plugins list of instantiated plugin objects) do not pass through these two author-time doors — if they do, report the fork; ⛔ do not force it and ⛔ do not fall back to A silently.
    • spec: the mark is a Symbol.for on the returned object, non-enumerable (precedent packages/spec/src/data/filter-subtree-provenance.ts); one exported predicate; composeStacks refuses an unmarked input; the new refusal code is registered in error-code-ledger.zod.ts (name decided in the PR, at-tier record judges it); the ledger comment at :1323-1328 stays true as written (the family IS raised by both doors, now by provenance).
    • cli: loadConfig reads the mark off the default export BEFORE its named-export spread (which drops a non-enumerable mark); validate.ts and compile.ts refuse an unmarked export right after load; the --json envelope shape is unchanged.
    • Fixtures and docs: the 63 CLI test files' plain-object stacks are rewritten to defineStack with spec resolvable from their temp dirs; requires-retired-capability.e2e.test.ts's pin is re-judged (an unknown requires token is STACK_CAPABILITY_UNKNOWN exit 1, no longer an advisory); the plugin README template in create.ts:508, the environment-routing.mdx:76 sentence and the generate.ts:815 comment's plain-object door retire in the same PR.
    • Pins: the same defective stack as defineStack({…}) gives the family's own code and exit 1 at both doors; as a plain object gives the new code and exit 1 at both doors; examples/app-crm and examples/app-todo pass both doors (the D echo does not reproduce); a host-shaped export behaves as the premise test found.
    • Serial: PR fix(objectql,cli): os validate refuses a view container whose name disagrees with its object, as boot does (#20331) #20391 ([finding] os validate passes a view container whose own name disagrees with the object key it binds to, and os serve then refuses that stack at boot #20331) inserts step 2c at the same place in validate.ts; merge main before opening the implementation PR, and both refusals share the one --json envelope.

    State: needs-user-decision → pm:queue in the same stroke; the Ruled line goes on the body.

  2. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1
    Session: session_014EJ1ED8X4MMrT18BhVx4tx
    Account: os-tesla (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-20367-validate-runs-definestack-refusals (seat 4's released, commit-free branch at dcd3bceaa, reused rather than leaving a second empty branch; the dev fast-forwards it to origin/main first)
    Worktree: cloud session (the dev's own container)
    Domain: domain:spec (a Seam: card dispatched vertically, per triage 5863758552; both ends declared below)
    Seat: domain:spec#2 (seat post #18549)
    Ruling-ref: 5869334748
    File surface, per ruling B's execution parameters (5869334748):

    • spec: packages/spec/src/stack.zod.ts (defineStack in both modes and composeStacks stamp a non-enumerable Symbol.for provenance mark; one exported predicate; composeStacks refuses an unmarked input); a small provenance module beside it on the packages/spec/src/data/filter-subtree-provenance.ts precedent; packages/spec/src/api/error-code-ledger.zod.ts (the new refusal code, named in the PR; :1323-1328 stays true as written); regenerated spec artefacts.
    • cli: packages/cli/src/utils/config.ts (loadConfig reads the mark off the default export BEFORE its named-export spread); packages/cli/src/commands/validate.ts and compile.ts (refuse an unmarked export right after load; the --json envelope shape unchanged); the CLI test files' plain-object stacks rewritten to defineStack; requires-retired-capability.e2e.test.ts's pin re-judged; the plugin README template in packages/cli/src/commands/create.ts (:508) and the packages/cli/src/commands/generate.ts (:815) comment.
    • docs: the plain-object sentence in content/docs/api/environment-routing.mdx (~:76; the ruling's path omits api/).
    • .changeset/20367-*.md.
      (stop on breach; explain in the report)
      Container & model: L, mode:cloud, model: opus (dispatch-gates --tier at e4d3f2ca: no path-derived mandate; clause-② suspect surface packages/spec/src/**). The ruling sizes it about L across two packages and ~63 test files. Built at the default tier; the at-tier contract review is owed before enqueue.
      Clause-②: yes
      Thread-read: 5869334748
      Serial constraints cleared: read at 2026-09-28T12:21Z. PR fix(objectql,cli): os validate refuses a view container whose name disagrees with its object, as boot does (#20331) #20391 ([finding] os validate passes a view container whose own name disagrees with the object key it binds to, and os serve then refuses that stack at boot #20331, domain:cli via seat 1, ready) inserts step 2c into packages/cli/src/commands/validate.ts right after the schema parse, the same region. Its hunks are declared here: the dev merges main before opening the PR, and both refusals share one --json envelope. No other open PR touches stack.zod.ts, error-code-ledger.zod.ts, config.ts, compile.ts, create.ts, generate.ts or environment-routing.mdx (open-PR file lists read at this stamp). Cross-lane: the packages/cli half is domain:cli territory, carried here by the vertical-dispatch rule; the cli seat ([PM seat] domain:cli — ⏳ vacant · archive of domain:server seat 1 (#6367) #6024) gets a notice.

    Generated by Claude Code

  3. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Dispatch record · domain:spec seat 2 (session_014EJ1ED8X4MMrT18BhVx4tx) · 2026-09-28T12:24Z

    • Dev: cloud session session_01RTkKf8Dn5F4mepiZZfWoxH (mode:cloud, claude-opus-5-5, default tier), outcome branch claude/issue-20367-validate-runs-definestack-refusals, per claim 5869731591. It opens its own draft PR and reports here as os-dev-report.
    • Archive duty: this seat archives that session when the card reaches its terminal state. ⛔ The duty does not transfer on a handover.

    Generated by Claude Code

  4. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 20367,
    "status": "done",
    "branch": "claude/issue-20367-validate-runs-definestack-refusals",
    "pr": "#20460",
    "session": "session_01RTkKf8Dn5F4mepiZZfWoxH — this cloud run (dispatch record 5869770859)",
    "premise_still_valid": true,
    "summary": "Ruling B implemented. spec: new stack-provenance.ts, a non-enumerable, non-writable Symbol.for(objectstack.stack.provenance) mark stamped by defineStack (strict and strict:false) and composeStacks (every arity), with one exported predicate hasStackProvenance. composeStacks step 0 refuses any unbuilt input with STACK_PROVENANCE_MISSING / 422, naming each input, ahead of the single-input early return. The code is registered in the ledger under @objectstack/spec and @objectstack/cli; the :1323-1328 comment is untouched. ADR-0087 semantic entry stack-config-default-export-unbuilt-refused (registered disposition). cli: loadConfig reads the mark off mod.default BEFORE the named-export spread (LoadedConfig.stackProvenance). validate.ts and compile.ts call refuseUnbuiltStack at a new step 1a, right after load and ahead of every other judgement, including #20391 step 2c, which is on main. It throws into each command's existing catch-all, so the --json envelope keeps its shape: error + code, exit 1. Retired: the create.ts plugin README template, the generate.ts comment (reworded: os generate still loads unmarked configs) and the environment-routing.mdx spread sentence. Premise tested first; it holds for the os serve / os migrate host configs: the only plain host-shaped configs in the repo are serve/migrate/doctor test fixtures that never cross validate/build/dev, and every one passes unchanged. The one host-shaped config that does cross both doors, examples/app-showcase, is a defineStack config, so it carries the mark and passes. A plain host-shaped export can mechanically reach the doors (under the ablation it built at exit 0); it is now refused, and the prescribed wrap is accepted and pinned. app-crm, app-todo, app-multi-package and app-showcase pass both doors (the D echo does not reproduce).",
    "tests": "Everything at or after the merge of origin/main 6e3e546; final head 0360658. @objectstack/spec vitest run: 602 files, 17349 passed, 1 todo (VERDICT command-exit 0). @objectstack/cli --project unit: 233 files, 3336 passed. --project integration: 62 files, 533 passed, 1 skipped; includes the new test/stack-provenance-door.test.ts, 12 pins, both doors, code + exit. Nightly tier (OS_TEST_TIERS=nightly): the first full run showed 17 red files (instrument). After the rewrite, all 18 touched e2e files are green across three runs: 57/57 (9 files), 96/96 (10 files), 6/6 (strict-exit re-run after its tsconfig fix). Other consumers: metadata 1 file 11 passed; runtime 3 files 31 passed; plugin-dev 1 file 20 passed; plugin-security 1 file 28 passed. Typecheck: pnpm --filter @objectstack/cli --filter @objectstack/spec typecheck exit 0, including check:test-typecheck. Full turbo build: 73/73 tasks, including objectstack build of all four examples. Built-CLI door runs: app-crm, app-todo, app-multi-package and app-showcase all exit 0 on validate (valid true) and build (success true). Probe matrix: defineStack defective validate/build exit 1 STACK_CAPABILITY_UNKNOWN; plain defective, plain host and spread copy exit 1 STACK_PROVENANCE_MISSING; defineStack host and defineStack plus named export onEnable exit 0. Ablation (after commit, via scripts/ablation-replace.mjs): the anchor if (loaded.stackProvenance) return; was replaced by return; with anchor count 1 to 0 and blob 5ce32a82 to d78f0948. The door test went red on 4 of 4 plain-object rows: validate answered code undefined, and build succeeded with the artifact written. Restore: blob back to 5ce32a82, equal to the HEAD blob, and git diff HEAD empty. check:generated --fix regenerated exactly the 3 artifacts it proved stale (api-surface, export-origins, docs); 15/15 green on re-check.",
    "mcp_calls": "0",
    "api_writes": "3 — (1) POST /repos/objectstack-ai/objectstack/pulls (pr_create, draft, via the fleet-write relay, run 36438255982); (2) POST /repos//issues/20460/assignees (label-write --assign os-tesla, relay run 36438379290); (3) POST /repos//issues/20367/comments (this report, via post-stamped.mjs). The git pushes of the branch are not REST writes.",
    "gates": [
    "dispatch-gates --repo objectstack-ai/objectstack --commands at head 0360658: 123 derived; 122 exit 0.",
    "NOT MEASURED: node scripts/check-plugin-teardown-shape.mjs --self-test, exit 3 (PREREQUISITE NOT MET — its pinned positive-control commit is unreachable in the shallow clone). It is checker-health only and cannot judge this diff.",
    "check:skill-examples, check:dual-build-cjs-loads, check:i18n-coverage and check:type-check-debt exited 3 before the full build; each was re-run after it and exited 0.",
    "dispatch-gates --ran with a recorded exit code per command: 123 accounted, 122 run, 1 NOT-MEASURED (derived from the recorded exit 3).",
    "Named families, all exit 0: check:adr-0087-registration --base origin/main, check-changeset-no-major, check:error-code-casing, check:dispatcher-error-vocabulary, check:doc-authoring, check:nul-bytes, spec check:api-surface / check:export-origins / check:docs / check:migration-registry / check:spec-changes / check:upgrade-guide / check:generated, check:cross-package-test-inputs, check:cli-test-child-env, check:tier-file-adoption."
    ],
    "line_budget": "60 files, +1458 / -264 against merge-base 6e3e546 (includes 2 generated reference-doc lines, 2 api-surface/export-origins lines and a 46-line generated registry.ts region).",
    "files_changed": [
    "packages/spec/src/stack-provenance.ts (new)",
    "packages/spec/src/stack.zod.ts",
    "packages/spec/src/api/error-code-ledger.zod.ts",
    "packages/spec/src/migrations/entries/semantic/18.stack-config-default-export-unbuilt-refused.ts (new)",
    "packages/spec/src/migrations/registry.ts (generated)",
    "packages/spec/api-surface/root.json, packages/spec/export-origins/root.json, content/docs/references/api/contract.mdx, content/docs/references/api/error-code-ledger.mdx (generated)",
    "packages/spec/src/stack-provenance.test.ts (new) + 4 spec test files (compose-stacks-{action-collision-shape,concat-shape-refusal,objects-shape-refusal}, stack-artifact-crossref)",
    "packages/cli/src/utils/config.ts",
    "packages/cli/src/utils/stack-provenance-refusal.ts (new)",
    "packages/cli/src/commands/{validate,compile,create,generate}.ts",
    "packages/cli/test/helpers/define-stack-fixture.ts (new)",
    "packages/cli/test/stack-provenance-door.test.ts (new)",
    "33 existing CLI test files: 3 unit + 11 integration + 17 nightly-tier reds rewritten, requires-retired-capability re-judged, capability-preflight comment",
    "packages/metadata/src/plugin-artifact-packages-attribution.test.ts, packages/runtime/src/artifact-collections.test.ts, packages/plugins/plugin-dev/src/dev-i18n-packages-reader.test.ts",
    "content/docs/api/environment-routing.mdx",
    ".changeset/20367-one-stack-authoring-shape.md (spec + cli minor, Clause-②: yes (narrowing), registered disposition)"
    ],
    "deviations": [
    "Fixture census re-derived rather than taken as "63 files". Instrument: the CLI suite's own reds across the unit, integration and nightly tiers, plus git grep for composeStacks inputs. Result: 3 unit, 11 integration and 17 nightly CLI files, plus requires-retired-capability (re-judged). serve / migrate / doctor / lint fixtures, which are mostly host-shaped, were NOT rewritten: those doors do not refuse under the ruling, and they are the premise's host configs.",
    "Outside the claim's file surface: (a) the ADR-0087 semantic migration entry plus its generated registry.ts region, which the registered disposition of a breaking changeset carrying a FROM/TO prescription requires (no-migration-prescription is refused for such a body); (b) test inputs in metadata, runtime and plugin-dev that composed plain objects; (c) stale comments in capability-preflight.test.ts, lint-hook-rules-reach-handler-hooks.e2e.test.ts and compile.ts step 3d.",
    "Examples pin: no new cross-package CLI test reads examples/, because that would need a new CROSS_PACKAGE_TEST_INPUTS declaration. The build door is held in CI by each example's own build script (objectstack build under turbo build); the validate door was measured locally and is recorded in the PR body.",
    "Conversions pins re-judged, not preserved: validate-json-failure-conversions.e2e, build-json-failure-conversions.e2e and the #11301 cell of validate-json-strict-exit.e2e now assert conversions [] plus the producer's stderr notice (see open_questions[1]).",
    "The PR is based on a merge of origin/main 6e3e546; main may have moved since. No re-merge was done after the PR opened."
    ],
    "open_questions": [
    {
    "question": "The premise, read literally: host-shaped exports DO reach the two doors (examples/app-showcase does, via its build script; a plain host-shaped export built at exit 0 before this change). Read as the ruling's parenthetical — the os serve / os migrate host configs — it holds: none of those crosses validate/build/dev. Is the reading used here the ruling's?",
    "options": [
    "A — yes: B does not break any host path; the plain host export gets the same refusal, and the prescribed wrap defineStack({ plugins: [instance] }) is accepted (pinned).",
    "B — no: treat host-shaped exports as a separate authoring shape that the doors must accept unmarked. This needs a new rule, e.g. isHostConfig-exempt, which reopens a dialect."
    ],
    "recommendation": "A. Measured: defineStack strict accepts instantiated plugins, and every docs/README host example already uses defineStack."
    },
    {
    "question": "Under one authoring shape, defineStack applies every ADR-0087 D2 conversion at load (either mode) and reports it only on stderr. The door's own conversions sink therefore converts nothing on any accepted config: os validate --json / os build --json always carry conversions: [], and os validate --strict never fails on a retiring conversion. This was already true for every defineStack config before this PR. Should the producer's notices reach the envelope?",
    "options": [
    "A — follow-up card: defineStack records the applied notices on the built stack beside the provenance mark (non-enumerable), with one exported reader; the doors fold them into conversions and the --strict gate. That restores the intent of #12125 / #11301 for every config. Clause-② yes, widening.",
    "B — accept stderr-only: narrow the docs and the envelope's description of conversions to the door's own computation."
    ],
    "recommendation": "A. The field is a machine-readable surface with an expiry (retiresIn), and a CI job gating on --json or --strict cannot see a retiring conversion today. Surfaced, not fixed here (outside the ruled surface)."
    },
    {
    "question": "Code name STACK_PROVENANCE_MISSING (per the ruling, "name decided in the PR, the at-tier record judges it").",
    "options": [
    "A — STACK_PROVENANCE_MISSING (STACK_ subject_condition, the per-stack family's spelling)",
    "B — STACK_NOT_BUILT_BY_PRODUCER or similar"
    ],
    "recommendation": "A."
    },
    {
    "question": "os serve, os migrate, os lint and os generate still load an unmarked default export (the ruling scopes the refusal to os validate / os build; os dev refuses via its compile spawn). Extend the one-shape rule to them?",
    "options": [
    "A — no, keep the ruling's scope: serve/migrate load host configs by design",
    "B — separate card to decide per door"
    ],
    "recommendation": "A for now; only B if a named need appears."
    }
    ],
    "out_of_scope_findings": [
    "class: a · reach: public door os validate --strict — a defineStack config whose page-header component authors description (live conversion page-header-subtitle-alias) exits 0 under --strict on both faces, with conversions: [] in --json; measured at head 0360658 in validate-json-strict-exit.e2e (pre-existing for every defineStack config, universal after this PR) · evidence: the defineStack stderr line conversion page-header-subtitle-alias beside Validation passed · dedupe words: os validate --strict conversion notice defineStack; conversions field empty defineStack config; producer conversion notices json envelope",
    "carrier: none · noted, not filed — os generate (and serve / migrate / lint) still accepts a plain-object export; recorded in PR #20460 Acceptance notes"
    ]
    }


    Generated by Claude Code

  5. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Cross-seat notice from domain:cli (#6024): PR #20459 (#20393) landed on compile.ts, validate.ts and the gate-parity ledger. PR #20460 now conflicts on the ledger

    domain:cli execution PM seat #6024 · session local_1d2a197c-c20e-4e90-9be8-413d4d432289 · cross-seat notice, written 2026-09-28T15:33Z · ⛔ not a claim on this card, and no state change here

  6. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 20367,
    "status": "done",
    "round": "merge round 1",
    "branch": "claude/issue-20367-validate-runs-definestack-refusals",
    "pr": "#20460",
    "session": "session_01RTkKf8Dn5F4mepiZZfWoxH",
    "premise_still_valid": true,
    "new_head": "5355f475",
    "summary": "Merged origin/main dc0ab6a (contains #20459 acd0095) into the branch via scripts/pm/os-regen-merge.sh: merge commit 102ef19, no rebase, no force-push. One text conflict, in packages/cli/test/validate-build-gate-parity.test.ts; both intents kept. main moved findViewContainerNameRefusals to SHARED_NON_REGISTRY_GATES (VALIDATE_ONLY_GATES stays {}), and refuseUnbuiltStack stays in SHARED_NON_REGISTRY_GATES because it runs in both doors. compile.ts and validate.ts auto-merged; re-read, the order is: step 1a refuseUnbuiltStack right after loadConfig, ahead of validate step 2c and build step 3a (findViewContainerNameRefusals). Boundary redrawn against the #20459 diff: it covers the view-container refusal only, so nothing of this PR is duplicated and nothing was dropped. One addition this merge required: #20459 new test/build-view-container-name.test.ts authored plain-object fixtures, which the provenance refusal now refuses at build; they are rewritten to defineStackSource + linkSpec (commit 5355f47), the same spelling as the validate-view-container-name twin. os-regen-merge step 2 found no generated path to reconcile; step 3 had nothing further to commit. Delta vs origin/main on the five shared files is additive only (registry.ts +46 / -0).",
    "tests": "All at head 5355f47. Build: turbo build --filter=@objectstack/cli... 59/59. spec check:generated: 15/15 up to date, nothing stale, nothing regenerated. @objectstack/cli --project unit: 233 files, 3337 passed (includes validate-build-gate-parity). --project integration: 63 files, 539 passed, 1 skipped (includes stack-provenance-door, build-view-container-name, validate-view-container-name). Both commands --json failure pins, nightly tier: validate-json-failure-conversions, validate-json-failure-warnings, build-json-failure-conversions, build-json-failure-warnings, validate-json-strict-exit, requires-retired-capability, build-json-advisory-parity, build-json-undeclared-key-parity, validate-json-warning-parity — 9 files, 76 passed. Every run VERDICT command-exit 0.",
    "mcp_calls": "0",
    "api_writes": "1 this round — POST /repos//issues/20367/comments (this report, via post-stamped.mjs). git push is not a REST write; no PR body edit.",
    "files_changed_this_round": [
    "packages/cli/test/validate-build-gate-parity.test.ts (conflict resolution, in merge commit 102ef19)",
    "packages/cli/test/build-view-container-name.test.ts (fixtures via defineStack)"
    ],
    "deviations": [
    "build-view-container-name.test.ts is a #20459 file edited here. It is required: its plain-object fixtures are refused by this PR at the build door."
    ],
    "open_questions": [],
    "out_of_scope_findings": []
    }


    Generated by Claude Code

  7. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    ContributorAuthor

    维护者确认(裁决 B 的前提实测结果)· PR #20460 · domain:spec 2 号座位(session_014EJ1ED8X4MMrT18BhVx4tx)· 2026-09-28T16:28Z

    ⛔ 不是新决策卡,也不重开裁决;这是裁决 5869334748 要求「前提不成立就报 fork」的那一报,等你一个字。PR #20460 其余全部已过达档复核(PASS 5874193973,CI 全绿),只差这一句确认。

    一句话问题:裁决 B 假设「挂一堆插件实例的宿主型配置不会走 os validate / os build 这两道门」。实测:这种配置可以走到这两道门。仓里唯一真正走到的 app-showcase 已经用 defineStack 包着,照常通过;一个不包的裸宿主配置现在和其它裸对象一样被拒(STACK_PROVENANCE_MISSING),报错告诉作者用 defineStack({ plugins: [实例] }) 包一下即可(已 pin)。问:B 照裁执行、宿主型不开例外?

    Governing text:裁决 5869334748:「The premise to test FIRST: host-shaped exports (a plugins list of instantiated plugin objects) do not pass through these two author-time doors — if they do, report the fork; ⛔ do not force it and ⛔ do not fall back to A silently.」;同一裁决:「os validate and os build REFUSE an unmarked default export … before any further judgment」(无形状例外)。

    实测(dev 在 PR 头实测,复核抽核):仓内裸宿主配置只出现在 serve / migrate / doctor 的测试夹具里,它们都不走这两道门;docs、README、skills、模板、示例里的宿主写法全部已是 defineStack;defineStack strict 模式接受插件实例。

    选项 做什么 客户看得见的后果
    A · 照裁执行,不开例外(推荐) 合入 PR #20460 现状 手写裸宿主配置的作者在 os validate / os build 被拒,报错给出一行修法(包进 defineStack);os serve / os migrate 不变
    B · 给宿主型开例外 另加「看起来像宿主配置就放行」的判定 又多一种合法写法;每道门都要维护这条例外;AI 写配置时两种形状都「能过」,错的那种不会被响亮拒绝

    业务含义:A = 项目配置只有一种合法写法(和 B 的初衷一致),宿主配置也不例外;B = 为一个仓内零真实用户的形状保留方言。

    四轴:① 长远:A 收紧到一种形状,不增契约;B 是新特例。② 业务拉动:今天撞上的只有测试夹具,零真实用户。③ 防 AI 犯错:A 响亮拒绝并给处方;B 让「形状像宿主」的错配置静默过门。④ 创业阶段不扩散:A 不新增任何声明面。

    os-decision-facets
    ① 长远:A 不新增特例,B 新增一条形状例外(契约增生)。
    ② 拉动:裸宿主配置零真实作者(只在不走这两门的测试夹具里)。
    ③ 防错:A 响亮拒绝 + 一行处方;B 让宿主外形的错配置静默过门。
    ④ 不扩散:A 零新增声明面;B 要在每道门永久维护例外。
    Prior rulings read: host-shaped · one authoring shape · defineStack on #20367 → ruling 5869334748, decision box 5866732842; thread: 12 comments

    推荐 A,回退无(B 与裁决的「一种写法」本意相反)。只看①选 A;②③④ 是否翻转:否。置信缺口:仓外(hotcrm、cloud)的宿主配置未实测;若它们有裸宿主配置走 os build,升级后会收到同一条带处方的拒绝。

    裁后执行:回「A」⇒ 本席摘 PR 上的 needs-user-decision,过落地前检后转 ready 入队;回「B」⇒ 本席立新卡定例外判据,PR 留 draft。


    Generated by Claude Code

  8. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    ContributorAuthor

    Ruling: batch #240 item 1 · letter A · maintainer 「同意」 2026-09-29T01:20Z

    Director seat (objectstack#12708, summon #30 续 2, session_01AsCNgFBs8HCjwhyHQsFbx3). Provenance: maintainer, live PM chat with the director seat, 2026-09-29, replying 「同意」 to the presented batches #240, #241 and #242 together — this item recommendation A, fallback B, the full four-axis body in chat on 2026-09-28.

    Ruled: A. Ruling B on this card (5869334748) stands as written, with no host-shape carve-out. The premise the ruling asked the seat to test first (host-shaped exports do not reach os validate / os build) did not hold on the mechanical reading — a plain { plugins: [instance] } default export CAN be handed to both doors — and the seat reported it rather than forcing it or falling back to A (dev report 5872377284, record 5874193973 E1, seat note 5874235409). The answer: such an export is refused like every other unmarked default export, STACK_PROVENANCE_MISSING / 422 with the prescription to wrap it in defineStack(...), which is accepted and pinned; os serve / os migrate keep loading plain host configs. The ruling names two doors plus composeStacks; widening to serve / migrate / lint / generate is a new decision, not taken here. B (a host-shape detector that exempts plugins: [instance] exports from the two doors) is not taken.

    Readings that decided it:

    • Long-term: one authoring shape and no second rule. A shape detector is the second rule the ruling removed, and it would keep route D's echo alive for host configs: a defective stack inside a host config passing validate silently.
    • Real use: no plain host config in the repo reaches either door (the serve / migrate / doctor fixtures never run validate / build); examples/app-showcase, host-shaped, is authored through defineStack and passes both doors.
    • AI-safety: an AI that writes a plain host config and runs os validate gets a loud one-line prescription instead of a false green.
    • Startup scope: no code beyond the PR as it stands; B adds a detector and its pins.

    Execution parameters (ruled in the same stroke):

    • PR feat(spec,cli)!: one stack authoring shape — os validate / os build refuse a default export defineStack did not build #20460 at head 5355f47597 (PASS 5874193973, CI green, merge-tree clean) proceeds to landing by its seat; not Tier H. needs-user-decision leaves the PR in this stroke; a pointer to this ruling goes on the PR.
    • OQ1 (the producer's conversion notices never reach the door envelope) is the seat's follow-up card, as its ACCEPT names it; OQ3 (extending the one-shape rule to serve / migrate / lint / generate) is not opened.
    • Confidence gap recorded: downstream hosts that author plain host configs and run os validate in CI begin to be refused; the prescription is one line.

    State: the card keeps pm:dispatched / os-tesla; the body's Ruled line (5869334748) stands and gains a Confirmed line in the same stroke.

  9. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT — PR #20460 at head 5355f47597 · domain:spec seat 2 (session_014EJ1ED8X4MMrT18BhVx4tx) · 2026-09-29T01:29Z

    The seat adopts the at-tier record and lands the PR, on the maintainer's ruling 5881817230 (batch #240 item 1, letter A, 「同意」 2026-09-29T01:20Z). That ruling confirms ruling B 5869334748 as written, with no host-shape carve-out, and hands the landing to this seat.

    • The one open item is answered. E1 of record 5874193973, this seat's premise report 5874235409: a plain host-shaped default export (plugins: [instance], no defineStack) can reach os validate / os build. Under the ruling it is refused like every other unmarked export, STACK_PROVENANCE_MISSING / 422, with the one-line prescription to wrap it in defineStack(...). The wrap is accepted and pinned. os serve / os migrate keep loading plain host configs. That is exactly what the diff does, so no code changes.
    • PR shape: draft, base main, first line Fixes #20367, assignee os-tesla, cloud dev session_01RTkKf8Dn5F4mepiZZfWoxH. 61 files (+1,463 / −265, 1,728 changed lines, under the 5,000 threshold). NOT governed. needs-user-decision already left the PR with the ruling's pointer 5881858172.
    • At-tier contract review: 5874193973 on the PR, at CONTRACT_REVIEW_TIER, on this head — PASS. It judged:
      • the provenance mark (Symbol.for, non-enumerable, one exported predicate hasStackProvenance);
      • composeStacks refusing an unmarked input;
      • loadConfig reading the mark before its named-export spread;
      • both doors refusing right after load inside the unchanged --json envelope;
      • the new ledger code STACK_PROVENANCE_MISSING;
      • the re-judged pins, and app-crm / app-todo passing both doors.
    • Pre-landing checks, at this stamp:
      • CI on this head: 43 check runs, 36 success, 7 skipped, every one on the roster (check-expected-skips exit 0), 0 failure.
      • The head is 47 commits behind main, and a driver-free merge-tree onto origin/main exits 0. The only files both sides touched are the generated api-surface/root.json and export-origins/root.json: main retypes MetadataConversion and this PR adds hasStackProvenance. These are disjoint lines of sorted lists, so the text merge equals the regeneration.
      • Closing keywords: Fixes #20367 only.
    • Findings and follow-ups:
    • Landing: this seat flips the PR ready and arms auto-merge in the next act.

    Generated by Claude Code

  10. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    ContributorAuthor

    Landing record — PR #20460 MERGED · domain:spec seat 2 (session_014EJ1ED8X4MMrT18BhVx4tx) · 2026-09-29T01:54Z

    • Merged through the merge queue at 2026-09-29T01:53Z as ba5927f714, after the at-tier PASS 5874193973, the maintainer's ruling 5881817230 (ruling B confirmed, no host-shape carve-out) and the ACCEPT 5881910223.
    • Verified by content on origin/main, not by the merged flag:
      • 57 of the 61 files are blob-identical to the reviewed head 5355f47597.
      • The other 4 moved on main while the PR waited: api-surface/root.json, export-origins/root.json, api/error-code-ledger.zod.ts and migrations/registry.ts. For each, the merge commit's own change carries exactly the PR's patch (per-file git patch-id --stable equal).
      • hasStackProvenance is in packages/spec/src/stack-provenance.ts, and STACK_PROVENANCE_MISSING is in the error-code ledger.
    • What ships: os validate and os build refuse a default export that defineStack / composeStacks did not build, before any further judgment. That includes a plain host-shaped { plugins: [instance] }, per the ruling. The answer is STACK_PROVENANCE_MISSING / 422, with the prescription to wrap the export in defineStack(...). composeStacks refuses an unbuilt input the same way. os serve / os migrate keep loading plain host configs.
    • Closing: Fixes #20367 closed this card as completed; pm:dispatched is removed in this act. Its parked follow-up finding(spec+cli): the ADR-0087 conversions defineStack applies never reach os validate / os build --json conversions or the --strict gate — a retiring conversion passes --strict at exit 0 #20476 (OQ1: the conversions defineStack applies never reach --json / --strict) returns to pm:queue in the next act, since its Blocked-by: #20367 is met.

    Generated by Claude Code

  11. added 4 commits that reference this issue on Sep 29, 2026
    ba5927f
    6154165
    487a784
    7a1faf1
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, iteratebugSomething isn't workingdomain:specpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions