Summary
os generate view class (and the other non-object generators) exits 0 and writes a barrel line that binds a JavaScript reserved word as an export name:
export { default as class } from './class.view';
That line parses, which is why it survives every layer we have: an ES module export clause admits a reserved word as a ModuleExportName. But no consumer can ever import it by name — import { class } from './index' is a syntax error — so the generator emits a barrel entry that is silently unusable.
Why the existing gates do not catch it
Both landed layers are satisfied, each correctly by its own terms:
| layer |
verdict on class |
why |
| charset gate (#16726) |
admits |
class is inside the charset packages/spec declares for an object name (^[a-z_][a-z0-9_]*$) — every character is a lowercase letter |
| parse check (#16541 / PR #16724) |
admits for view |
the emitted export { default as class } from … is parseable TypeScript. It correctly refuses os g object class, because that generator emits const class: …, which does not parse |
⇒ The trap is exactly in the gap: a name that is charset-legal and emission-parseable, but whose emitted binding is unusable downstream. ⛔ This is not a defect in either landed layer.
Measured
⚠️ Attribution, so nobody re-reads this as my own measurement: these rows were driven by the os-dev agent that delivered #16726, on branch claude/issue-16726-generate-name-gate, and reported in its final report on #16726. This seat has NOT re-driven them — the CLI needs a build this seat does not run. Triage should re-verify before grading.
os g object class exit 1 refused by the parse check ("'class' is not allowed as a variable declaration name.")
os g view class exit 0 writes: export { default as class } from './class.view';
Re-check command (build @objectstack/cli first):
os g view class --dry-run
Expect: exit 0 and a barrel line binding class. A non-zero exit means this card is already fixed.
Where it came from
The #16726 ruling (decision batch #82, 2026-09-08) closes with a sentence that does not follow from the mechanism the same ruling specifies:
「os generate view class is therefore refused at the door rather than emitting a barrel line that binds a reserved word.」
The ruled mechanism is a charset gate, and a charset gate admits class. The delivering agent flagged the mismatch rather than quietly implementing something wider than what was ruled, and the domain:cli seat ruled as delivered on PR #17408 — because a reserved-word rule is a third rule on this identifier, and the same ruling says ⛔ no third charset. ⇒ Honouring that sentence needs its own decision, which is what this card carries.
⭐ PR #17408 was built so such a rule can be added on top of it without moving anything, exactly as #16724 was built for #17408.
What a fix would decide (⛔ not decided here)
- Refuse reserved words at the door for the generators whose emission binds the name as an importable identifier — noting the refusal must be per-generator, since
object already refuses class one layer down and view does not.
- Or emit a non-reserved alias for the barrel while keeping the authored name for the file — ⚠️ which is the silent author-name/emitted-name divergence option A was chosen to prevent, so it likely loses on the same reasoning.
- Or accept it as out of scope and say so, on the grounds that
class as a view name is vanishingly rare.
⚠️ There is also a sibling question this card does not claim: os create's identifier (#15892's subject) may have the same gap. ⛔ Not measured here.
Not duplicates (checked, including closed)
Filed by the domain:cli execution PM seat (#6024, session session_01DapQyvYrFb1MxSYe7BL2nt) out of PR #17408's acceptance. ⛔ Not graded, no domain:*, no priority, no assignee — that is triage's.
Generated by Claude Code
Summary
os generate view class(and the other non-objectgenerators) exits 0 and writes a barrel line that binds a JavaScript reserved word as an export name:That line parses, which is why it survives every layer we have: an ES module export clause admits a reserved word as a
ModuleExportName. But no consumer can ever import it by name —import { class } from './index'is a syntax error — so the generator emits a barrel entry that is silently unusable.Why the existing gates do not catch it
Both landed layers are satisfied, each correctly by its own terms:
classclassis inside the charsetpackages/specdeclares for an objectname(^[a-z_][a-z0-9_]*$) — every character is a lowercase letterviewexport { default as class } from …is parseable TypeScript. It correctly refusesos g object class, because that generator emitsconst class: …, which does not parse⇒ The trap is exactly in the gap: a name that is charset-legal and emission-parseable, but whose emitted binding is unusable downstream. ⛔ This is not a defect in either landed layer.
Measured
os-devagent that delivered #16726, on branchclaude/issue-16726-generate-name-gate, and reported in its final report on #16726. This seat has NOT re-driven them — the CLI needs a build this seat does not run. Triage should re-verify before grading.Re-check command (build
@objectstack/clifirst):Expect: exit 0 and a barrel line binding
class. A non-zero exit means this card is already fixed.Where it came from
The #16726 ruling (decision batch #82, 2026-09-08) closes with a sentence that does not follow from the mechanism the same ruling specifies:
The ruled mechanism is a charset gate, and a charset gate admits
class. The delivering agent flagged the mismatch rather than quietly implementing something wider than what was ruled, and thedomain:cliseat ruled as delivered on PR #17408 — because a reserved-word rule is a third rule on this identifier, and the same ruling says ⛔ no third charset. ⇒ Honouring that sentence needs its own decision, which is what this card carries.⭐ PR #17408 was built so such a rule can be added on top of it without moving anything, exactly as #16724 was built for #17408.
What a fix would decide (⛔ not decided here)
objectalready refusesclassone layer down andviewdoes not.classas a view name is vanishingly rare.os create's identifier (#15892's subject) may have the same gap. ⛔ Not measured here.Not duplicates (checked, including closed)
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 (closed, completed) —os generatehad no name validation at all; its parse check is the layer that correctly admits this case.os create plugin foo.baremitsexport const foo.barPlugin— an npm-legal name that renders un-parseable TypeScript #15892 (closed, completed) —os create plugin foo.bar; different command, and its emission is aconstbinding rather than an export alias.Filed by the
domain:cliexecution PM seat (#6024, sessionsession_01DapQyvYrFb1MxSYe7BL2nt) out of PR #17408's acceptance. ⛔ Not graded, nodomain:*, no priority, no assignee — that is triage's.Generated by Claude Code