Repository navigation
[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
Description
Activity
objectstack-fleet commented
on Sep 28, 2026 ContributorAuthorMore actionsPath: the road — verify | 缺项 (no item runs
os validateon a view whose container name disagrees with its object key) | P3Triage: first grade —
bug·priority:p3·domain:cli·area:devpath·pm:queueTriage: lands in
packages/cli(os validate), which reuses the judgeObjectQL.registerMetadataCollectionsalready applies at boot (packages/objectql/src/engine.ts) ⇒domain:cli. Rationale:os validateexits 0 andos servethen refuses the stack, because the view container's ownnamedisagrees with its object key. The boot refusal is loud and names the fix, so an author loses a cycle, not data ⇒ p3.Triage seat (objectstack-wide, seat post #6015) ·
session_01W89enF2dYV7K4N2Fbfj33f· 2026-09-28T00:23Z. ⛔ Not a claim, ⛔ not a dispatch. Read: this card (no comments), PR #20329 (open; theos g viewscaffold half) and #20301 (pm:queue; RETIRE the view bodyname).Direction: one judge, not a second rule.
os validateruns the same container-name-vs-key check the boot path runs, and reports it with the boot path's words. ⛔ No lint twin.Ordering note. #20301 retires the view container's body
nameonce cloud's writer stops sending it. When that lands, this mismatch cannot be authored, and this card closes as moot. Until then the check is cheap, so this card does not wait on it. Mergemainafter PR #20329.Execution notes.
- Share the check out of
registerMetadataCollections, so validate and boot call one function. - Pin a mismatched authored view (validate refuses with the boot message) and a matching one (the control).
- Share the check out of
- addedarea:devpathThe road — create, dev, verify, publish/install, connect an agent, iterateThe road — create, dev, verify, publish/install, connect an agent, iteratebugSomething isn't workingSomething isn't working
on Sep 28, 2026 objectstack-fleet commented
on Sep 28, 2026 ContributorAuthorMore actionsClaim: PM loop round 3, twentieth slot (batch 3; the review slot freed when PR #20373 (#20338) was accepted; beside #20333 and #20339 on disjoint surfaces)
Session:session_01UYBdGBzWSrAMzpW8ah3GbP
Account:os-litant
Branch:claude/issue-20331-validate-view-container-name
Worktree:objectstack-issue-20331
Domain:domain:cli
Seat:domain:cli#1
File surface (a cross-domain surface, as triage5861222223routes it: execution note 1 shares the check out ofengine.ts):packages/objectql/src/engine.ts: theviews:container-name check insideObjectQL.registerMetadataCollectionsonly (about :6589-:6606), moved into one shared function that the boot path keeps calling;- that function's home under
packages/objectql/src/, and one export line inpackages/objectql/src/index.tsif the CLI needs it; packages/cli/src/commands/validate.ts, or thepackages/cli/src/utils/module it judges the stack with: the one call to the shared function;- tests in
packages/objectqlandpackages/cli; - one
.changeset/20331-*.md.
Read-only:
packages/spec/**andpackages/metadata/**(the artifact/HMR loader's own refusal of the same document is to be reported, not edited). No lint rule. Stop on breach and explain in the report
Container & model:S,mode:subagent,model: default tier(dispatch-gates --tieron this surface: no path-derived mandate).
Clause-②: yes
Thread-read: 5861222223
Serial constraints cleared:PR #20329 (#20215) landed at c5dcb3ba, so the merge triage asked for is on main. #20301 (spec lane, in flight) is stage 1 only, list.tabs; per its claim 5859916882 the view container's body name waits on cloud's writer, so this card does not wait (triage's ordering note). engine.ts is also held by #20334 (PR #20368, engine.aggregate hunks from about :10995) and #20309 (validation/ and the normalizeBlankTypedValues call sites). Both are disjoint from registerMetadataCollections, so this is ordinary same-file concurrency: merge main on conflict. The file lists of all 17 open PRs were read in this act, and none but PR #20368 touches engine.ts or the validate command. The round's other slots are #20333 (packages/create-objectstack) and #20339 (the i18n-extract docblock), both disjointTaken 2026-09-28T04:17Z,
origin/main862b6ce86. Executes triage's grade5861222223: 「one judge, not a second rule」.os validateruns the same container-name-vs-key check the boot path runs, and reports it with the boot path's words. ⛔ No lint twin.Clause-②: yes(amended in place for patch round 1, REWORK on this card):@objectstack/objectqlgains two root exports (viewContainerNameRefusaland its type), a widening of its published surface, so it takesminor, per the PR #20236 (#20193) precedent.@objectstack/clistayspatch: the runtime already refuses this document at boot, andos validatestops passing what boot refuses.
Generated by Claude Code
- added a commit that references this issue
on Sep 28, 2026 objectstack-fleet commented
on Sep 28, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 20331,
"status": "done",
"branch": "claude/issue-20331-validate-view-container-name",
"pr": "#20391",
"session": "session_01UYBdGBzWSrAMzpW8ah3GbP",
"premise_still_valid": true,
"summary": "The premise held at origin/main 862b6ce. On an os init -t app project with a view { name: 'order_line', object: 'my_app_order_line' }, os validate exited 0 while os serve --dev exited 1 with the boot refusal. The divergent view-container name check now lives in @objectstack/objectql's new viewContainerNameRefusal(container, sourceLabel, ownerId). It is unchanged, and registerMetadataCollections throws what it returns. os validate calls the same function at a new step 2c, over the views the load path registers (the top level under the manifest id, or each packages[i].manifest body under its own id), and exits 1. After the fix: validate --json errors[0].message is byte-equal to the os serve --dev line, the controls (a matching name, no name) exit 0, and the boot message is byte-identical before and after. os compile does not reach the function: it is a separate code path, it was not widened, and the gap is recorded in a new VALIDATE_ONLY_GATES ledger row and reported below. The PR carries a new public export, and open_questions asks whether that makes Clause-② yes.",
"tests": "HEAD 4e15d3c. New pins: objectql src/view-container-name-refusal.test.ts 8/8 (the manifest and nested-plugin seams throw exactly what the function returns, plus 5 controls); cli src/utils/view-container-names.test.ts 5/5 (unit); cli test/validate-view-container-name.test.ts 5/5 (integration: --json exit 1 and message === ObjectQL.registerApp throw, text face exit 1, matching and no-name controls exit 0). Package suites: objectql vitest --project local in 4 shards, 323 files / 5865 tests passed; cli --project unit in 2 shards, 232 files / 3315 tests passed; typecheck exit 0 for both; tsc --listFilesOnly shows all 5 new files in a program; pnpm lint (eslint . --no-inline-config) exit 0. The cli integration tier beyond the new file is declared to CI. Ablation (fix committed first, scripts/ablation-replace.mjs wrap): an early return undefined injected into viewContainerNameRefusal took the anchor x1->x0 and the blob 07da29b9->ab52f626; after the objectql rebuild, ablation-dist-preflight found the marker in 4 dist files. Result: objectql 5/20 red, including the existing boot pins "THE PIN (#14666)" and "MEASURED CORRECTION ... enveloped (#7378 row 1)"; cli 6/10 red; the controls stayed green; the direction was red as expected. Restore: git checkout HEAD -- on the absolute path; blob == HEAD 07da29b9, git diff HEAD empty; after the rebuild, preflight --absent found the marker in none of the 14 dist files and a clean tree; then 20/20 and 10/10 green.",
"gates": {
"node scripts/check-adr-0087-registration.mjs --base origin/main": 0,
"node scripts/check-adr-0087-registration.mjs --self-test": 0,
"node scripts/check-changeset-no-major.mjs --base origin/main": 0,
"node scripts/check-changeset-no-major.mjs --self-test": 0,
"node scripts/check-ci-filter-parity.mjs": 0,
"node scripts/check-closing-keyword-parity.mjs": 0,
"node scripts/check-closing-keyword-parity.mjs --self-test": 0,
"node scripts/check-comment-mask-adoption.mjs": 0,
"node scripts/check-comment-mask-adoption.mjs --self-test": 0,
"node scripts/check-comment-mask-corpus.mjs": 0,
"node scripts/check-empty-changeset.mjs --base origin/main": 0,
"node scripts/check-empty-changeset.mjs --self-test": 0,
"node scripts/check-engine-split-ratio.mjs --days 90": 0,
"node scripts/check-engine-split-ratio.mjs --self-test": 0,
"node scripts/check-issue-citations.mjs": 0,
"node scripts/check-keyed-text-bounds.mjs": 0,
"node scripts/check-keyed-text-bounds.mjs --self-test": 0,
"node scripts/check-platform-object-tenancy-census.mjs": 0,
"node scripts/check-platform-object-tenancy-census.mjs --self-test": 0,
"node scripts/check-plugin-teardown-shape.mjs": 0,
"node scripts/check-plugin-teardown-shape.mjs --self-test": 0,
"node scripts/check-registry-log-declared.mjs": 0,
"node scripts/check-registry-log-declared.mjs --self-test": 0,
"node scripts/check-rest-log-spy-declared.mjs": 0,
"node scripts/check-rest-log-spy-declared.mjs --self-test": 0,
"node scripts/check-system-context-census.mjs": 0,
"node scripts/check-system-context-census.mjs --self-test": 0,
"node scripts/check-undeclared-dep-imports.mjs": 0,
"node scripts/check-undeclared-dep-imports.mjs --self-test": 0,
"node scripts/docs-audit/check-affected-docs.mjs": 0,
"node scripts/docs-audit/check-drift-comment.mjs": 0,
"node scripts/pm/release-rehearsal-clone.mjs --self-test": 0,
"pnpm --filter @objectstack/spec run check:duration-unit-keys": 0,
"pnpm check:changeset-gate-self-tests": 0,
"pnpm check:cli-test-child-env": 0,
"pnpm check:cross-package-test-inputs": 0,
"pnpm check:dispatcher-error-vocabulary": 0,
"pnpm check:doc-authoring": 0,
"pnpm check:driver-memory-census": 0,
"pnpm check:dts-closure": 0,
"pnpm check:dual-build-cjs-loads": 0,
"pnpm check:durability-log-level": 0,
"pnpm check:engine-double-contract": 0,
"pnpm check:gitlink-declared": 0,
"pnpm check:i18n": 0,
"pnpm check:i18n-coverage": 0,
"pnpm check:i18n-walk-parity": 0,
"pnpm check:issue-citations": 0,
"pnpm check:lean-entry-closure": 0,
"pnpm check:logger-receiver-detach": 0,
"pnpm check:nul-bytes": 0,
"pnpm check:objectql-double-limit": 0,
"pnpm check:objectui-changeset": 0,
"pnpm check:org-identifier": 0,
"pnpm check:page-declaration-shape": 0,
"pnpm check:pm-changeset-deadline-census": 0,
"pnpm check:published-files": 0,
"pnpm check:query-options-erasure": 0,
"pnpm check:refd-timer-probe": 0,
"pnpm check:slot-lookup": 0,
"pnpm check:sourcemap-no-sources-content": 0,
"pnpm check:stack-collection-maps": 0,
"pnpm check:swallow-census-controls": 0,
"pnpm check:test-source-alias": 0,
"pnpm check:tier-file-adoption": 0,
"pnpm check:type-check-coverage": 0,
"pnpm check:type-check-debt": 3,
"pnpm check:watch-hint-literal": 0,
"pnpm check:where-matcher": 0,
"node scripts/check-issue-citations.mjs --base origin/main": 0,
"pnpm lint": 0,
"pnpm --filter @objectstack/objectql run typecheck": 0,
"pnpm --filter @objectstack/cli run typecheck": 0,
"pnpm --filter @objectstack/objectql exec vitest run --project local --maxWorkers=2 --shard=1/4 .. 4/4": 0,
"pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2 --shard=1/2 .. 2/2": 0,
"pnpm --filter @objectstack/cli exec vitest run --maxWorkers=2 test/validate-view-container-name.test.ts (integration tier)": 0,
"node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran ran.list": 0
},
"gates_note": "69 derived (dispatch-gates --commands at 4e15d3c), --ran reconciled: 68 run, 1 NOT-MEASURED, 0 UNRUN. NOT MEASURED: pnpm check:type-check-debt, reason: its --re-measure runs a whole-workspace turbo run build inside itself, which did not fit the foreground cap on this shared box (the first attempt was SIGTERMed mid-build), then PREREQUISITE NOT MET (exit 3); neither touched package has a DEBT/TEST_DEBT row; lint.yml runs it in CI. Retried to 0 after their prerequisites were met: check:dual-build-cjs-loads, check:i18n, check:i18n-coverage, check:i18n-walk-parity (exit 3 until the CLI, the examples and 12 unbuilt packages were built); check-engine-split-ratio --days 90 (exit 2 on a shallow window until the history was deepened with --shallow-since=2026-06-23); check-issue-citations (exit 2 on 4 added citations that do not resolve, rephrased).",
"files_changed": [
".changeset/20331-validate-view-container-name.md",
"packages/cli/src/commands/validate.ts",
"packages/cli/src/utils/view-container-names.test.ts",
"packages/cli/src/utils/view-container-names.ts",
"packages/cli/test/validate-build-gate-parity.test.ts",
"packages/cli/test/validate-view-container-name.test.ts",
"packages/objectql/src/engine.ts",
"packages/objectql/src/index.ts",
"packages/objectql/src/view-container-name-refusal.test.ts",
"packages/objectql/src/view-container-name-refusal.ts"
],
"deviations": [
"Zone 3 suggested passing the derived key into the shared function; it derives the key itself (deriveViewContainerObject, which is what resolveMetadataItemName returns for a container), so os validate does not re-spell which derivation boot uses. The boot message and envelope are byte-identical (measured).",
"test/validate-build-gate-parity.test.ts gained a VALIDATE_ONLY_GATES ledger and a both-ways prune test. The closed roster reds any unclassified validate.ts call, SHARED_NON_REGISTRY_GATES would require compile.ts to call it (widening, which the order forbids), and a NOT_A_GATE row would be false.",
"The export is on the @objectstack/objectql root entry only, not /core (validate.ts already loads the root barrel through authoring-filter-judge).",
"My gate runner timed out during pnpm check:type-check-debt. Its internal, unlocked whole-workspace turbo build was killed mid-write and left packages/spec/dist in this worktree without declarations. I rebuilt spec under the verify lock; check:dts-closure then swept 72 packages with 166/166 declarations present, and only packages/spec/dist had been rewritten.",
"origin/main was merged once (at b1cbd92, before the PR). It has since moved 3 commits with no file overlap with this diff; not re-merged.",
"The merge commit 7743440 carries git's default merge message and no trailer pair (the pre-push trailer check passed).",
"The Clause-② line was copied verbatim from the claim (no) although the diff adds a public export; see open_questions."
],
"line_budget": "n/a — no skills/** diff",
"mcp_calls": "0 — no MCP GitHub call",
"api_writes": "3 — relay writes, each a POST /repos/objectstack-ai/objectstack/dispatches executed as objectstack-fleet[bot]: (1) pr_create, i.e. POST /repos/objectstack-ai/objectstack/pulls (draft #20391, body read back byte-equal, 10590 bytes); (2) scripts/pm/label-write.mjs --issue 20391 --assign os-litant, i.e. POST /repos//issues/20391/assignees (read back: assignee os-litant; the labels documentation/size-l/tests/tooling came from the path labeler, not this write); (3) this os-dev-report comment, i.e. POST /repos//issues/20331/comments. Plus git push of the branch (not REST). The one pr_create dry-run sent nothing.",
"open_questions": [
{
"question": "Does the new public export viewContainerNameRefusal (plus the type ViewContainerNameRefusal) on the @objectstack/objectql root entry make Clause-② yes? The claim and the dispatch say no/patch on the accept-set reading. The declaration criterion is 「本卡放宽接受集或扩大公开面吗」, and contract-review.md measures the published surface by the exports map, which this export widens.",
"options": [
"A: keep Clause-② no and patch (the dispatch reading: no runtime accept set moves; the export is an internal-contract seam between two packages of this repo).",
"B: declare Clause-② yes (widening) and grade @objectstack/objectql minor. The seat rewrites the PR body line, and the changeset gets '@objectstack/objectql': minor plus a matching Clause-② line (check-changeset-no-major's level axis then needs >= minor on a moved package)."
],
"recommendation": "B, because the criterion names the public surface as well as accept sets, and the exports map is the stated measure; a no here is the auditable false declaration landing-operations.md warns about. The dev did not switch it unilaterally because the claim line is to be copied verbatim."
}
],
"out_of_scope_findings": [
"class: a · reach: public door — os compile (os build) exits 0 on this card's repro stack and writes dist/objectstack.json carrying { name: 'order_line', object: 'my_app_order_line' }; os serve booting that artifact (no config) exits 1 with the same boot refusal (measured at 862b6ce; compile.ts is untouched by this PR and does not call viewContainerNameRefusal, as the VALIDATE_ONLY_GATES row pins) · Seam: runtime:packages/objectql/src/engine.ts ObjectQL.registerMetadataCollections → cli:packages/cli/src/commands/compile.ts (os build) · the same family as #20331 (an author-time door passes what boot refuses), so the seat decides whether it rides a family close-out card; closing it is one call in compile.ts plus moving the ledger row to SHARED_NON_REGISTRY_GATES · dedupe words:os build passes view container name mismatch·compile artifact refused at boot views container name·viewContainerNameRefusal build parity",
"carrier: 承接者:无 · noted in the PR's Acceptance notes, not filed — an empty-string container name (ViewSchema.name is z.string().optional()) is accepted by the boot loop and by os validate (both treat it as absent) but refused at the artifact/HMR door by the generic assertMetadataRegisterContract (packages/core/src/metadata-service-contract.ts); read-only inference, no producer found and no public-door reach measured",
"carrier: 承接者:无 · Acceptance note, not a finding — Zone 2 item 4: the artifact/HMR loader (packages/metadata/src/plugin.ts MetadataPlugin._parseAndRegisterArtifact, container branch around :1103-:1121) is NOT a second spelling of this judgment. It registers under deriveViewContainerObject(item) via this.manager.register('view', key, item), and the refusal is the generic register contract assertMetadataRegisterContract (#7378 row 1) in its own words; not edited"
]
}
Generated by Claude Code
objectstack-fleet commented
on Sep 28, 2026 ContributorAuthorMore actionsREWORK (patch round 1): PR #20391 at
4e15d3ce, two red CI pins, the semver grade and one boot preconditiondomain:cliexecution PM seat #6024 · sessionsession_01UYBdGBzWSrAMzpW8ah3GbP· review verdict, written 2026-09-28T06:24Z- The at-tier independent contract review answered FAIL (record
5864600436on the PR). The seat verified each finding at source and upholds all three. - The review judged the rest correct:
- the moved check is boot's judgment, with the same message and envelope;
- boot throws exactly what the function returns, pinned at both seams against a real
registerApp; - validate derives boot's key, label and owner for the top level and every
packages[]body; - the new root export is the established seam;
- the integration pin compares with boot's REAL throw;
- the
VALIDATE_ONLY_GATESrow is the correct classification; - the
plugins[]bound is named in the PR body.
Required changes:
-
CI is red because of this PR:
Test Core (4/6)and the aggregateTest Core. Two existing CLI integration pins assert exit 0 on stacks boot refuses:test/union-fold-command-parity.test.ts› PEDIGREE CONTROL (fixture about :208, :212:ob_account_list/ob_order_list);test/validate-per-package-authoring-parity.test.ts› per-package advisory parity (about :52, :56:pp_account_list/pp_order_list).
Test Core (4/6)is green on the base, so the red is this PR's. Fix each fixture so boot accepts it: drop the containername, or set it to the bound object. Keep each pin's own assertion unchanged.Then census every fixture under
packages/cli/test/,packages/cli/src/,packages/qa/andexamples/for aviews:container whosenamedisagrees with its derived key, the nightly and.e2etiers included. Fix each in-claim one the same way, and list every hit. Run the CLI integration tier in full:--project integration, plusOS_TEST_TIERS=nightlyon the files your census touches. Round 0 declared that tier to CI, and CI found these. -
The semver grade:
@objectstack/objectql→minor, andClause-②: yesin the changeset.@objectstack/clistayspatch.- This answers your open question B, on the lane's precedent PR fix(runtime,rest): the dispatcher /meta item reads ask the per-caller read gate RestServer asks (#20193) #20236 ([finding] the runtime dispatcher's /meta item reads apply NO per-caller read gate: through a catch-all host, GET /meta/doc/:name serves a permission-set-gated doc body to a non-holder, and /meta/app/:name serves requiredPermissions-gated entries #20193): a gate moved into its own module and exported from the
.entry took'@objectstack/rest': minor+Clause-②: yes. - The changeset states the widening: the two new root exports.
- Send a find/replace pair for the PR body's
Clause-②line; the seat rewrites the body. - The claim
5863262426was amended in place toClause-②: yes.
- This answers your open question B, on the lane's precedent PR fix(runtime,rest): the dispatcher /meta item reads ask the per-caller read gate RestServer asks (#20193) #20236 ([finding] the runtime dispatcher's /meta item reads apply NO per-caller read gate: through a catch-all host, GET /meta/doc/:name serves a permission-set-gated doc body to a non-holder, and /meta/app/:name serves requiredPermissions-gated entries #20193): a gate moved into its own module and exported from the
-
Boot's precondition belongs to the shared judge. Boot's loop skips an item whose derived key is falsy (
if (!itemName) { warn; continue }) BEFORE the check.viewContainerNameRefusalhas no such guard, andderiveViewContainerObjectreturns''forlist.data.object: '', because the??chain keeps an empty string. So validate would refuse a container that boot warns about and skips.- Return
undefinedwhen the derived key is falsy. - Add that control to
view-container-name-refusal.test.ts: boot registers nothing and throws nothing; the function returnsundefined. - Correct the 「moved … byte for byte」 docblock and the 「Moved there unchanged」 comment so they are true of the gate as well.
- Return
Carried in the same push (plainly correct nit):
validate-build-gate-parity.test.ts's unclassified-name remedy text (about :836-:841) still lists three ledgers; nameVALIDATE_ONLY_GATEStoo.Out of scope, recorded by the seat: the
os build/os compilegap you measured (class a, public door) is filed by the seat as its own card after this round, in the same family. Keep theVALIDATE_ONLY_GATESrow and the changeset'sos buildsentence as they are.Gates:
- After merging
origin/main, runpnpm lintandnode scripts/check-issue-citations.mjs --base origin/main. - Run the
@objectstack/objectqland@objectstack/clisuites and typechecks, the CLI integration tier in full, and thecheck-changeset-no-majorlevel axis. - Re-derive and reconcile the gate list.
- Rerun the ablation on the new guard as well: remove it, and the new control goes red.
Report again in the same JSON shape; a delta at-tier review follows.
Generated by Claude Code
- The at-tier independent contract review answered FAIL (record
objectstack-fleet commented
on Sep 28, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 20331,
"status": "done",
"branch": "claude/issue-20331-validate-view-container-name",
"pr": "#20391",
"session": "session_01UYBdGBzWSrAMzpW8ah3GbP",
"premise_still_valid": true,
"round": "patch round 1 (REWORK 5864616314, review 5864600436), head 6b5895b",
"summary": "All three required changes and the nit are in 6b5895b. (1) The two red CI pins (union-fold-command-parity PEDIGREE CONTROL, validate-per-package-authoring-parity) carried containers named *_list bound to _account/_order; each now names its bound object, with the assertions unchanged. The census of packages/cli/test, packages/cli/src, packages/qa and examples (structural scan of 963 tracked files, .e2e tier included) found 15 divergent containers in 9 files, all fixed the same way; the examples pass os validate with 0 refusals. (2) The changeset grades @objectstack/objectql minor with Clause-② yes, states the two new root exports, and keeps @objectstack/cli patch; the PR-body Clause-② line is in body_edits. (3) viewContainerNameRefusal returns undefined when the derived key is falsy (boot's warn-and-skip precondition), pinned by a new control, and the docblock and engine.ts comment now say the gate moved whole. The gate-parity remedy text names VALIDATE_ONLY_GATES. The CLI integration tier ran in full (61 files green), and 69/69 derived gates exit 0, check:type-check-debt now measured.",
"tests": "HEAD 6b5895b. objectql --project local, 4 shards: 325 files / 6017 tests passed. view-container-name-refusal.test.ts is 9/9 with the new falsy-key control (boot throws nothing, registry.listItems('view') is [], the function returns undefined; premise guard deriveViewContainerObject(...) === ''). cli --project unit, 2 shards: 233 files / 3335 tests passed. cli --project integration IN FULL, 3 shards: 61 files / 512 passed, 1 skipped, including both formerly red pins and validate-view-container-name.test.ts 5/5. OS_TEST_TIERS=nightly: the census touched no .e2e file, so the 13 .e2e files that run os validate were run: 13 files / 129 tests passed. Typecheck exit 0 for both packages. pnpm lint exit 0. Guard ablation via scripts/ablation-replace.mjs --delete of the 'if (!itemName) return undefined;' line (anchor x1->x0, blob 21af64e5->56d25d71): exactly 1 of 21 went red, the new control (the function returned "Invalidviews:container ... binds to, ''" where undefined was expected); the existing boot pins stayed green (boot keeps its own skip). The ablation touches objectql src only and the suite imports ./view-container-name-refusal, so no dist leg was needed. Restore: blob == HEAD 21af64e5, git diff HEAD empty, 21/21 green. The round-0 ablation of the refusal path (5/20 and 6/10 red, restore proven) is unchanged; this round did not alter that path.",
"gates": {
"node scripts/check-adr-0087-registration.mjs --base origin/main": 0,
"node scripts/check-adr-0087-registration.mjs --self-test": 0,
"node scripts/check-changeset-no-major.mjs --base origin/main": 0,
"node scripts/check-changeset-no-major.mjs --self-test": 0,
"node scripts/check-ci-filter-parity.mjs": 0,
"node scripts/check-closing-keyword-parity.mjs": 0,
"node scripts/check-closing-keyword-parity.mjs --self-test": 0,
"node scripts/check-comment-mask-adoption.mjs": 0,
"node scripts/check-comment-mask-adoption.mjs --self-test": 0,
"node scripts/check-comment-mask-corpus.mjs": 0,
"node scripts/check-empty-changeset.mjs --base origin/main": 0,
"node scripts/check-empty-changeset.mjs --self-test": 0,
"node scripts/check-engine-split-ratio.mjs --days 90": 0,
"node scripts/check-engine-split-ratio.mjs --self-test": 0,
"node scripts/check-issue-citations.mjs": 0,
"node scripts/check-keyed-text-bounds.mjs": 0,
"node scripts/check-keyed-text-bounds.mjs --self-test": 0,
"node scripts/check-platform-object-tenancy-census.mjs": 0,
"node scripts/check-platform-object-tenancy-census.mjs --self-test": 0,
"node scripts/check-plugin-teardown-shape.mjs": 0,
"node scripts/check-plugin-teardown-shape.mjs --self-test": 0,
"node scripts/check-registry-log-declared.mjs": 0,
"node scripts/check-registry-log-declared.mjs --self-test": 0,
"node scripts/check-rest-log-spy-declared.mjs": 0,
"node scripts/check-rest-log-spy-declared.mjs --self-test": 0,
"node scripts/check-system-context-census.mjs": 0,
"node scripts/check-system-context-census.mjs --self-test": 0,
"node scripts/check-undeclared-dep-imports.mjs": 0,
"node scripts/check-undeclared-dep-imports.mjs --self-test": 0,
"node scripts/docs-audit/check-affected-docs.mjs": 0,
"node scripts/docs-audit/check-drift-comment.mjs": 0,
"node scripts/pm/release-rehearsal-clone.mjs --self-test": 0,
"pnpm --filter @objectstack/spec run check:duration-unit-keys": 0,
"pnpm check:changeset-gate-self-tests": 0,
"pnpm check:cli-test-child-env": 0,
"pnpm check:cross-package-test-inputs": 0,
"pnpm check:dispatcher-error-vocabulary": 0,
"pnpm check:doc-authoring": 0,
"pnpm check:driver-memory-census": 0,
"pnpm check:dts-closure": 0,
"pnpm check:dual-build-cjs-loads": 0,
"pnpm check:durability-log-level": 0,
"pnpm check:engine-double-contract": 0,
"pnpm check:gitlink-declared": 0,
"pnpm check:i18n": 0,
"pnpm check:i18n-coverage": 0,
"pnpm check:i18n-walk-parity": 0,
"pnpm check:issue-citations": 0,
"pnpm check:lean-entry-closure": 0,
"pnpm check:logger-receiver-detach": 0,
"pnpm check:nul-bytes": 0,
"pnpm check:objectql-double-limit": 0,
"pnpm check:objectui-changeset": 0,
"pnpm check:org-identifier": 0,
"pnpm check:page-declaration-shape": 0,
"pnpm check:pm-changeset-deadline-census": 0,
"pnpm check:published-files": 0,
"pnpm check:query-options-erasure": 0,
"pnpm check:refd-timer-probe": 0,
"pnpm check:slot-lookup": 0,
"pnpm check:sourcemap-no-sources-content": 0,
"pnpm check:stack-collection-maps": 0,
"pnpm check:swallow-census-controls": 0,
"pnpm check:test-source-alias": 0,
"pnpm check:tier-file-adoption": 0,
"pnpm check:type-check-coverage": 0,
"pnpm check:watch-hint-literal": 0,
"pnpm check:where-matcher": 0,
"pnpm check:type-check-debt": 0,
"node scripts/check-issue-citations.mjs --base origin/main": 0,
"pnpm lint": 0,
"node scripts/check-changeset-no-major.mjs --base origin/main --event event-yes.json (the level axis, PR body with Clause-② yes)": 0,
"pnpm --filter @objectstack/objectql run typecheck": 0,
"pnpm --filter @objectstack/cli run typecheck": 0,
"pnpm --filter @objectstack/objectql exec vitest run --project local --maxWorkers=2 --shard=1/4 .. 4/4": 0,
"pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2 --shard=1/2 .. 2/2": 0,
"pnpm --filter @objectstack/cli exec vitest run --project integration --maxWorkers=2 --shard=1/3 .. 3/3": 0,
"OS_TEST_TIERS=nightly pnpm --filter @objectstack/cli exec vitest run --maxWorkers=2 (13 nightly .e2e files that run os validate)": 0,
"node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran ran.list": 0
},
"gates_note": "69 derived (dispatch-gates --commands at 6b5895b, the same 69 as round 0), --ran reconciled: 69 run, 0 NOT-MEASURED, 0 UNRUN, all exit 0. check:type-check-debt, NOT MEASURED in round 0, is measured now: all packages were built through turbo first, so its internal closure build was cache hits ("4 ledger entr(ies) re-measured, none above its recorded number"). Its first run this round refused PREREQUISITE NOT MET, because the ablation restore left objectql's src mtime newer than its dist; objectql was rebuilt directly, then it exited 0. Level axis: driven offline with --event on the stored PR body with Clause-② flipped to yes, it prints "✓ LEVEL AXIS: this PR declares clause-②yes, and it grades a package ... at minor ... @objectstack/objectql: minor". Against the body as currently stored (still no) it reads the no declaration and passes.",
"census": [
"packages/cli/test/union-fold-command-parity.test.ts: ob_account_list->ob_account, ob_order_list->ob_order (red CI pin)",
"packages/cli/test/validate-per-package-authoring-parity.test.ts: pp_account_list->pp_account, pp_order_list->pp_order (red CI pin)",
"packages/cli/test/build-text-face-advisory-count.test.ts: bc_account_list->bc_account, bc_order_list->bc_order",
"packages/cli/test/format-zod-union.test.ts: union_probe_view->union_probe_obj",
"packages/cli/test/info-detail-package-fold.test.ts: ob_order_views->ob_order",
"packages/cli/test/lint-handwritten-checks-package-fold.test.ts: probe_view->probe_order",
"packages/cli/test/lint-label-case-localized.test.ts: invoice_views->invoice (x2)",
"packages/cli/test/lint-per-package-authoring-parity.test.ts: pp_account_list->pp_account, pp_order_list->pp_order",
"packages/cli/test/lint-per-package-authoring-seam.test.ts: pp_account_list->pp_account, pp_order_list->pp_order",
"no hit: packages/qa/, examples/ (structural; and os validate --json on app-crm, app-multi-package, app-showcase, app-todo all exit 0 with 0 container refusals); no .e2e file hit",
"not a hit: doctor-refs.test.ts views: [{ name: contacts, object: crm_contact }] has no list/form arm, so it is not an aggregated container and boot keys it by its own name",
"method: a string/comment-aware object-literal walker over every tracked .ts/.js/.mjs/.cjs/.mts/.cts/.json file in the four roots; views is not in MAP_SUPPORTED_FIELDS, so no map key can inject a name. Bound: a container built by a function or variable (the deliberate divergent fixture in validate-view-container-name.test.ts is one) is not read by the scanner; the full CLI integration and unit runs cover those files behaviourally."
],
"files_changed": [
".changeset/20331-validate-view-container-name.md",
"packages/cli/src/commands/validate.ts",
"packages/cli/src/utils/view-container-names.test.ts",
"packages/cli/src/utils/view-container-names.ts",
"packages/cli/test/build-text-face-advisory-count.test.ts",
"packages/cli/test/format-zod-union.test.ts",
"packages/cli/test/info-detail-package-fold.test.ts",
"packages/cli/test/lint-handwritten-checks-package-fold.test.ts",
"packages/cli/test/lint-label-case-localized.test.ts",
"packages/cli/test/lint-per-package-authoring-parity.test.ts",
"packages/cli/test/lint-per-package-authoring-seam.test.ts",
"packages/cli/test/union-fold-command-parity.test.ts",
"packages/cli/test/validate-build-gate-parity.test.ts",
"packages/cli/test/validate-per-package-authoring-parity.test.ts",
"packages/cli/test/validate-view-container-name.test.ts",
"packages/objectql/src/engine.ts",
"packages/objectql/src/index.ts",
"packages/objectql/src/view-container-name-refusal.test.ts",
"packages/objectql/src/view-container-name-refusal.ts"
],
"files_changed_this_round": [
".changeset/20331-validate-view-container-name.md",
"packages/cli/test/build-text-face-advisory-count.test.ts",
"packages/cli/test/format-zod-union.test.ts",
"packages/cli/test/info-detail-package-fold.test.ts",
"packages/cli/test/lint-handwritten-checks-package-fold.test.ts",
"packages/cli/test/lint-label-case-localized.test.ts",
"packages/cli/test/lint-per-package-authoring-parity.test.ts",
"packages/cli/test/lint-per-package-authoring-seam.test.ts",
"packages/cli/test/union-fold-command-parity.test.ts",
"packages/cli/test/validate-build-gate-parity.test.ts",
"packages/cli/test/validate-per-package-authoring-parity.test.ts",
"packages/objectql/src/engine.ts",
"packages/objectql/src/view-container-name-refusal.test.ts",
"packages/objectql/src/view-container-name-refusal.ts"
],
"deviations": [
"The worktree was re-added on the existing branch without force, then origin/main was merged at 0d7ed5a (merge commit a6e3662, git default message, no trailer pair; no conflict). origin/main has since moved 6 commits; one, #20368, touches engine.ts in the engine.aggregate hunks only, and a git merge-tree probe merges cleanly, so I did not merge again.",
"The nightly leg ran the 13 .e2e files that run os validate, because the census touched no nightly-tier file and OS_TEST_TIERS=nightly on the touched files would have collected nothing.",
"Seven more fixtures than the two named were fixed under the REWORK's census instruction (all under packages/cli/test, inside the claim's test surface). They run os build / os lint / os info, which do not call the check, so they were not red; each is still a stack boot refuses.",
"A classifier outage (7 consecutive no-verdict responses) interrupted the round mid-build; nothing ran unrecorded, and the killed first turbo build was resumed and completed from cache."
],
"line_budget": "n/a — no skills/** diff",
"mcp_calls": "0 — no MCP GitHub call",
"api_writes": "1 — this os-dev-report comment through scripts/pm/post-stamped.mjs, a relay POST /repos/objectstack-ai/objectstack/dispatches executed as objectstack-fleet[bot], i.e. POST /repos//issues/20331/comments. Plus git push of 6b5895b (not REST). The PR body was not edited (see body_edits). Reads: REST GETs of the REWORK, the review record, the card's comments and PR #20391.",
"body_edits": [
{
"find": "Clause-②: no",
"replace": "Clause-②: yes"
},
{
"find": "It carries the same gate: the container branch (isAggregatedViewContainer), and anamethat is present and differs from the derived key.",
"replace": "It carries the same gate: the container branch (isAggregatedViewContainer), and anamethat is present and differs from the derived key. It also carries boot's precondition for that gate: a falsy derived key is boot's warn-and-skip, and the function answersundefinedfor it."
},
{
"find": "viewContainerNameRefusaland its typeViewContainerNameRefusal. The changeset states it.",
"replace": "viewContainerNameRefusaland its typeViewContainerNameRefusal. This is the widening behindClause-②: yes:@objectstack/objectqlis gradedminor, and@objectstack/clistayspatch. The changeset states it."
},
{
"find": "packages/objectql/src/view-container-name-refusal.test.ts(new, 8 tests)",
"replace": "packages/objectql/src/view-container-name-refusal.test.ts(new, 9 tests, with the falsy-derived-key control added in patch round 1)"
},
{
"find": "- Upstream:origin/mainwas merged atb1cbd9277. It has since moved 3 commits, and none of them touches a file in this diff. I did not merge again.",
"replace": "- Upstream:origin/mainwas merged atb1cbd9277, and again at0d7ed5a37in patch round 1. It has since moved 6 commits. One of them, #20368, touchesengine.tsin theengine.aggregatehunks only, and agit merge-treeprobe merges cleanly, so I did not merge again."
},
{
"find": "## Acceptance notes",
"replace": "## Patch round 1 (head6b5895b97, after the FAIL review of4e15d3cea)\n\n1. Two red CI pins, and every fixture of the same shape.test/union-fold-command-parity.test.ts(PEDIGREE CONTROL) andtest/validate-per-package-authoring-parity.test.tsasserted exit 0 on stacks the boot registrar refuses: each carried containers named*_listbound to*_account/*_order. Each container now names its bound object, and each pin's assertion is unchanged.\n - Census ofpackages/cli/test/,packages/cli/src/,packages/qa/andexamples/(all 963 tracked.ts/.js/.jsonfiles, the.e2etier included). A structural scan read every object literal carryingnameplus a container arm (list/form/listViews/formViews) and noviewKind, and derived the key as boot does. It found 15 divergent containers in 9 files, all fixed the same way: the 4 above,build-text-face-advisory-count.test.ts(2),format-zod-union.test.ts(1),info-detail-package-fold.test.ts(1),lint-handwritten-checks-package-fold.test.ts(1),lint-label-case-localized.test.ts(2),lint-per-package-authoring-parity.test.ts(2),lint-per-package-authoring-seam.test.ts(2). A rescan finds none, apart from the docblock examples in this PR's own files.examples/:os validateexits 0 on each of app-crm, app-multi-package, app-showcase and app-todo, with 0 container refusals.packages/qa/: no hit.viewsis not a map-form collection (MAP_SUPPORTED_FIELDSexcludes it), so no key-injected name can hide there.\n2. Semver:@objectstack/objectqlis nowminorwithClause-②: yes, for the two new root exports (viewContainerNameRefusal,ViewContainerNameRefusal).@objectstack/clistayspatch. The changeset states the widening.\n3. Boot's precondition moved with the gate.registerMetadataCollectionswarns about and skips an entry whose derived key is falsy before the name check runs.viewContainerNameRefusalnow returnsundefinedfor that entry too. The derivation keeps''forlist: { data: { object: '' } }, so without thisos validatewould refuse a container that boot only skips. A new control pins it: boot throws nothing and registers nothing, and the function returnsundefined. The docblock and theengine.tscomment now say what moved: the message and the envelope moved byte for byte, and the gate moved whole, precondition included.\n4. The gate-parity remedy text namesVALIDATE_ONLY_GATEStoo.\n\nRuns at6b5895b97:\n\n-@objectstack/objectql:--project localin 4 shards, 325 files / 6017 tests passed. Typecheck exit 0. The new test file is 9/9.\n-@objectstack/cli:--project unitin 2 shards, 233 files / 3335 tests passed.--project integrationIN FULL in 3 shards, 61 files / 512 tests passed and 1 skipped. Typecheck exit 0.\n-OS_TEST_TIERS=nightly: the census touched no.e2efile, so the 13 nightly files that runos validatewere run instead. 13 files / 129 tests passed.\n-pnpm lintexit 0.check-issue-citations --base origin/mainexit 0.\n-check-changeset-no-majorlevel axis: driven with--eventon this body withClause-②: yes, it answers "✓ LEVEL AXIS …@objectstack/objectql: minor" (exit 0).\n- Gates: 69 derived,--ranreconciles 69 run, 0 NOT-MEASURED, 0 UNRUN, every one exit 0. That includescheck:type-check-debt: 4 entries re-measured, none above its number.\n- Guard ablation:scripts/ablation-replace.mjs --deleteremoved theif (!itemName) return undefined;line (anchor x1 to x0, blob21af64e5to56d25d71). Exactly the new control went red: the function returned "Invalidviews:container … binds to, ''" whereundefinedwas expected. The other 20 tests stayed green, the existing boot pins among them. The restore gave a blob equal to HEAD21af64e5and an emptygit diff HEAD, then 21/21 green.\n\n## Acceptance notes"
}
],
"body_edits_note": "Six find/replace pairs against the stored body of PR #20391 (byte-identical to the round-0 body, read back at 6b5895b). Each find occurs exactly once in the stored body (checked); the pairs are order-independent, and no replacement contains an angle bracket. Applied together, the body is 14902 bytes. The first pair flips the line-initial Clause-② line to yes; the last inserts a "## Patch round 1" section before "## Acceptance notes".",
"open_questions": [],
"out_of_scope_findings": [
"carrier: the seat — the os build / os compile gap (class a, public door, measured in round 0) is filed by the seat after this round, as the REWORK states; the VALIDATE_ONLY_GATES row and the changeset's os build sentence are unchanged",
"carrier: 承接者:无 · noted in the PR's Acceptance notes, not filed — an empty-string container name is accepted by the boot loop and os validate but refused at the artifact/HMR door by the generic assertMetadataRegisterContract; read-only inference, no producer found"
]
}
Generated by Claude Code
- added a commit that references this issue
on Sep 28, 2026 objectstack-fleet commented
on Sep 28, 2026 ContributorAuthorMore actionsClaim: PM loop round 1 of the
domain:cliseat's new session: a takeover of claim5863262426and of PR #20391, same branch (maintainer arbitration, seat post5869029598)
Session:local_1d2a197c-c20e-4e90-9be8-413d4d432289
Account:hotlong
Branch:claude/issue-20331-validate-view-container-name
Worktree:objectstack-issue-20331
Domain:domain:cli
Seat:domain:cli#1
File surface: unchanged from claim5863262426as amended in place (PR #20391's 19 files); this claim reviews and lands the PR, and no dev is dispatched now.
Container & model:S,mode:subagent,model: default tier(unchanged); the owed delta review runs as an isolatedCONTRACT_REVIEW_TIERsubagent.
Clause-②: yes
Thread-read: 5866334331
Serial constraints cleared:The file lists of all 14 open PRs were read in this act: none but PR #20391 touches packages/objectql/src/engine.ts, packages/cli/src/commands/validate.ts or packages/cli/src/utils/view-container-names.ts, and no in-flight card claims them
Release: sessionsession_01UYBdGBzWSrAMzpW8ah3GbP(os-litant), claim5863262426· reason: the claimant is unreachable, its seat session ran out of tokens · destination: this claim, same branch; assigneeos-litant→hotlongin this stroke · provenance: the maintainer, in live chat with desktop sessionlocal_1d2a197c-c20e-4e90-9be8-413d4d432289after/pm-dispatch cli, verbatim 「前任没有token了,你接管,包括他的pr」Hand-over record: PR #20391's head is
6b5895b9, the dev's patch round 1 (report5866334331, executing REWORK5864616314). The last review record on the PR is the round-0 FAIL5864600436; the delta contract review at this head is owed and runs now. CI at this head: 31 success, 3 skipped, 0 red. Taken 2026-09-28T11:37Z,origin/maindbddf02c.objectstack-fleet commented
on Sep 28, 2026 ContributorAuthorMore actionsACCEPT: PR #20391 at
6b5895b9, R1 of thedomain:cliseat's new session (after patch round 1)domain:cliexecution PM seat #6024 · sessionlocal_1d2a197c-c20e-4e90-9be8-413d4d432289· review of record, written 2026-09-28T12:02ZReviewed against GitHub and
origin/main, not against the report.Checklist:
- PR shape: draft, base
main. The first line isFixes #20331, followed by a line-initialClause-②: yes. The seat applied the dev's sixbody_editspairs (report5866334331) to the live body; eachfindmatched exactly once. A scan of the whole body finds one closing keyword,Fixes #20331.mergeable_state: clean. - Scope: 19 files, all inside claim
5863262426as amended in place and taken over by5869087302.check-governed-merges --pr 20391: NOT governed; +844 −69, under the 5000-line fork. The read-only fences (packages/spec/**,packages/metadata/**) are untouched. - Review route: the changeset prose and the new
@objectstack/objectqlroot exports make this a contract-review surface, andClause-②: yesis declared.- Round 0's at-tier record (
5864600436, FAIL at4e15d3ce) named three required changes. - The delta record (
5869428914) is PASS at this head, and judges each round-0 finding resolved. The seat verified its transcript: served atCONTRACT_REVIEW_TIER, read-only, no GitHub write.
- Round 0's at-tier record (
- The seat's own readings at this head:
view-container-name-refusal.ts:99carriesif (!itemName) return undefined;, the boot precondition.- The changeset grades
@objectstack/objectqlminorand@objectstack/clipatch. compile.tshas zero calls to the shared judge, so theVALIDATE_ONLY_GATESrow holds.
- CI on head
6b5895b9: 41 check-runs, 36successand 5skipped, 0 red. That includes the body-reading re-runs after the seat's edit:Check Changesetprints the level axis onClause-②: yes. Round 0's red (Test Core (4/6)and the aggregate) is green. - Commits: all four non-merge commits carry the model-free trailer pair. The two merge commits (
77434405,a6e3662a) carry git's default message and no pair; they are squashed on landing.
Out-of-scope findings, one line each:
- The
os build/os compilegap (class a, public door): filed [finding]os build/os compilewrites an artifact with a view container whosenamedisagrees with its object key, andos servethen refuses that artifact at boot #20393,pm:blockedwithBlocked-by: #20331. It unlocks when this PR lands. - An empty-string container
nameis accepted by the boot loop and byos validate, but refused at the artifact/HMR door.Acceptance notes: a read-only inference with no producer found, pre-existing on both sides.
Next: landing now, as
markPullRequestReadyForReview+enablePullRequestAutoMerge(SQUASH) asobjectstack-fleet[bot], the same two mutations the relay opspr_ready+automerge_enablesend.- PR shape: draft, base
objectstack-fleet commented
on Sep 28, 2026 ContributorAuthorMore actionsLanded: PR #20391 →
c5d6b2bd87244d4d7e44597c7d5cd783a618f9fcdomain:cliexecution PM seat #6024 · sessionlocal_1d2a197c-c20e-4e90-9be8-413d4d432289· landing record, written 2026-09-28T12:25Z- Merged through the merge queue: readied and armed 2026-09-28T12:03Z, merged 2026-09-28T12:22Z. The landing is a squash (
git rev-list --parents -n 1gives 2 fields), and it is an ancestor oforigin/main. - Content read on
origin/main:packages/objectql/src/view-container-name-refusal.tscarriesviewContainerNameRefusaland the boot preconditionif (!itemName) return undefined;.packages/objectql/src/index.tsexports it;engine.tscalls it (3 hits);packages/cli/src/utils/view-container-names.tsuses it (3).validate.tscalls the walker (3 hits);compile.tscalls it 0 times, as theVALIDATE_ONLY_GATESrow states.
- Card: closed
completedby the PR'sFixes #20331.pm:dispatchedis stripped in this stroke. - Lane reconcile: the open
domain:cliset went from 13 to 12 cards, and the only card missing is this one. - Unlock: [finding]
os build/os compilewrites an artifact with a view container whosenamedisagrees with its object key, andos servethen refuses that artifact at boot #20393 (Blocked-by: #20331) is re-read against the merged ref in the same act, on its own thread.
- Merged through the merge queue: readied and armed 2026-09-28T12:03Z, merged 2026-09-28T12:22Z. The landing is a squash (
- added a commit that references this issue
on Sep 28, 2026
Filing gate: ① a defect with a named landing site. The refusal lives in
packages/objectql/src/engine.tsObjectQL.registerMetadataCollections(the view container name vs its derived object key), andos validatehas no author-time counterpart. Finding class (a), withreach:measured at a public door.Found by the
os-devround on #20215 (PR #20329, which fixes theos g viewSCAFFOLD's name). Filed by thedomain:cliexecution seat (#6024,session_01UYBdGBzWSrAMzpW8ah3GbP). ⛔ Filed bare: routing and grading belong to triage. ⛔ Not a claim.What happens (measured by the dev at
origin/main6a6a17b6, relayed)os init -t appproject, hand-wired, with the view scaffoldname: 'order_line'bound to the objectmy_app_order_line.os validateexits 0.os serve --devrefuses at boot: 「Invalid views: container from manifest com.example.my-app: the container's own name is order_line, which disagrees with the object key it binds to, my_app_order_line」.PR #20329 makes the
os g viewscaffold write a matching name. That fixes the scaffold only: any AUTHORED view with the same mismatch still validates green and fails at boot.The contract it contradicts
os validateis the author-time judge of what the runtime will accept (NORTH-STAR road step ①). A greenos validatefollowed by a boot refusal is the silent-validator shape.Seam
runtime:packages/objectql/src/engine.ts ObjectQL.registerMetadataCollections(the view container name vs its object key) →cli:os validate, which has no author-time counterpart. Where the rule belongs (spec schema, lint rule, or validate) is triage's call.Dedupe
MCP issue search in this repository, run 2026-09-27: 「os validate passes view whose container name disagrees with object key, os serve refuses at boot registerMetadataCollections」 → 7 hits.
os generate's non-object scaffolds never reach anos initproject's stack (the config imports only./src/objects), soos validatepasses with 0 flows and 0 apps; once wired, the flow scaffold is refused #20215 (open) is the parent round.os validate/os buildaccept a view container whoseobjectnames no object in the stack — no error, no advisory — and the runtime'sgetViewsByObjectthen never finds the view #20216 (closed) is a view whoseobjectnames no object, a different check.os generate object NAMEin anos init -t appproject writesname: 'NAME'with no namespace prefix, so the nextos validaterefuses the object the CLI just generated #20197, os validate and os lint judge an EMPTY stack when a project declares its metadata only in packages[] — the ADR-0130 D4 union fold (authoringRuleUnionStack) is wired into os build alone #17069, docs: the sampleos validatetranscript on validating-metadata.mdx prints a stale author-time rule count (38 shown, registry holds more) #9034,objectstack devwatcher rebuilds dist/objectstack.json but the running server keeps serving the metadata it booted with #5148 and lint: no reference-integrity or option-key validation for app metadata #3583 are closed and unrelated.None of them is this defect.
Dedupe words:
views container name disagrees object key·os validate passes view serve refuses·registerMetadataCollections view name mismatchGenerated by Claude Code