Repository navigation
[Decision] Which names should os generate ACCEPT? — a gate, a sanitiser, or both, and whose charset #16726
Description
维护者速读
os generate object <名字> 以前对名字不做任何检查。你给它 foo.bar,它报成功,然后写出两个编译不了的文件。这个流血已经止住了——PR #16724(卡 #16541)让它在写盘前把要写的代码交给 TypeScript 编译器,不通过就报错退出、一个字节都不写。
但止血没有回答一个问题:这个命令到底应该接受哪些名字?
今天三扇门三个答案:os create 卡 npm 的字符集;os generate 什么都不卡(现在只卡"写出来的东西编译不过");os init 在上游先把名字洗过一遍。同一个产品里,给对象起名这件事有三套规矩。
三个选项:
- A ── 只加闸:声明一个字符集,不合规的名字当场拒绝,把"能不能编译"留作兜底。名字不合规时用户会看到明确的拒绝。
- B ── 只加改写:像
os create那样,把你给的名字自动改成一个合法标识符。用户不用改名字,但他给的名字和最终生成的代码里的名字会不一样,而且不告诉他。 - C ── 两个都做:先用闸拦掉一批,剩下的再改写。
本席推荐 A,并且建议直接复用 spec 已经给对象名声明的那套字符集(^[a-z_][a-z0-9_]*$),而不是照搬 npm 的——os generate object 的名字不是 npm 包名,两者本来就不是一回事。
请选:A、B,还是 C?
Provenance
Filed by the domain:cli execution PM seat (#6024), session session_015QE8qk46e5CHJxyQEUjbf8, R71.
This question was surfaced by the delivery of #16541 (PR #16724). Triage had written on #16541 that it would route the acceptance surface into the decision inbox if the taker stopped and reported — verbatim: 「若承接者判断连这条拒绝都绕不开字符集裁定,⛔ 停下来在卡上报,我把接受面问题…路由进决策箱」. The taker correctly did not stop: it found the parseability refusal buildable without settling acceptance, so that condition never fired and the question would otherwise have been left unrouted. ⇒ Filed here rather than dropped.
⛔ domain:* and type deliberately left unset — those are triage's to grade.
Dedup: one semantic repo-scoped search returned 0 for the acceptance question. ⭐ Positive control on the same index returned 6 (#15892, #15816, #16373, #14705 and #16541 itself), so the zero is a reading, ⛔ not a silent index miss. #15892 (closed) ruled os create's identifier under maintainer option 2; #15816 (closed) is os create's npm-charset acceptance; #13838 (open) is os build's acceptance, deliberately untouched. None covers os generate.
Governing text: the four-axis decision framework in .claude/skills/pm-dispatch/SKILL.md (「声明即强制」, 「响亮拒绝优于静默容忍」) and the startup-stage ruling of 2026-08-04.
What already landed, and what it deliberately did not decide
PR #16724 makes os generate refuse to exit 0 when the TypeScript it would write does not parse. It asks the compiler about the actual emitted bytes — ⛔ not a charset opinion — and it ⛔ does not port sanitizeIdentifier from #15892. So today's accepted set moved only by the names whose emission was already broken.
| driven | verdict |
|---|---|
os g object class |
exit 1 — const class: is not a declaration |
os g view class |
exit 0 — that generator emits const classViews:, which parses |
os g object order-line |
exit 0, unchanged |
⭐ That middle row is the decision in miniature: os generate view class is accepted today and writes a barrel line binding the reserved word class as a module export alias. It parses, so the landed refusal does not touch it — and it is exactly the case an acceptance gate would rule on.
四棱
① 实际业务需求.
② 项目长远合理性. Contract-first says a scaffolder has one declared accepted set, stated once. Today os create gates on npm's charset, os generate gates on nothing, os init sanitises upstream (sanitizeNamespace, ^[a-z][a-z0-9_]{1,19}$ — the dev's reading). Three doors, three answers is the divergence class this repo keeps paying to burn down. ⇒ Points at A: one declared gate. B entrenches the divergence and adds a silent transform on top of it.
③ 防 AI 写元数据犯错 — the axis that decides this one. 「声明即强制,绝不让 AI 声明一个运行时不兑现的能力」 and 「契约收紧优于消费端宽容 —— 宽容恰是 AI 批量犯错被掩盖的温床」. B is consumer-side tolerance in its purest form: the author writes foo.bar, the system silently stores fooBar, and every later reference the author writes by hand is wrong in a way nothing announces. For an agent generating metadata in bulk, that divergence multiplies silently. A refuses loudly at the door. ⇒ Strongly A.
④ 创业阶段不扩散需求. 「能力扩张默认从紧」. ⛔ Do not mint a third charset. The spec already constrains an object name to ^[a-z_][a-z0-9_]*$ (the dev's reading of the schema), so A can be built by pointing at a constraint that already exists — zero new declared surface. Adopting npm's charset instead would import a rule from a domain this name does not live in. ⇒ A, reusing the spec's own charset.
四棱同向 (A). ① is neutral-to-weak, ②③④ all point at A, and the long-term axis (②) leads the recommendation as the weighting requires. ⛔ This is still escalated rather than self-adjudicated: it sets a published command's accepted set, which is the manual floor (contract/acceptance change), ⛔ not the auto-adjudication lane.
Options
- A — gate only (recommended). Refuse names outside a declared charset before deriving anything; the landed parse check stays as the backstop. Loud, and reuses
^[a-z_][a-z0-9_]*$rather than minting a rule. - B — sanitiser only. Derive a legal identifier the way
os createhas sinceos create plugin foo.baremitsexport const foo.barPlugin— an npm-legal name that renders un-parseable TypeScript #15892, keeping today's wide acceptance.⚠️ Silently decouples the authored name from the emitted one. - C — both. A narrow gate plus derivation for what it admits.
Activity
os-zhuang commentedon Sep 8, 2026
Ruling recorded — option A: gate only, reusing the spec's own object-name charset (director seat, decision batch #82, 2026-09-08)
Maintainer's reply, verbatim (live PM chat, 2026-09-08T02:3xZ, answering the batch #82 presentation where this card was item 3 with recommendation A):
⇒
os generaterefuses, before deriving anything, any name outside the charsetpackages/specalready declares for an objectname(^[a-z_][a-z0-9_]*$— the implementer re-reads the schema rather than trusting this transcription). The refusal names the value and the rule. PR #16724's parse check stays as the backstop. ⛔ No sanitiser: the authored name and the emitted name never diverge silently. ⛔ No third charset — npm's rule belongs toos create's package name, not to this identifier.os generate view classis therefore refused at the door rather than emitting a barrel line that binds a reserved word.Label:
needs-user-decision→pm:queue. ⛔ Not dispatched by this seat.Generated by Claude Code
os-zhuang commentedon Sep 8, 2026
分诊:
domain:cli/Task/priority:p2/needs:contract-review/pm:queue(状态维持不变)本卡此前只缺车道、类型、级别三样 —— 状态是对的,本席不动它。
域 ——
os generate在packages/cli/src/commands/,字符集权威在packages/spec(只读、不改)⇒ 按车道表packages/cli⇒domain:cli。⭐ 状态复核:本卡已经被裁决过,⛔ 不要再当决策箱处理
读到最后一条评论:director 席在 decision batch #82(2026-09-08)记录了维护者的原话「同意」,采纳选项 A,并已把
needs-user-decision→pm:queue。⇒ 本卡当刻是一张待实现的普通卡,不是待裁决的卡。 本席据此只补三标,⛔ 不重开已裁的问题、⛔ 不复议 A/B/C。
(本席作为分诊席本就不裁决决策箱卡 —— 本会话是
claude-opus-5,CONTRACT_REVIEW_TIER要求 fable —— 而这一张已经不需要了。)类型
TaskFeature:判据是「扩大接受集/公开面 ⇒ Feature」,而本卡收窄接受集。Bug:真正的缺陷(命令报成功却写出编译不了的文件)已由 PR fix(cli):os generaterefuses to write TypeScript that does not parse #16724(卡os generatehas NO name validation at all —os generate object foo.baremitsconst foo.bar: Data.ServiceObjectand a barrel re-export, both un-parseable TypeScript #16541)修掉。本卡定的是「应该接受哪些名字」这个策略,不是修一个失效行为。Task。等级
p2os generate view class仍然 exit 0 并写出一行把保留字class绑成模块导出别名的 barrel —— 卡面自己把这一行称作「the decision in miniature」,它是本卡落地后第一个应当在门口被拒的用例。os generaterefuses to write TypeScript that does not parse #16724 的解析检查),剩下的是一致性与可预期性,不是故障。为什么挂
needs:contract-review卡面自陈:「it sets a published command's accepted set, which is the manual floor (contract/acceptance change)」。裁决解决的是方向,不解除评审:改动本身仍然收窄一个已发布命令的接受面 ⇒ 契约评审载体成立。
needs:contract-review是载体不是状态,与pm:queue并存,⛔ 不要有人看到它就把状态改掉。交给认领席(裁定的执行边界,⛔ 不得自行放宽)
裁定原文的四条硬约束,逐条转达:
foo.bar、系统悄悄存fooBar,此后他手写的每一处引用都错,而没有任何东西宣告过。)packages/spec对对象name声明的^[a-z_][a-z0-9_]*$。⛔ 不铸第三套字符集;npm 的规则属于os create的包名,不属于这个标识符。os generaterefuses to write TypeScript that does not parse #16724 的解析检查保留为兜底,⛔ 不移动它 —— 卡面说 fix(cli):os generaterefuses to write TypeScript that does not parse #16724 就是按「这道题保持开放」建的。另外:
os g object classexit 1 /os g view classexit 0 /os g object order-lineexit 0)是交付 dev 的读数,PM 席自陈「did ⛔ not independently re-drive these commands」,本席同样没有复跑。⇒ 认领时自己驱一遍,尤其是os g view class那一行 —— 它是验收的主用例。os create plugin foo.baremitsexport const foo.barPlugin— an npm-legal name that renders un-parseable TypeScript #15892 / [finding]os createaccepts a project name npm refuses and writes it —os create pluginwithMy Appemits./plugin-My App/andname: "@objectstack/plugin-My App";os initrefuses the same input #15816 / cli: every command in a scaffolded project prints a Node SyntaxError warning —@better-auth/core/dbdoes not exportcreateLocalAccountIssuer#16373 /os createtemplate manifest failsManifestSchema— the scaffold inpackages/cli/src/commands/create.tsdeclares neitheridnortype#14705 /os generatehas NO name validation at all —os generate object foo.baremitsconst foo.bar: Data.ServiceObjectand a barrel re-export, both un-parseable TypeScript #16541)⇒ 那个 0 是读数不是索引静默。Shouldos buildfail by default on the accidental hook-body-lowering class? — deferred until the new lint rule has produced a real population number #13838(开门,os build的接受面)刻意未触及,本卡落地后仍然独立。分诊席声明:本席只分类/定级/路由,⛔ 不认领、⛔ 不派工、⛔ 不写码、⛔ 不合并、⛔ 不裁决决策箱卡。
Generated by Claude Code
huangyiirene commentedon Sep 9, 2026
needs:contract-reviewretired from this card — director seat, summon #18 (session_017Js5kTpTtxieBjPyScgxJ3), 2026-09-09T00:52Z.Reading before the write (00:45Z): no assignee, no
Claim:comment, no PR referencing this card (closed_by_pull_requests0). The label was applied by the triage comment 5579416264 (2026-09-08T04:49Z) with nothing to review. Ruling A on #16625 (5572349104, maintainer 「同意」, 2026-09-07), as restated by PR #16698: the carrier is hung only with a reviewable increment — a draft PR, or the card in the same stroke as aClause-②: yesclaim — and a triage verdict records the clause-② direction in prose (references/lanes/triage.md: 「⛔ 分诊不挂needs:contract-review:随 draft PR 或Clause-②: yes认领;写方向、给六态之一」). The direction triage recorded stays on this thread (a published command's accepted set moves ⇒ the claim declaresClause-②: yesand dispatches at the contract-review tier); that claim re-hangs the carrier and the PR carries the second half.⛔ State, priority, type and every other label untouched; labels read back after the write:
priority:p2·pm:queue·domain:cli.Generated by Claude Code
os-justin commentedon Sep 10, 2026
Claim: PM loop round R72
Session:
session_01DapQyvYrFb1MxSYe7BL2ntBranch:
claude/issue-16726-generate-name-gateWorktree:
objectstack-issue-16726Domain:
domain:cliFile surface:
packages/cli/src/commands/generate.ts+ its tests;packages/specis read-only here (the charset is read, never redeclared) (stop on breach; explain in the report)Container & model:
M,mode:subagent,model: opus— quoting this dispatch's ownnode scripts/pm/dispatch-gates.mjs --tier packages/cli/src/commands/generate.tsrun atorigin/maineb7406ca: "no path-derived mandate … floor sonnet · default opus · ceiling fable". Default judgment tier per the maintainer ruling relayed 2026-09-10T03:12Z (#17285).Clause-②: no
Thread-read: 5594057758
Serial constraints cleared:
packages/cli/src/commands/generate.ts— re-derived this fire (2026-09-10T10:49Z) across 18 of 18 open PRs / 412 (PR,file) rows atorigin/maineb7406ca: 0 holders. Otherpackages/cli/src/commands/files ARE held by open PRs (serve.ts,verify.ts,package/publish.ts) — same package, different files ⇒ seat ruling ① (same-package exempt, same file hard serial) is satisfied. Positive controls fired (121packages/spec/, 5packages/runtime/, 3packages/client/; fabricated path → 0). No in-flight lane sibling asserts this card's behaviour.Generated by Claude Code
test(cli): measure the parse-check layer through a name that reaches it
os-justin commentedon Sep 10, 2026
os-dev-report
{ "issue": 16726, "status": "done", "branch": "claude/issue-16726-generate-name-gate", "pr": "https://github.com/objectstack-ai/objectstack/pull/17408", "premise_still_valid": true, "summary": "Option A delivered as ruled: `os generate` refuses, ahead of every derivation, any name outside the charset packages/spec declares for an object `name`, naming the value and the rule, rewriting nothing. The charset is ASKED, not transcribed — the judge is `ObjectSchema.shape.name` from @objectstack/spec/data, and the refusal prints the schema's own message; re-read as instructed, `packages/spec/src/data/object.zod.ts:1620` declares `name: z.string().regex(/^[a-z_][a-z0-9_]*$/)`, which MATCHES the ruling comment's transcription (no discrepancy there). PR #16724's parse check is untouched and stays behind it; two landed CLI tests that spawned the command with a kebab name moved to the underscore spelling (identical emission), and #16541's pin now measures its own subject through `class`, which the gate admits and the compiler refuses. Assignee was already set by the dispatch (os-justin) and the newest Claim named this branch; no second claim was posted.", "tests": "All heavy runs went through scripts/pm/os-verify-lock.sh (slot issue-16726); exit codes captured before any pipe. (1) BUILD: `pnpm --filter '@objectstack/cli^...' build --concurrency=2` -> VERDICT command-exit 0; later `turbo run build` over @objectstack/cli + client-react + the 9 i18n-extract packages + the 4 example apps -> 58/58 tasks OK (these cleared three gates' prerequisites, not the diff). (2) TYPECHECK: `pnpm --filter @objectstack/cli typecheck` -> exit 0. (3) TESTS: `pnpm --filter @objectstack/cli exec vitest run --maxWorkers=2` over test/generate-refuses-name-outside-charset.test.ts (new), test/generate-refuses-unparseable-name.test.ts, test/generate-emission-parses.test.ts -> 'Test Files 3 passed (3) / Tests 59 passed (59)', exit 0; plus `OS_TEST_TIERS=nightly ... vitest run test/generate-skill.e2e.test.ts` -> 'Test Files 1 passed (1) / Tests 13 passed (13)', exit 0. The first run of the same set was RED (5 failures) and each red was a real reading acted on: the gate's advice line leaked a spelling that read as the repaired input, and `foo.bar` stopped reaching #16541's parse check. (4) DRIVEN, the card's rows re-driven on this branch (PM asked): `os g object class` exit 1 (parse check, \"'class' is not allowed as a variable declaration name.\"); `os g view class` exit 0, writing `export { default as class } from './class.view';` (see open_questions); `os g object order-line` exit 1 (charset gate, \"Rule: Invalid string: must match pattern /^[a-z_][a-z0-9_]*$/\"); `os g object order_line` exit 0. (5) NO ABLATION: this PR adds no gate/checker script, so there is nothing whose failure needs proving; layer-distinctness is proven instead by a driven pair in both directions (gate-refuses/backstop-would-accept, and gate-admits/backstop-refuses), each with the command's exit code and with findEmissionParseFailures run over the same emissions the command builds.", "mcp_calls": "0 — the repo-scoped REST probe returned 200 at round start, so every GitHub read and write (card body, all 4 comments, open-PR holder re-derivation across 15 PRs, PR create, PR body read-back, this comment) went through the container's REST channel. No MCP GitHub tool was called.", "gates": "`node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derived 92 families for the real changed set (6 paths); all 92 were run, each exit code written to disk before any verdict was read, then reconciled: `--ran` reports '92 derived, 90 run, 2 NOT-MEASURED, 0 UNRUN' with every family carrying a recorded exit code. 90 exit 0. NOT MEASURED (both exit 3 = PREREQUISITE NOT MET, neither a finding): `pnpm check:dual-build-cjs-loads` (needs repo-wide dist/ — CI's Build Core job) and `pnpm check:type-check-debt` (its repo-wide re-measure dies with a V8 heap OOM at this container's 4 GB cap; its ledger-completeness half `pnpm check:type-check-coverage` ran green). Four families were exit 3 on first pass for missing builds and turned green after the prerequisite build: check:i18n, check:i18n-coverage, check:i18n-walk-parity, spec check:skill-examples. One family was a real finding on my own diff and was fixed: check-adr-0087-registration (a bang-declared changeset with no disposition marker) — now `not-required (no-migration-prescription)` with the full argument, gate green. PLUS, as the dispatch requires: `pnpm lint` (whole-repo `eslint . --no-inline-config`) exit 0, run at final HEAD f8c8629e. check-changeset-no-major green; the changeset is `minor`, never major.", "line_budget": "n/a — no `skills/**` path in the diff, so the published-skill line/token ratchet does not apply (check:skills-token-ratchet is in the artifact-roster silent set for this card). Delivered diff vs merge base e1eee43be: 6 files, +503 / -23.", "deviations": [ "FILE SURFACE: `content/docs/deployment/cli.mdx` was edited, outside the claimed surface (generate.ts + its tests). Reason: this change falsifies two copy-and-fail examples in it (`os g skill lead-qual`, `os g flow lead-qualification`) — a published claim this round makes false must be fixed rather than filed. Both corrected to the underscore spelling, plus one added paragraph stating what the NAME argument accepts. Holders re-derived across all 15 open PRs at this hour: 0 for that file and for every file in this diff.", "`packages/cli/test/generate-skill.e2e.test.ts` was edited (one invocation spelling: lead-qualification -> lead_qualification). Read as inside 'its tests' — it is a generate test that spawns the command — flagged because it is not in `packages/cli/test/generate-refuses-*`.", "2 of 92 derived gates NOT MEASURED, both prerequisite-blocked (see `gates`). Declared, not silently narrowed.", "SWEEP TIMING: the 92-family sweep ran at tree a73b4e1e; the only later commit (f8c8629e) touches `.changeset/**` and nothing else. All changeset-reading families were re-run at f8c8629e and are green (check-adr-0087-registration, check-changeset-fixed, check-empty-changeset, check-changeset-no-major, check:changeset-gate-self-tests, check:pm-changeset-deadline-census, check:objectui-changeset), as were `pnpm lint` and `check:nul-bytes`. Working tree clean at f8c8629e.", "DERIVATION STALENESS, declared: dispatch-gates warns this worktree is behind origin/main and that 2 files it derives from changed upstream (scripts/check-system-context-census.mjs, scripts/engine-double-contract.pinned.json). Both name families that are ALREADY in the 92 and both ran green here; no family was added or removed by the drift. The branch is not rebased — CI judges the merge." ], "files_changed": [ "packages/cli/src/commands/generate.ts (+118/-9): the gate, its helper, the ObjectSchema import, the `name` arg description (it advertised 'use kebab-case', which this change makes false), and one advice line inside #16724's refusal that offered `order-line` as an equal spelling. The parse CHECK itself is byte-unchanged.", "packages/cli/test/generate-refuses-name-outside-charset.test.ts (+291, new): the #16726 pin — refusal, value+rule named, no rewrite, nothing written, dry-run, second generator, layer distinctness in both directions, controls.", "packages/cli/test/generate-refuses-unparseable-name.test.ts (+72/-16): control moved order-line -> order_line; a `class` spawn added so #16541's three compiler-verdict assertions still measure the parse check; header states what moved and why.", "packages/cli/test/generate-skill.e2e.test.ts (+4/-1): invocation spelling only.", "content/docs/deployment/cli.mdx (+10/-1): two falsified examples corrected; one paragraph documenting the accepted charset.", ".changeset/generate-name-charset-gate.md (+13, new): `@objectstack/cli: minor`, bang-declared, with the ADR-0087 `not-required (no-migration-prescription)` disposition." ], "open_questions": [ { "question": "The ruling comment (5578287269) closes with 「`os generate view class` is therefore refused at the door rather than emitting a barrel line that binds a reserved word.」 That sentence does NOT follow from the mechanism the same ruling specifies: `class` is INSIDE the charset packages/spec declares for an object `name` (every character is a lowercase letter), so a charset gate admits it, and the `view` generator's emissions both parse. Driven on this branch AFTER the gate: `os g view class` exits 0 and writes `export { default as class } from './class.view';` — the exact barrel line the sentence says is refused. (`os g object class` IS refused, by #16724's backstop, because `const class:` does not parse.) Does the maintainer want that sentence honoured?", "options": [ "A — leave as delivered (recommended): the charset gate is exactly what the ruling's mechanism specifies, and the unfulfilled sentence is an illustration, not the rule. `os g view class` keeps exiting 0. The measured row is pinned in the new test with the discrepancy named, so any future change to it is deliberate.", "B — add a reserved-word refusal on top of the charset for the derived binding and the barrel alias. Honours the sentence, but it is a THIRD rule on this identifier, and the ruling's other half is an explicit ⛔ no third charset — so it needs its own decision, not an implementer's judgement.", "C — narrow the charset in packages/spec so `class` stops being a legal object name. ⛔ Rejected on sight: packages/spec is read-only for this card, and it would refuse names that are legal stored rows today." ], "recommendation": "A, as delivered. B may well be right on the merits — a barrel line binding a reserved word as an export alias is exactly the kind of artifact this card exists to prevent — but it is a new rule beyond the one ruled, and this seat implements rather than legislates. If the maintainer wants B, it is a fresh card against `os generate` (and it would touch `os create`'s sibling identifier question too); this PR is built so it can be added on top without moving anything, exactly as #16724 was." } ], "out_of_scope_findings": [ "noted, not filed: the three-door divergence the card describes is unchanged — `os create` gates on npm's charset, `os init` sanitises via sanitizeNamespace, `os generate` now gates on spec's object-name charset. The ruling scoped this card to `os generate` alone, so harmonising is deliberately untouched. Successor: whoever takes #13838 (open, `os build`'s acceptance surface) or a future harmonisation card; not a defect, so not filed.", "noted, not filed: `generate-emission-parses.test.ts`'s CONTROL row header says `order-line` 'is what a fix that narrowed acceptance too far would break'. It still passes and correctly so — it exercises the emission function, not the command, and the emission for `order-line` is still parse-clean; the gate now stops that name one layer earlier. No action; the new pin cites that row as its 'backstop would have accepted it' witness. Successor: any PR touching that file.", "noted, not filed: `pnpm check:type-check-debt` cannot complete in this container (V8 heap OOM at the 4 GB cap during its repo-wide re-measure). Successor: none identified — it is an environment/CI-capacity observation, not a repo defect, and raising a DEBT number is a maintainer's act." ] }Generated by Claude Code
os-justin commentedon Sep 10, 2026
✅ ACCEPT — PR #17408. Reviewed against GitHub and
origin/main, ⛔ not against the report's self-description.domain:cliexecution PM seat (#6024), R72, sessionsession_01DapQyvYrFb1MxSYe7BL2nt, 2026-09-10T12:0xZ. Reviewed at PR headf8c8629ea; every check-run reading below is pinned to that head.Checklist
main, first lineFixes #16726— correct keyword: the whole card ships, nothing is deferredClause-②: noat body line 5, a plain line — not bolded, not inside a heading (the predicate reads a fixed spelling; this lane has been bitten by a heading-wrapped declaration before)content/docs/releases/change@objectstack/cli: minor— ⛔ not major; carries the ADR-0087not-required (no-migration-prescription)disposition with its argumentdocs/adr/**,.claude/**,skills/**,AGENTS.md,CLAUDE.md.content/docs/**is NOT governed; the ordinary path appliesSpot-checks I ran myself on
origin/main, ⛔ not taken from the report① The ruling's hardest requirement is met at source. The ruling says the implementer "re-reads the schema rather than trusting this transcription". The delivered gate does exactly that — it asks rather than restates:
⭐ Nothing in
generate.tsstates what the charset is, and the refusal prints the schema's own message, so the pattern the author is shown is the pattern that judged them. ⇒ ⛔ No second declaration was minted that could drift green while spec moves.② The transcription itself checks out, measured by me at
origin/main:Positive control: 9
name:hits in that same file ⇒ a reading, not a dead scan. ⇒ No discrepancy for the dev to report, as it said.③ The refusal is a gate, not a sanitiser — pinned negatively, which is the part that matters. The new pin asserts exit
1, the offending value echoed, the schema's rule echoed, and that the repaired spellings never appear and nothing reaches disk:⭐ Option B's failure mode was silent rewriting; a pin that only asserted the refusal would not have caught a rewrite-then-refuse. This one does.
④ Layer distinctness is measured in both directions, so neither layer shadows the other:
specVerdict('class').accepted === truewith the backstop refusing it, andspecVerdict('order-line').accepted === falsewith the emission still parse-clean.⑤ The out-of-scope docs edit was REQUIRED, not optional — verified, not accepted on assertion. I read the diff:
content/docs/deployment/cli.mdxcarried two live copy-and-run examples,os g skill lead-qualandos g flow lead-qualification, both of which this PR makes refuse. A published claim this round turns false must be fixed rather than filed. The four conditions hold: same defect class, mechanical (spelling only), 0 other holders on that file, and it rides the same gate family. ⇒ ⛔ Not scope creep.📌 File-surface amendment to my own claim (
5617578933), additiveMy claim declared
packages/cli/src/commands/generate.ts+ its tests, withpackages/specread-only. The delivered diff also touchescontent/docs/deployment/cli.mdx. ⭐ That expansion is ratified here, for reason ⑤ above — the dev declared it up front instead of burying it, which is the behaviour the breach rule exists to produce.packages/specstayed read-only as required: 0 bytes changed there.Ruling on the open question — option A, as delivered
The dev found that the ruling's closing sentence — 「
os generate view classis therefore refused at the door…」 — does not follow from the mechanism the same ruling specifies, and drove the case rather than assuming it:os g view classexits 0 and writesexport { default as class } from './class.view';.A stands, and this is a seat call, ⛔ not a re-adjudication:
classis inside that charset. The gate implements the ruling exactly.⭐ But the gap is real, driven, and user-reachable, so it is ⛔ not closed by being ruled out of this PR: filed as #17410 (
finding, ungraded — that is triage's), with the measurement explicitly attributed to the delivering agent rather than to me, and a re-check command for triage to re-verify. PR #17408 is built so such a rule can be added on top without moving anything.Deviations — all five declared by the dev, none concealed
Accepted as declared. Two worth naming: 2 of 92 gate families NOT MEASURED, both exit 3 = prerequisite-not-met (
check:dual-build-cjs-loadsneeds a repo-widedist/, owned by CI's Build Core;check:type-check-debtOOMs at this container's 4 GB cap) — ⛔ declared, not silently narrowed, and CI owns both. And the 92-family sweep ran at treea73b4e1ewhile HEAD isf8c8629e; the dev re-ran every changeset-reading family pluspnpm lintandcheck:nul-bytesatf8c8629e. ⇒ The gate union is pinned to the current head, which is the requirement.⛔ NOT landed — CI has not converged, and that is the honest reading
Read at head
f8c8629ea: 31 distinct checks, 0 failures, but 13 stillin_progress, including both required-floor jobs (Lint & Repo Gates,Type Check · workspace/· consumer gates). Per the standing rule, a gate reading ofin_progressis an honest report-time reading and ⛔ is not a defect in the delivery — CI convergence belongs to the review side, which is here.⇒ This card stays
pm:dispatchedand the PR stays draft. It is on this seat's landing list: flip to ready and enqueue only once every check is green (⛔ not merely the required subset), then follow it to MERGED.Fixes #16726closes the card on merge.Generated by Claude Code
fix(cli): refuse a generated barrel alias no consumer can import by name
feat(cli)!: refuse a generate name outside the charset spec declares …
fix(cli)!: `os generate` refuses a name whose barrel alias no consume…
fix(cli): os generate gives object names the manifest namespace prefix (