Repository navigation
[finding] fresh starters carry a latent field-no-consumers warning that the first os generate wakes (create-objectstack blank note.body; os init -t app item description / status) #21370
Description
Activity
objectstack-fleet commented
on Oct 2, 2026 ContributorAuthorMore actionsTriage: first grade —
bug·priority:p2·domain:cli·area:devpath·pm:queue;findingremoved. The starters place their own fields; the rule is unchangedTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-02T09:03Z. ⛔ Not a claim, ⛔ not a dispatch.Why p2. Every new project carries the latent warning, and the first correct scaffold wakes it. So the first thing a new author or AI agent sees is a warning about a field it did not write. That teaches it to ignore warnings, or to "fix" the template. Nothing breaks, because
os validatestill exits 0.Routing.
packages/create-objectstackandpackages/cli(init.ts) aredomain:cli.Ruling on the fork: give each starter field a consumer, through the platform's own placement surface.
- Each starter object places the fields it declares in a keyed field group:
fieldGroupson the object andgroupon each field (ADR-0085 §5). The rule already credits this as display, because it is what the form, detail and drawer surfaces render (packages/lint/src/validate-field-consumers.ts, "The synthesized layout"). - This applies to the blank template's
note.object.tsand to theappentry ofTEMPLATESininit.ts. The title field is already exempt through thenameFieldladder. - ⛔ No starter carve-out in the rule. A starter is a stack like any other. A carve-out would keep silencing the warning on apps that grew from the starter, and it adds a special case to a rule whose severity is fixed at
warning. - ⛔ The fields are not dropped. The starter exists to show a working object. A field group is also the step that a real author's next field needs.
Pins: each starter, after
os g dashboard,os g viewandos g flow, reports 0field-no-consumersfindings under bothos validateandos lint. A starter field with itsgroupremoved warns again; that is the control.Serial: after PR #21369 (#21325) lands. The fix removes that PR's one
STARTER_LATENT_FINDINGSentry, so the ledger shrinks to empty. If this card lands first, PR #21369 mergesmainand drops the entry.
Generated by Claude Code
- Each starter object places the fields it declares in a keyed field group:
- 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 workingpriority:p2Medium: important, M3Medium: important, M3and removed
on Oct 2, 2026 objectstack-fleet commented
on Oct 2, 2026 ContributorAuthorMore actionsClaim: PM loop round 1 of the
domain:cliseat's sessionsession_01VvcEokUG1tvVxkceYfR5XB(batch3):priority:p2, to triage's ruling5948728426. Landed asFixes #21370.
Session:session_01VvcEokUG1tvVxkceYfR5XB
Account:huangyiirene
Branch:claude/issue-21370-starter-field-groups
Worktree:objectstack-issue-21370
Domain:domain:cli
Seat:domain:cli#1
File surface, derived atorigin/main69a12a0952:- The ruling, as built: each starter object places the fields it declares in a keyed field group:
fieldGroupson the object andgroupon each field (ADR-0085 §5). The rule already credits this as display (packages/lint/src/validate-field-consumers.ts, "The synthesized layout"). The title field stays exempt through thenameFieldladder.- ⛔ No starter carve-out in the rule.
- ⛔ The fields are not dropped.
- Expected files:
packages/create-objectstack/src/templates/blank/src/objects/note.object.ts;- the
appentry ofTEMPLATESinpackages/cli/src/commands/init.ts; - any other starter template that carries the same latent warning, if the dev measures one, reported with the measurement;
packages/cli/test/generate-scaffold-gates.e2e.test.ts: its oneSTARTER_LATENT_FINDINGSentry is deleted, so the ledger shrinks to empty, as triage's serial note directs (PR fix(cli)!: os generate binds declared metadata (--object, --flow), never the item name, and every scaffold passes validate, build and lint #21369 has landed as11905a4f8b);- pins beside them.
- Pins:
- Each starter, after
os g dashboard,os g viewandos g flow, reports 0field-no-consumersfindings under bothos validateandos lint, measured red before the fix. - A starter field with its
groupremoved warns again (the control).
- Each starter, after
- Changeset: one
.changeset/21370-*.mdforcreate-objectstackand@objectstack/cli(theinittemplate), at the level the real diff takes. - Stop clause: stop if the rule does not credit a field group as display for a starter object as triage read it, or if the fix needs an edit in
packages/lint.
(stop on a breach outside these; explain in the report)
Container & model:S,mode:subagent,model: opus(dispatch-gates --tier: no path-derived mandate, floor sonnet · default opus · ceiling fable; the default).
Clause-②: no. The starter output gains placement keys the schema already accepts; no accept set or public member moves. The dev re-reads this against the real diff and states the measured arm on the PR's line 2.
Thread-read: 5948728426
Serial constraints cleared:PR #21369 (#21325), triage's named predecessor, landed as 11905a4f8b. Of the 7 open PRs (all file lists read in this act), none touches the create-objectstack templates, init.ts, generate-scaffold-gates.e2e.test.ts or validate-field-consumers.ts. No in-flight claim names them (#21321: runtime and cloud-connection; #21360: api-client, cloud-config and the environments commands).
- The ruling, as built: each starter object places the fields it declares in a keyed field group:
objectstack-fleet commented
on Oct 2, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 21370,
"status": "done",
"branch": "claude/issue-21370-starter-field-groups",
"pr": "#21407",
"session": "session_01VvcEokUG1tvVxkceYfR5XB",
"premise_still_valid": true,
"summary": "Per triage ruling 5948728426, the blank starter's note (create-objectstack) and the os init app template's item (init.ts TEMPLATES.app) now declare fieldGroups [{ key: 'details', label: 'Details' }] and put group: 'details' on every field. Each object has an author-facing comment explaining the group and linking /docs/ui/field-grouping-and-order. There is no rule carve-out, no field dropped and no packages/lint edit. The plugin template was measured clean (its only field is the title), and empty declares no object, so neither changes. The generate-scaffold-gates STARTER_LATENT_FINDINGS ledger is emptied, with a floor test so its describe block is not empty (vitest 4 fails an empty suite). Added a per-PR in-process pin and a nightly real-command pin. Patch changeset for both packages, Clause-②: no.",
"tests": "All at d94a0d6. Pre-fix red: the in-process pin on the 69a12a0 templates gave 8 failed / 6 passed (blank x3 and app x3 waker legs, plus the 2 app controls). Post-fix: test/starter-field-consumers.test.ts 14/14. Nightly (OS_TEST_TIERS=nightly): starter-field-consumers.e2e 15/15; generate-scaffold-gates.e2e 37/37 with the empty ledger. cli unit project in 3 shards: 82+82+83 files, 1315+1079+1095 passed, 14 skipped. One file, published-subpath-console.pin, refused with 'packages/cli is not built' (a prerequisite, not a red); after a cli build it passed 14/14. Integration create-objectstack-stack-reach 4/4. cli typecheck exit 0 (the test layer holds its ledgered 28 errors in 3 files; --listFiles includes all 3 touched tests with 0 errors in them). create-objectstack typecheck exit 0; its test suite: 16 files, 249 tests pass. Gates: 68/68 exit 0, and dispatch-gates --ran reads '68 derived, 68 run, 0 NOT-MEASURED, 0 UNRUN'. Lint: proven narrowing, 5 files / 0 errors / 0 warnings, no type-aware config (see gates). Ablations: see the ablations field; each ran through scripts/ablation-replace.mjs, restored blob == HEAD, dist-mediated legs rebuilt and preflighted.",
"mcp_calls": "0",
"api_writes": "3 REST writes, each through the fleet-write relay (one POST /repos/objectstack-ai/objectstack/dispatches each; the board workflow ran it as objectstack-fleet[bot]): (1) pr_create, POST /repos/objectstack-ai/objectstack/pulls (#21407, draft, body read back byte-identical, 10230 bytes); (2) label-write assign, POST /repos//issues/21407/assignees ['huangyiirene'], read back as matching; (3) this os-dev-report comment, POST /repos//issues/21370/comments. Also git push x5 (branch marker + 4 pushes of commits), which is not REST. Reads: REST GETs of the card, its comments, the PR and the check-runs.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: none (承接者:无) · noted, not filed. A bareos init -t appproject'sos validateprints one plain-string warning, 'No apps or plugins defined — this stack may not do much'. It is not a rule finding and reads the same before and after. Not a defect class (a, b or c). In PR Acceptance notes."
],
"templates": {
"npm create objectstack (blank note)": {
"before": "field-no-consumers: bare 0; after os g dashboard 1 (objects[0].fields.body); after os g object + os g view 1 (body); after os g object + os g flow --object 1 (body). Identical under os validate and os lint, exit 0. One project per waker, at 69a12a0.",
"after": "0 under os validate and os lint after os g dashboard + object + view + flow (nightly e2e at d94a0d6). Control: with every group line stripped, both report field-no-consumers on body only."
},
"os init -t app (item)": {
"before": "bare 0; after dashboard / view / flow: 2 each (description, status), validate and lint, exit 0.",
"after": "0 under both. Control: groups stripped, so description and status warn (title name exempt)."
},
"os init -t plugin": {
"before": "0 in every leg (its only fieldnameis the title, exempt).",
"after": "0, unchanged template."
},
"os init -t empty": {
"before": "no object declared (srcFiles {}), nothing to judge; not run through the commands.",
"after": "unchanged."
}
},
"ledger": {
"before": "STARTER_LATENT_FINDINGS = { 'field-no-consumers@objects[0].fields.body': … } (1 entry)",
"after": "STARTER_LATENT_FINDINGS = {} (0 entries). A floor test pins it empty (it adds no entry); both entry-honesty it.each blocks kept."
},
"repro": "Script: scratchpad issue-21370/repro.sh. Starters: on-ramp bin, plus os init -t app / -t plugin under packages/cli/node_modules. Per waker: a fresh project, thenos validate --jsonandos lint --jsonthrough bin/run-dev.js, counting rule == field-no-consumers. Reproduced the card's three claims exactly: blank body, and app description + status. Waking needed no view/flow on the starter's own object (view and flow were bound to a separate gate_target object).",
"gates": "dispatch-gates --commands at d94a0d6: 68 commands, all exit 0 (exit captured before any pipe), recorded as 'CMD :: exit N'. dispatch-gates --ran: '68 derived, 68 run, 0 NOT-MEASURED, 0 UNRUN', exit 0. On an earlier pass at 72bf6e9: check:cross-package-test-inputs was red because the unit pin's header named packages/lint's rule path in prose, which the gate reads as a read; reworded in d94a0d6, then green. check:dual-build-cjs-loads and check:i18n-coverage exited 3 (PREREQUISITE NOT MET) untilturbo run build --filter='!@objectstack/docs'(71/72 cached), then green; both green in the final union. pnpm lint: proven narrowing, not a full run. (1) Population from eslint.config.mjs:eslint . --no-inline-configminus the global NEVER_LINTED set, which contains none of the 5 touched TS files. (2)eslint --no-inline-config --format jsonreturned 5 file results, 0 errors, 0 warnings, none of them 'file ignored'. (3) The config enables no type-aware linting (no parserOptions.project or projectService) and no cross-file rule plugin; its only reads are two baseline JSONs this diff does not touch, so no untouched file's verdict can move. HEAD at lint d94a0d6.",
"ablations": [
"A1 (unit pin), blank body group replaced by a comment: 3 failed / 11 passed, exactly the 3 blank waker legs. Restored blob == HEAD.",
"A2 (unit pin), app description group replaced: 4 failed / 10 passed, the 3 app waker legs plus the status control. Restored blob == HEAD.",
"A3 (unit pin), lint guardif (section.key === undefined) continue;deleted, lint rebuilt. Preflight: pristine marker present; mutated --absent --source-marker passed. Result: 3 failed / 11 passed, exactly the 3 control legs. Restore leg: rebuilt, marker 'section.key === void 0' present again, blob == HEAD.",
"E (nightly pin), blank body and app description groups replaced. create-objectstack and cli rebuilt; dist group counts 2→1 and 3→2, plant markers 1/1. Result: 4 failed / 11 passed, exactly the blank + app zero legs under validate and lint. Restore: rebuilt, counts 2/3, markers 0/0, blob == HEAD.",
"EC (nightly control), the A3 guard deletion: 15/15 green, so it did NOT red. On reading this is expected: the e2e control strips every group, title included, so deriveFieldGroupLayout returns null and there is no untitled bucket to credit. That class is held by the unit control (A3).",
"EC2 (nightly control), findings stop carrying the field-no-consumers id (plant '-ablated'; lint rebuilt; preflight present in 4 built files): 4 failed / 11 passed, exactly the 4 control legs. Restore: rebuilt, marker absent, tree clean, blob == HEAD.",
"Null first attempts, rerun as above. The first A1/A2 were refused by ablation-replace before the suite ran (the replacement was a substring of the anchor, so its count could not rise); rerun in plant mode. The first A3 preflight refused its tree reading (it needed --source-marker for the undefined vs void 0 spelling); the suite did not run, and it was rerun."
],
"files_changed": [
"packages/create-objectstack/src/templates/blank/src/objects/note.object.ts",
"packages/cli/src/commands/init.ts (TEMPLATES.app item only)",
"packages/cli/test/starter-field-consumers.test.ts (new, per-PR unit pin)",
"packages/cli/test/starter-field-consumers.e2e.test.ts (new, nightly pin)",
"packages/cli/test/generate-scaffold-gates.e2e.test.ts (ledger emptied, header, floor test)",
".changeset/21370-starter-field-groups.md (create-objectstack patch, @objectstack/cli patch)"
],
"deviations": [
"A stray file /e2e-new.pid (5 bytes, content '6914', a PID) was written to / by a mis-scoped shell variable: the&covered the wholecd && SP=… && (…)chain, so $SP was empty in the foreground echo. Removing it was blocked by the harness safety check on a removal at /, and that was not worked around. The operator needs to delete it: rm /e2e-new.pid.",
"Two e2e pins plus a unit pin, where the dispatch said 'pins beside them'. The real-command pin is nightly (.e2e, about 25 cold starts); per-PR protection is the in-process pin, following the generate-scaffold-validates / -gates split.",
"generate-scaffold-gates.e2e gained a floor test. Without it the emptied ledger leaves a describe block with zero tests, which vitest 4.1.11 fails as 'No test found in suite'. This is a test, not a ledger entry.",
"Lint was run as a proven narrowing, not a fullpnpm lint(three pieces of evidence in gates).",
"The PR footer uses the AGENTS.md session-URL form, and commit trailers are the dispatched model-free pair, not the harness reminder's forms (per os-dev attribution precedence).",
"Not merged with main: origin/main moved 7 commits to 1d0600b, and none touches a file this PR reads or changes. CI tests the merge ref."
],
"checks_after_push": "Read once after the PR opened, without waiting: d94a0d6 check-runs total 34, 9 success, 4 skipped, 21 in_progress, 0 failing. Convergence belongs to PM.",
"labels": "None written beyond the PR assignee. The read-back shows documentation, size/l, tests, tooling, applied by the labeler bots, not by this seat."
}
Generated by Claude Code
objectstack-fleet commented
on Oct 2, 2026 ContributorAuthorMore actionsACCEPT: PR #21407 at
d94a0d61(the starter objects place their fields in a field group, so a fresh project reports nofield-no-consumers).Fixes #21370; landing through the queuedomain:cliseat ·session_01VvcEokUG1tvVxkceYfR5XB· 2026-10-02T13:00Z- Contract faces: none of the three is hit.
Clause-②: no: starter output gains placement keys the schema already accepts, and no accept set or public member moves. So the seat self-reviews, and no isolated review is owed. - The ruling, as built (
5948728426):- The blank starter's
noteand theos init -t appitem each declarefieldGroups: [{ key: 'details', label: 'Details' }]and setgroup: 'details'on every field. - There is no rule carve-out, no field is dropped, and
packages/lintis untouched. - The
plugintemplate (its one field is the exempt title) and theemptytemplate (no object) were measured clean and are unchanged.
- The blank starter's
- The ledger:
STARTER_LATENT_FINDINGSingenerate-scaffold-gates.e2e.test.tsshrinks to empty. A floor test keeps the block non-empty under vitest 4; it is a test, not an entry. - Pins:
- Per PR:
starter-field-consumers.test.ts, 14 cases, red before the fix (8 failed). - Nightly:
starter-field-consumers.e2e.test.ts(15 cases), andgenerate-scaffold-gates.e2e37 / 37 with the empty ledger. - Controls: a starter with its groups stripped warns again.
- Ablations: they went red as predicted, and each restore is proven by blob == HEAD.
- Per PR:
- Seat verification:
- Checks on
d94a0d61, collapsed latest-per-name: 37 names, 33success, 4 skipped, 0 red. - The net diff is 6 files, +697 / −17.
check-governed-merges --pr 21407reads not governed.mergeable_statereadsclean.Fixes #21370is the only closing keyword. - The changeset and both template comments were read sentence by sentence. The linked page
content/docs/ui/field-grouping-and-order.mdxexists onmain.
- Checks on
- Out-of-scope note: on a bare
os init -t appproject,os validateprints the plain advisory "No apps or plugins defined", unchanged by this PR. It is not a rule finding and not a defect class; Acceptance notes. - Landing: not governed, so
pr_readyandautomerge_enablefollow in this act.
Generated by Claude Code
- Contract faces: none of the three is hit.
objectstack-fleet commented
on Oct 2, 2026 ContributorAuthorMore actionsLanded: PR #21407 →
fa7b565212(the starter objects place their fields in a keyed field group).Fixes #21370: the card is completedomain:cliseat ·session_01VvcEokUG1tvVxkceYfR5XB· 2026-10-02T13:29Z- Landing reading:
fa7b565212is onorigin/mainas a single-parent squash: 6 files, +697 / −17. Itsgit patch-id --stableis709fce4fda00…, equal to the reviewed net diff atd94a0d61. - The card: closed as completed by this merge.
pm:dispatchedis removed in the same act as this note. - The ledger:
STARTER_LATENT_FINDINGSis now empty onmain. A future starter latent warning turns the nightly pin red instead of hiding in the ledger.
Generated by Claude Code
- Landing reading:
Source: the os-dev report on #21325 (
5947877777, PR #21369),out_of_scope_findings[0], class a. Filed by thedomain:cliseat,session_01VvcEokUG1tvVxkceYfR5XB.What happens (measured, public door)
npm create objectstack tasks-app(the in-repo on-ramp bin), thenos g dashboard sales, thenos validate: exit 0 with one warning,field-no-consumersatobjects[0].fields.body. That is the starter's owntasks_app_note.body, which the author never touched.view,flow,action,apporskillscaffold.os init -t app, thenos g dashboard probe, thenos lint: the same warning onmy_app_item.descriptionandmy_app_item.status.A bare starter with no
os greports 0 findings. The warning appears only once something consumes the stack.Why
The rule stays silent while a stack holds no consumer root. Each fresh template therefore carries a latent warning, and the author's first consumer wakes it. The first thing a new author or an AI agent sees after its first correct scaffold is a warning about a field it did not write. That teaches it to ignore warnings, or to "fix" the template.
Where the fix lands
packages/create-objectstack/src/templates/blank/src/objects/note.object.ts;appentry ofTEMPLATESinpackages/cli/src/commands/init.ts.The direction is triage's call: give each template field a consumer, drop the fields, or change how the rule treats a starter.
Pin hand-off
PR #21369 (#21325) adds
packages/cli/test/generate-scaffold-gates.e2e.test.ts. When it lands, that test holds this finding in a one-entry, shrink-onlySTARTER_LATENT_FINDINGSledger. The entry must sit on a starter-declared object and must still fire, so the pin goes red when the template is fixed, and the fix removes the entry. Until #21369 lands, the ledger exists only on that PR.Dedupe words: field-no-consumers starter template; note body latent warning; os init app template item description status; fresh scaffold consumer root.
Generated by Claude Code