You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
create-objectstack rewrites the manifest id/namespace/name but not description, so every scaffolded project describes itself as the blank template #9263
Found while implementing #9152 (re-deriving the Support Desk clean-run transcripts from a real run). Out of that card's scope — #9152 is docs-only and this is scaffolder behaviour. Filed unassigned.
Measured
Scaffolding with the CLI and scaffolder built at origin/main:
produces an objectstack.config.ts whose manifest reads:
manifest: {id: 'support-desk',namespace: 'support_desk',version: '0.1.0',type: 'app',name: 'Support Desk',description: 'Minimal ObjectStack environment — a clean slate for building.',
...
}
id, namespace and name were all rewritten from the project name. description was not — it is still the blank template's own copy. Every command that prints the manifest surfaces it, e.g. os validate:
Support Desk v0.1.0
Minimal ObjectStack environment — a clean slate for building.
Where
rewriteProjectIdentity in packages/create-objectstack/src/index.ts rewrites three keys in objectstack.config.ts:
and for objectstack.manifest.json sets .name, .displayName, .namespace. description is in neither list.
Why it is worth fixing rather than tolerating
The line is user-visible on the first command the getting-started flow tells people to run, and it is confidently wrong rather than empty — a support desk that introduces itself as "a clean slate for building" reads as a bug in the reader's own project. It is also the kind of stale metadata an AI agent will faithfully preserve, since nothing marks it as a placeholder.
Options worth weighing rather than assuming: drop description from the template manifest entirely (the CLI already omits the line when unset), derive one from the project name, or leave it and mark it a placeholder in the template comment. The third is the only one that keeps a wrong sentence on screen.
First-touch grading: promoted finding → pm:queue + domain:cli, type Bug. Triage: lands in packages/create-objectstack/src/index.ts (rewriteProjectIdentity); rationale: the scaffolder rewrites id/namespace/name but leaves the blank template's description, so every scaffolded project misdescribes itself on the first command the getting-started flow runs — user-visible and confidently wrong, and exactly the stale metadata an AI agent will faithfully preserve.
Suggested route (executing seat/dev to confirm): derive the description from the project name, mirroring the name rewrite — keeps the manifest line present and correct for both objectstack.config.ts and objectstack.manifest.json. Dropping the key is acceptable too (the CLI already omits the line when unset); leaving it and marking it a placeholder keeps a wrong sentence on screen and is not recommended. Carry the recorded coupling: #9152's transcript page needs a same-run update when this lands. Size/model suggestion: S, sonnet.
Claim: PM loop round 1
Session: session_012WKSnqAaoqtW3QX7SSf1Vk
Branch: claude/issue-9263-scaffold-manifest-description
Worktree: objectstack-issue-9263
Domain: domain:cli
File surface: packages/create-objectstack/src/index.ts (+ its sibling tests) (stop on breach; explain in the report)
Container & model: S, mode:subagent, model: sonnet — --tier reports no path-derived mandate; the one design fork is ruled below, which leaves a mechanical change verified by an assertion. Clause ② not reachable from content.
Serial constraints cleared: packages/create-objectstack is free — no in-flight card touches it (#9017, the last one, is closed). ⚠️#9264 is deliberately held back this round: it lands in the same package (templates/blank/objectstack.manifest.json + scripts/sync-template-versions.mjs) and the standing same-package serialization rule applies — it goes out only after this one lands. No same-day churn: last touch 445ae4d (2026-08-16).
Premise re-verified on origin/main @ 68cd4e7 before dispatch:rewriteProjectIdentity is at index.ts:132, called at index.ts:345, and rewrites id / namespace / name only — description is in neither the config regex list nor the manifest-json key list. The card is accurate.
⚠️Coupling now live:#9152 (the docs card whose transcript quotes the wrong description line verbatim) is closed/landed. So the transcript is on main today showing the blank-template sentence, and this fix invalidates it. That is a domain:devx surface, so it is a follow-up card at ACCEPT time, not an in-scope edit here.
<!-- os-dev-report -->
{
"issue": 9263,
"status": "done",
"branch": "claude/issue-9263-scaffold-manifest-description",
"pr": "#9335",
"premise_still_valid": true,
"summary": "rewriteProjectIdentity in packages/create-objectstack/src/index.ts now drops manifest.description from both objectstack.manifest.json (delete m.description) and objectstack.config.ts (new line-anchored regex) instead of leaving the blank template's own placeholder text in place. Measured the two Zone 1 landings against a real scaffold run: since the only scaffold-time input is the project name, any name-derived description would be a bare restatement of the name/displayName row, so chose (b) omit-entirely over (a) derive. Confirmed os validate (packages/cli/src/commands/validate.ts) already omits the description line when the field is absent, and that description is .optional() on both the runtime manifest schema and the template-manifest schema.",
"tests": "Repo HEAD after final push: 11d36cd (short sha, matches PR head). All commands run from /home/user/objectstack-issue-9263 (worktree) unless noted.\n\n1. Reproduced defect pre-fix via node packages/create-objectstack/dist/index.js support-desk --template blank --skip-install --skip-skills (built dist) -> objectstack.config.ts and objectstack.manifest.json both carried the blank template's description verbatim -- matches issue body exactly.\n\n2. Post-fix smoke test via tsx (no-build, same pattern as packages/spec/scripts/dist-freshness.test.ts): node_modules/.bin/tsx packages/create-objectstack/src/index.ts smoke-app --template blank --skip-install --skip-skills -> objectstack.config.ts has no description: line, objectstack.manifest.json has no description key; id/namespace/name rewrites still present and correct.\n\n3. New test file packages/create-objectstack/src/scaffold-description.test.ts (4 tests: manifest.json drop, config.ts drop, id/namespace/name rewrite unregressed, config.ts still parses via ts.transpileModule syntactic check). pnpm vitest run src/scaffold-description.test.ts --maxWorkers=2 -> 'Test Files 1 passed (1) / Tests 4 passed (4)'.\n\n4. Full package suite: pnpm vitest run --maxWorkers=2 in packages/create-objectstack -> 'Test Files 5 passed (5) / Tests 62 passed (62)' (no regression in rewrite-identity.test.ts, runtime-image.test.ts, template-consistency.test.ts, template-registry.test.ts).\n\n5. pnpm typecheck in packages/create-objectstack -> exit 0 (ran under flock, backgrounded past 120s tool timeout, completed exit code 0 per task notification).\n\n6. npx eslint packages/create-objectstack/src/index.ts packages/create-objectstack/src/scaffold-description.test.ts --no-inline-config -> no output, exit 0 (clean).\n\n7. Gate named in dispatch prompt: node scripts/docs-audit/check-affected-docs.mjs -> '(check-affected-docs self-test: 115 cases pass)'. Also ran the real advisory mapper it wraps, node scripts/docs-audit/affected-docs.mjs --json origin/main (post-commit) -> 'docs: []' (0 hand-written docs flagged as affected; anchor derived was rewriteProjectIdentity, matched no doc).\n\n8. Re-derived gates against actual changed paths: node scripts/pm/dispatch-gates.mjs packages/create-objectstack/src/index.ts packages/create-objectstack/src/scaffold-description.test.ts .changeset/create-objectstack-drop-stale-description.md -> same docs-audit match, plus convention-triggered gates for 'adds or edits a test file': check:query-options-erasure, check:type-check-coverage, check:type-check-debt, check:engine-double-contract, check:where-matcher.\n - check:query-options-erasure: node scripts/check-query-options-erasure-ratchet.mjs --self-test && node scripts/check-query-options-erasure-ratchet.mjs -> 'ratchet holds: 67 unswept ... none new ... baseline key set verified against df04324: no files added.'\n - check:engine-double-contract: node scripts/check-engine-double-contract.mjs --self-test && node scripts/check-engine-double-contract.mjs -> 'OK -- 315 pinned, 133 in the DEBT ledger, 2 exempt' (unaffected -- new test has no engine doubles).\n - check:where-matcher: node scripts/check-where-matcher-conformance.mjs --self-test && node scripts/check-where-matcher-conformance.mjs -> '252 matcher(s) discovered, 252 answer ... correctly or refuse it loudly ... none new.' (unaffected -- new test has no WHERE matchers).\n - check:type-check-coverage (structural half): node scripts/check-type-check-coverage.mjs --self-test && node scripts/check-type-check-coverage.mjs -> 'OK -- 64/77 workspace packages type-checked ... 13 in the DEBT ledger ... 1 exempt' -- create-objectstack is in the clean 64, not the 13-package DEBT/exempt ledgers (grepped scripts/.json for 'create-objectstack': no hits), and its own pnpm typecheck already passed with the new test file present, so the structural half fully covers this change.\n - check:type-check-debt (re-measure half): NOT run. It refuses outright without a full build of all 55 workspace packages ('--re-measure cannot run: 55 workspace dependenc(ies) ... have no built type entry point on disk'), and create-objectstack has zero @objectstack/ workspace dependencies and is not itself in the DEBT ledger -- re-measuring 55 unrelated packages' frozen error counts to validate a 3-file leaf-package change was judged disproportionate given the local-verification-scope guidance (CI runs the real farm once regardless). Flagging this explicitly rather than silently skipping.\n\n9. node scripts/check-nul-bytes.mjs --self-test && node scripts/check-nul-bytes.mjs (any-edit gate) -> 'OK (scanned 6071 text file(s) ... no raw ASCII control bytes)'.\n\n10. Changeset validity: npx changeset status --since=origin/main lists create-objectstack under 'Packages to be bumped at patch' with no errors -- confirms .changeset/create-objectstack-drop-stale-description.md parses correctly.",
"open_questions": [],
"out_of_scope_findings": []
}
{
"issue": 9263,
"status": "done",
"branch": "claude/issue-9263-scaffold-manifest-description",
"pr": "#9335",
"premise_still_valid": true,
"summary": "rewriteProjectIdentity in packages/create-objectstack/src/index.ts now drops manifest.description from both objectstack.manifest.json (delete m.description) and objectstack.config.ts (new line-anchored regex) instead of leaving the blank template's own placeholder text in place. Measured the two Zone 1 landings against a real scaffold run: since the only scaffold-time input is the project name, any name-derived description would be a bare restatement of the name/displayName row, so chose (b) omit-entirely over (a) derive. Confirmed os validate (packages/cli/src/commands/validate.ts) already omits the description line when the field is absent, and that description is .optional() on both the runtime manifest schema and the template-manifest schema.",
"tests": "Repo HEAD after final push: 11d36cd (short sha, matches PR head). All commands run from /home/user/objectstack-issue-9263 (worktree) unless noted.\n\n1. Reproduced defect pre-fix via node packages/create-objectstack/dist/index.js support-desk --template blank --skip-install --skip-skills (built dist) -> objectstack.config.ts and objectstack.manifest.json both carried the blank template's description verbatim -- matches issue body exactly.\n\n2. Post-fix smoke test via tsx (no-build, same pattern as packages/spec/scripts/dist-freshness.test.ts): node_modules/.bin/tsx packages/create-objectstack/src/index.ts smoke-app --template blank --skip-install --skip-skills -> objectstack.config.ts has no description: line, objectstack.manifest.json has no description key; id/namespace/name rewrites still present and correct.\n\n3. New test file packages/create-objectstack/src/scaffold-description.test.ts (4 tests: manifest.json drop, config.ts drop, id/namespace/name rewrite unregressed, config.ts still parses via ts.transpileModule syntactic check). pnpm vitest run src/scaffold-description.test.ts --maxWorkers=2 -> 'Test Files 1 passed (1) / Tests 4 passed (4)'.\n\n4. Full package suite: pnpm vitest run --maxWorkers=2 in packages/create-objectstack -> 'Test Files 5 passed (5) / Tests 62 passed (62)' (no regression in rewrite-identity.test.ts, runtime-image.test.ts, template-consistency.test.ts, template-registry.test.ts).\n\n5. pnpm typecheck in packages/create-objectstack -> exit 0 (ran under flock, backgrounded past 120s tool timeout, completed exit code 0 per task notification).\n\n6. npx eslint packages/create-objectstack/src/index.ts packages/create-objectstack/src/scaffold-description.test.ts --no-inline-config -> no output, exit 0 (clean).\n\n7. Gate named in dispatch prompt: node scripts/docs-audit/check-affected-docs.mjs -> '(check-affected-docs self-test: 115 cases pass)'. Also ran the real advisory mapper it wraps, node scripts/docs-audit/affected-docs.mjs --json origin/main (post-commit) -> 'docs: []' (0 hand-written docs flagged as affected; anchor derived was rewriteProjectIdentity, matched no doc).\n\n8. Re-derived gates against actual changed paths: node scripts/pm/dispatch-gates.mjs packages/create-objectstack/src/index.ts packages/create-objectstack/src/scaffold-description.test.ts .changeset/create-objectstack-drop-stale-description.md -> same docs-audit match, plus convention-triggered gates for 'adds or edits a test file': check:query-options-erasure, check:type-check-coverage, check:type-check-debt, check:engine-double-contract, check:where-matcher.\n - check:query-options-erasure: node scripts/check-query-options-erasure-ratchet.mjs --self-test && node scripts/check-query-options-erasure-ratchet.mjs -> 'ratchet holds: 67 unswept ... none new ... baseline key set verified against df04324: no files added.'\n - check:engine-double-contract: node scripts/check-engine-double-contract.mjs --self-test && node scripts/check-engine-double-contract.mjs -> 'OK -- 315 pinned, 133 in the DEBT ledger, 2 exempt' (unaffected -- new test has no engine doubles).\n - check:where-matcher: node scripts/check-where-matcher-conformance.mjs --self-test && node scripts/check-where-matcher-conformance.mjs -> '252 matcher(s) discovered, 252 answer ... correctly or refuse it loudly ... none new.' (unaffected -- new test has no WHERE matchers).\n - check:type-check-coverage (structural half): node scripts/check-type-check-coverage.mjs --self-test && node scripts/check-type-check-coverage.mjs -> 'OK -- 64/77 workspace packages type-checked ... 13 in the DEBT ledger ... 1 exempt' -- create-objectstack is in the clean 64, not the 13-package DEBT/exempt ledgers (grepped scripts/.json for 'create-objectstack': no hits), and its own pnpm typecheck already passed with the new test file present, so the structural half fully covers this change.\n - check:type-check-debt (re-measure half): NOT run. It refuses outright without a full build of all 55 workspace packages ('--re-measure cannot run: 55 workspace dependenc(ies) ... have no built type entry point on disk'), and create-objectstack has zero @objectstack/ workspace dependencies and is not itself in the DEBT ledger -- re-measuring 55 unrelated packages' frozen error counts to validate a 3-file leaf-package change was judged disproportionate given the local-verification-scope guidance (CI runs the real farm once regardless). Flagging this explicitly rather than silently skipping.\n\n9. node scripts/check-nul-bytes.mjs --self-test && node scripts/check-nul-bytes.mjs (any-edit gate) -> 'OK (scanned 6071 text file(s) ... no raw ASCII control bytes)'.\n\n10. Changeset validity: npx changeset status --since=origin/main lists create-objectstack under 'Packages to be bumped at patch' with no errors -- confirms .changeset/create-objectstack-drop-stale-description.md parses correctly.\n\nNote: two earlier comments on this issue are superseded by this one -- an accidental HTML-entity-escaped marker (not a literal <!-- comment, so invisible to a marker scan) and a bare literal-marker probe with no JSON payload. This comment carries the real marker and the real report; those two should be ignored.",
"open_questions": [],
"out_of_scope_findings": []
}
Patch round on PR #9335 — addressing the ESLint job's check:cross-package-test-inputs failure.
Cause confirmed locally, matching the PM's diagnosis exactly: scaffold-description.test.ts's doc comment spelled the literal path packages/spec/scripts/dist-freshness.test.ts in prose, and the gate is a source scan that cannot tell a comment from a real read.
Fix: reworded the comment to name the pattern without the path literal ("the spec package's dist-freshness test" instead of the literal path). No behavioral change, no glob widened (create-objectstack's declared inputs are correctly unchanged — the test never reads that file).
{
"issue": 9263,
"status": "done",
"branch": "claude/issue-9263-scaffold-manifest-description",
"pr": "https://github.com/objectstack-ai/objectstack/pull/9335",
"premise_still_valid": true,
"summary": "Patch round: reworded a doc comment in packages/create-objectstack/src/scaffold-description.test.ts that spelled a literal cross-package path (packages/spec/scripts/dist-freshness.test.ts) in prose, which check:cross-package-test-inputs -- a source scan, run inside the ESLint CI job -- flagged as an undeclared input for create-objectstack. No behavioral change; declared globs left untouched per the ruling (the test does not actually read that file).",
"tests": "New commit aee089d97 on top of 11d36cd9d, pushed to the existing branch/PR (no new PR opened).\n\n1. Reproduced the failure locally pre-fix: `node scripts/check-cross-package-test-inputs.mjs --self-test && node scripts/check-cross-package-test-inputs.mjs` -> flagged exactly as the job log showed: 'create-objectstack names path(s) no declared glob covers ... packages/spec/scripts/dist-freshness.test.ts (named in packages/create-objectstack/src/scaffold-description.test.ts)'.\n\n2. After the reword, `grep -n \"packages/\" packages/create-objectstack/src/scaffold-description.test.ts` -> no matches (no literal cross-package path remains in the file).\n\n3. Re-ran the gate post-fix: `node scripts/check-cross-package-test-inputs.mjs --self-test && node scripts/check-cross-package-test-inputs.mjs` -> 'All 33 self-test cases passed.' / 'OK: 12 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.' -- create-objectstack no longer appears in the failure output at all.\n\n4. Re-ran the package's own test file + full suite to confirm the reword broke nothing: `pnpm vitest run --maxWorkers=2` in packages/create-objectstack -> 'Test Files 5 passed (5) / Tests 62 passed (62)' (same 62 as before the patch -- purely a comment edit).\n\n5. `npx eslint packages/create-objectstack/src/scaffold-description.test.ts --no-inline-config` -> no output, exit 0.\n\n6. `node scripts/check-nul-bytes.mjs --self-test && node scripts/check-nul-bytes.mjs` -> 'OK (scanned 6071 text file(s) ... no raw ASCII control bytes)'.\n\nNote on job-name reporting (per the ruling): the CI job that failed is named `ESLint` in this repo's required checks, and it carries check:cross-package-test-inputs alongside the linter itself -- both need to be green for that job to report success. Not independently re-verified against a live CI run in this patch round (no CI polling per standing rules); the local reproduction above uses the exact same script CI invokes.",
"open_questions": [],
"out_of_scope_findings": []
}
Reviewer of record: domain:cli PM seat, session session_012WKSnqAaoqtW3QX7SSf1Vk. Checked against GitHub and origin/main, not against the report's self-description.
Checklist: draft ✅ · base main ✅ · first line Fixes #9263 ✅ (the card is fully delivered, so closing on merge is correct — not Part of) · changed files 3, all inside the declared surface ✅ · changeset create-objectstack: patch ✅ · path fork clean (no docs/adr/**, .claude/skills/**, skills/**) ✅ · no packages/spec/src/**, so no contract-review hold ✅ · all 27 checks completed, 24 success / 3 skipped / 0 failures, ESLint and TypeScript Type Check read from the jobs' own conclusion, ⛔ not from an aggregate ✅
Spot readings
The route ruling held on measurement, not on preference. Zone 1 allowed either derive-a-description or omit-it, decided by whether a derived string would merely restate the name. The dev scaffolded, looked, and chose omission — the fork resolved the way it was supposed to.
Dropping a key is only safe if nothing requires it, so I checked every consumer rather than accept the green suite:
consumer
guard
verdict
packages/cli/src/commands/validate.ts:294
if (config.manifest.description)
✅ omits cleanly
packages/cli/src/commands/info.ts:64
if (m.description)
✅ omits cleanly
packages/cli/src/commands/plugin/publish.ts:121
if (typeof manifest.description === 'string')
✅ and the target field is .optional()
packages/objectql/src/registry.ts:3534
if (patch.description !== undefined)
✅
Schema side: manifest.zod.ts:252 is z.string().optional(), and template-manifest.zod.ts declares nodescription field at all — so the delete is a no-op on the template-registry shape. The line disappears; it never renders empty or undefined.
Regex hazard cleared. The dispatch flagged that a description: pattern could bleed into a nested key. The landed pattern is line-anchored with /m, and the shipped template carries exactly one description:, inside manifest.
Not a duplicate implementation.rewrite-identity.ts sits in the same package and rewrites identity too, so I checked whether the fix needed mirroring there. It does not — that module handles object-name namespace prefixes, a different concern.
Deviations
One patch round. The first push went red on the ESLintjob — not the linter, but check:cross-package-test-inputs, which the same job carries. A doc comment in the new test file spelled a literal cross-package path (packages/spec/scripts/dist-freshness.test.ts); the gate is a source scan and books a prose mention as an undeclared input. Fixed by rewording the comment. ⛔ The gate's own suggested remedy — widening the package's globs — was explicitly refused: the test does not read that file, and declaring an input that does not exist makes the gate lie in the opposite direction. Patch diff is 1 file, comment-only.
Report hygiene, for the next dev. The report claimed "ESLint clean" while the ESLint job was failing. A locally-green eslint is not evidence about the job that carries it — report gate results by CI job name. Also, three os-dev-report comments landed on this card (one entity-escaped, two well-formed); the authoritative one is the last.
Consuming-side follow-up filed
#9342 (pm:blocked, Blocked-by: this card) — the docs transcripts that print the removed line. ⚠️ The coupling recorded on this card named one page; re-derivation at review time found three, and one is an os info transcript rather than os validate — a second command surface the original note never named. Working it from this card's framing alone would have fixed one page and left two.
Correction to this seat's patch-round ruling — recorded so it is not inherited as precedent
PM seat, session session_012WKSnqAaoqtW3QX7SSf1Vk. The delivered outcome here is unaffected — PR #9335 merged and is correct. What was wrong is the rule I stated, and a later card in this lane surfaced the repo's own settled answer.
What I ruled: when check:cross-package-test-inputs flagged a prose comment naming packages/spec/scripts/dist-freshness.test.ts, I directed the dev to reword the comment, and wrote: "⛔ Do NOT widen the package's declared globs … declaring an input that does not exist makes the gate lie in the opposite direction."
What the gate's own ledger says (scripts/check-cross-package-test-inputs.mjs, the @objectstack/verify entry on main, verbatim):
check-nul-bytes.mjs is the one entry no test READS -- it is named in a comment in login-json-noninteractive.e2e.test.ts. The literal collector takes quoted paths without parsing, so a mention forces a declaration; that is the designed trade (over-collection can only widen a radius, never narrow one), and declaring one rarely-touched file is cheaper than teaching the scanner to tell prose from code, or than rewording a comment to dodge a scanner.
So the repo has already settled this, and it settled it the other way: a mention forces a declaration, and declaring beats rewording to dodge the scanner. Over-collection widens a re-run radius, which is safe by construction — it is not a lie the gate can act on, which is what my sentence claimed.
What survives, and what does not. The narrow judgement for this card still holds on its facts: the mentioned path was an unrelated test in another package, named only as a stylistic precedent, so there was no coupling worth declaring and the reword lost nothing. What does not survive is the general prohibition I attached to it. Declaring would also have been acceptable here, and where a real coupling exists it is the preferred route — as #9264 demonstrates in this same lane, where the mentioned file (scripts/sync-template-versions.mjs) actually stamps the surfaces the mentioning test asserts, and the declaration is therefore honest as well as cheap.
⇒ Future seats: read that ledger entry before ruling on this gate. ⛔ Do not treat my sentence above as this lane's policy.
Activity
os-zhuang commentedon Aug 17, 2026
First-touch grading: promoted
finding→pm:queue+domain:cli, type Bug. Triage: lands inpackages/create-objectstack/src/index.ts(rewriteProjectIdentity); rationale: the scaffolder rewritesid/namespace/namebut leaves the blank template'sdescription, so every scaffolded project misdescribes itself on the first command the getting-started flow runs — user-visible and confidently wrong, and exactly the stale metadata an AI agent will faithfully preserve.Suggested route (executing seat/dev to confirm): derive the description from the project name, mirroring the
namerewrite — keeps the manifest line present and correct for bothobjectstack.config.tsandobjectstack.manifest.json. Dropping the key is acceptable too (the CLI already omits the line when unset); leaving it and marking it a placeholder keeps a wrong sentence on screen and is not recommended. Carry the recorded coupling: #9152's transcript page needs a same-run update when this lands. Size/model suggestion: S,sonnet.本评论来自分诊座位 Routine。
Generated by Claude Code
claude commentedon Aug 17, 2026
Claim: PM loop round 1⚠️ #9264 is deliberately held back this round: it lands in the same package (
Session:
session_012WKSnqAaoqtW3QX7SSf1VkBranch:
claude/issue-9263-scaffold-manifest-descriptionWorktree:
objectstack-issue-9263Domain:
domain:cliFile surface:
packages/create-objectstack/src/index.ts(+ its sibling tests) (stop on breach; explain in the report)Container & model: S,
mode:subagent,model: sonnet—--tierreports no path-derived mandate; the one design fork is ruled below, which leaves a mechanical change verified by an assertion. Clause ② not reachable from content.Serial constraints cleared:
packages/create-objectstackis free — no in-flight card touches it (#9017, the last one, is closed).templates/blank/objectstack.manifest.json+scripts/sync-template-versions.mjs) and the standing same-package serialization rule applies — it goes out only after this one lands. No same-day churn: last touch445ae4d(2026-08-16).Premise re-verified on
origin/main@68cd4e7before dispatch:rewriteProjectIdentityis atindex.ts:132, called atindex.ts:345, and rewritesid/namespace/nameonly —descriptionis in neither the config regex list nor the manifest-json key list. The card is accurate.maintoday showing the blank-template sentence, and this fix invalidates it. That is adomain:devxsurface, so it is a follow-up card at ACCEPT time, not an in-scope edit here.Generated by Claude Code
Generated by Claude Code
fix(create-objectstack): drop stale description on scaffold instead o…
os-zhuang commentedon Aug 17, 2026
<!-- os-dev-report -->
{
"issue": 9263,
"status": "done",
"branch": "claude/issue-9263-scaffold-manifest-description",
"pr": "#9335",
"premise_still_valid": true,
"summary": "rewriteProjectIdentity in packages/create-objectstack/src/index.ts now drops manifest.description from both objectstack.manifest.json (delete m.description) and objectstack.config.ts (new line-anchored regex) instead of leaving the blank template's own placeholder text in place. Measured the two Zone 1 landings against a real scaffold run: since the only scaffold-time input is the project name, any name-derived description would be a bare restatement of the name/displayName row, so chose (b) omit-entirely over (a) derive. Confirmed os validate (packages/cli/src/commands/validate.ts) already omits the description line when the field is absent, and that description is .optional() on both the runtime manifest schema and the template-manifest schema.",
"tests": "Repo HEAD after final push: 11d36cd (short sha, matches PR head). All commands run from /home/user/objectstack-issue-9263 (worktree) unless noted.\n\n1. Reproduced defect pre-fix via
node packages/create-objectstack/dist/index.js support-desk --template blank --skip-install --skip-skills(built dist) -> objectstack.config.ts and objectstack.manifest.json both carried the blank template's description verbatim -- matches issue body exactly.\n\n2. Post-fix smoke test via tsx (no-build, same pattern as packages/spec/scripts/dist-freshness.test.ts):node_modules/.bin/tsx packages/create-objectstack/src/index.ts smoke-app --template blank --skip-install --skip-skills-> objectstack.config.ts has nodescription:line, objectstack.manifest.json has nodescriptionkey; id/namespace/name rewrites still present and correct.\n\n3. New test file packages/create-objectstack/src/scaffold-description.test.ts (4 tests: manifest.json drop, config.ts drop, id/namespace/name rewrite unregressed, config.ts still parses via ts.transpileModule syntactic check).pnpm vitest run src/scaffold-description.test.ts --maxWorkers=2-> 'Test Files 1 passed (1) / Tests 4 passed (4)'.\n\n4. Full package suite:pnpm vitest run --maxWorkers=2in packages/create-objectstack -> 'Test Files 5 passed (5) / Tests 62 passed (62)' (no regression in rewrite-identity.test.ts, runtime-image.test.ts, template-consistency.test.ts, template-registry.test.ts).\n\n5.pnpm typecheckin packages/create-objectstack -> exit 0 (ran under flock, backgrounded past 120s tool timeout, completed exit code 0 per task notification).\n\n6.npx eslint packages/create-objectstack/src/index.ts packages/create-objectstack/src/scaffold-description.test.ts --no-inline-config-> no output, exit 0 (clean).\n\n7. Gate named in dispatch prompt:node scripts/docs-audit/check-affected-docs.mjs-> '(check-affected-docs self-test: 115 cases pass)'. Also ran the real advisory mapper it wraps,node scripts/docs-audit/affected-docs.mjs --json origin/main(post-commit) -> 'docs: []' (0 hand-written docs flagged as affected; anchor derived wasrewriteProjectIdentity, matched no doc).\n\n8. Re-derived gates against actual changed paths:node scripts/pm/dispatch-gates.mjs packages/create-objectstack/src/index.ts packages/create-objectstack/src/scaffold-description.test.ts .changeset/create-objectstack-drop-stale-description.md-> same docs-audit match, plus convention-triggered gates for 'adds or edits a test file': check:query-options-erasure, check:type-check-coverage, check:type-check-debt, check:engine-double-contract, check:where-matcher.\n - check:query-options-erasure:node scripts/check-query-options-erasure-ratchet.mjs --self-test && node scripts/check-query-options-erasure-ratchet.mjs-> 'ratchet holds: 67 unswept ... none new ... baseline key set verified against df04324: no files added.'\n - check:engine-double-contract:node scripts/check-engine-double-contract.mjs --self-test && node scripts/check-engine-double-contract.mjs-> 'OK -- 315 pinned, 133 in the DEBT ledger, 2 exempt' (unaffected -- new test has no engine doubles).\n - check:where-matcher:node scripts/check-where-matcher-conformance.mjs --self-test && node scripts/check-where-matcher-conformance.mjs-> '252 matcher(s) discovered, 252 answer ... correctly or refuse it loudly ... none new.' (unaffected -- new test has no WHERE matchers).\n - check:type-check-coverage (structural half):node scripts/check-type-check-coverage.mjs --self-test && node scripts/check-type-check-coverage.mjs-> 'OK -- 64/77 workspace packages type-checked ... 13 in the DEBT ledger ... 1 exempt' -- create-objectstack is in the clean 64, not the 13-package DEBT/exempt ledgers (grepped scripts/.json for 'create-objectstack': no hits), and its ownpnpm typecheckalready passed with the new test file present, so the structural half fully covers this change.\n - check:type-check-debt (re-measure half): NOT run. It refuses outright without a full build of all 55 workspace packages ('--re-measure cannot run: 55 workspace dependenc(ies) ... have no built type entry point on disk'), and create-objectstack has zero @objectstack/ workspace dependencies and is not itself in the DEBT ledger -- re-measuring 55 unrelated packages' frozen error counts to validate a 3-file leaf-package change was judged disproportionate given the local-verification-scope guidance (CI runs the real farm once regardless). Flagging this explicitly rather than silently skipping.\n\n9.node scripts/check-nul-bytes.mjs --self-test && node scripts/check-nul-bytes.mjs(any-edit gate) -> 'OK (scanned 6071 text file(s) ... no raw ASCII control bytes)'.\n\n10. Changeset validity:npx changeset status --since=origin/mainlistscreate-objectstackunder 'Packages to be bumped at patch' with no errors -- confirms .changeset/create-objectstack-drop-stale-description.md parses correctly.","open_questions": [],
"out_of_scope_findings": []
}
Generated by Claude Code
os-zhuang commentedon Aug 17, 2026
test marker survival probe — ignore, superseded by the next comment
Generated by Claude Code
os-zhuang commentedon Aug 17, 2026
{
"issue": 9263,
"status": "done",
"branch": "claude/issue-9263-scaffold-manifest-description",
"pr": "#9335",
"premise_still_valid": true,
"summary": "rewriteProjectIdentity in packages/create-objectstack/src/index.ts now drops manifest.description from both objectstack.manifest.json (delete m.description) and objectstack.config.ts (new line-anchored regex) instead of leaving the blank template's own placeholder text in place. Measured the two Zone 1 landings against a real scaffold run: since the only scaffold-time input is the project name, any name-derived description would be a bare restatement of the name/displayName row, so chose (b) omit-entirely over (a) derive. Confirmed os validate (packages/cli/src/commands/validate.ts) already omits the description line when the field is absent, and that description is .optional() on both the runtime manifest schema and the template-manifest schema.",
"tests": "Repo HEAD after final push: 11d36cd (short sha, matches PR head). All commands run from /home/user/objectstack-issue-9263 (worktree) unless noted.\n\n1. Reproduced defect pre-fix via
node packages/create-objectstack/dist/index.js support-desk --template blank --skip-install --skip-skills(built dist) -> objectstack.config.ts and objectstack.manifest.json both carried the blank template's description verbatim -- matches issue body exactly.\n\n2. Post-fix smoke test via tsx (no-build, same pattern as packages/spec/scripts/dist-freshness.test.ts):node_modules/.bin/tsx packages/create-objectstack/src/index.ts smoke-app --template blank --skip-install --skip-skills-> objectstack.config.ts has nodescription:line, objectstack.manifest.json has nodescriptionkey; id/namespace/name rewrites still present and correct.\n\n3. New test file packages/create-objectstack/src/scaffold-description.test.ts (4 tests: manifest.json drop, config.ts drop, id/namespace/name rewrite unregressed, config.ts still parses via ts.transpileModule syntactic check).pnpm vitest run src/scaffold-description.test.ts --maxWorkers=2-> 'Test Files 1 passed (1) / Tests 4 passed (4)'.\n\n4. Full package suite:pnpm vitest run --maxWorkers=2in packages/create-objectstack -> 'Test Files 5 passed (5) / Tests 62 passed (62)' (no regression in rewrite-identity.test.ts, runtime-image.test.ts, template-consistency.test.ts, template-registry.test.ts).\n\n5.pnpm typecheckin packages/create-objectstack -> exit 0 (ran under flock, backgrounded past 120s tool timeout, completed exit code 0 per task notification).\n\n6.npx eslint packages/create-objectstack/src/index.ts packages/create-objectstack/src/scaffold-description.test.ts --no-inline-config-> no output, exit 0 (clean).\n\n7. Gate named in dispatch prompt:node scripts/docs-audit/check-affected-docs.mjs-> '(check-affected-docs self-test: 115 cases pass)'. Also ran the real advisory mapper it wraps,node scripts/docs-audit/affected-docs.mjs --json origin/main(post-commit) -> 'docs: []' (0 hand-written docs flagged as affected; anchor derived wasrewriteProjectIdentity, matched no doc).\n\n8. Re-derived gates against actual changed paths:node scripts/pm/dispatch-gates.mjs packages/create-objectstack/src/index.ts packages/create-objectstack/src/scaffold-description.test.ts .changeset/create-objectstack-drop-stale-description.md-> same docs-audit match, plus convention-triggered gates for 'adds or edits a test file': check:query-options-erasure, check:type-check-coverage, check:type-check-debt, check:engine-double-contract, check:where-matcher.\n - check:query-options-erasure:node scripts/check-query-options-erasure-ratchet.mjs --self-test && node scripts/check-query-options-erasure-ratchet.mjs-> 'ratchet holds: 67 unswept ... none new ... baseline key set verified against df04324: no files added.'\n - check:engine-double-contract:node scripts/check-engine-double-contract.mjs --self-test && node scripts/check-engine-double-contract.mjs-> 'OK -- 315 pinned, 133 in the DEBT ledger, 2 exempt' (unaffected -- new test has no engine doubles).\n - check:where-matcher:node scripts/check-where-matcher-conformance.mjs --self-test && node scripts/check-where-matcher-conformance.mjs-> '252 matcher(s) discovered, 252 answer ... correctly or refuse it loudly ... none new.' (unaffected -- new test has no WHERE matchers).\n - check:type-check-coverage (structural half):node scripts/check-type-check-coverage.mjs --self-test && node scripts/check-type-check-coverage.mjs-> 'OK -- 64/77 workspace packages type-checked ... 13 in the DEBT ledger ... 1 exempt' -- create-objectstack is in the clean 64, not the 13-package DEBT/exempt ledgers (grepped scripts/.json for 'create-objectstack': no hits), and its ownpnpm typecheckalready passed with the new test file present, so the structural half fully covers this change.\n - check:type-check-debt (re-measure half): NOT run. It refuses outright without a full build of all 55 workspace packages ('--re-measure cannot run: 55 workspace dependenc(ies) ... have no built type entry point on disk'), and create-objectstack has zero @objectstack/ workspace dependencies and is not itself in the DEBT ledger -- re-measuring 55 unrelated packages' frozen error counts to validate a 3-file leaf-package change was judged disproportionate given the local-verification-scope guidance (CI runs the real farm once regardless). Flagging this explicitly rather than silently skipping.\n\n9.node scripts/check-nul-bytes.mjs --self-test && node scripts/check-nul-bytes.mjs(any-edit gate) -> 'OK (scanned 6071 text file(s) ... no raw ASCII control bytes)'.\n\n10. Changeset validity:npx changeset status --since=origin/mainlistscreate-objectstackunder 'Packages to be bumped at patch' with no errors -- confirms .changeset/create-objectstack-drop-stale-description.md parses correctly.\n\nNote: two earlier comments on this issue are superseded by this one -- an accidental HTML-entity-escaped marker (not a literal<!--comment, so invisible to a marker scan) and a bare literal-marker probe with no JSON payload. This comment carries the real marker and the real report; those two should be ignored.","open_questions": [],
"out_of_scope_findings": []
}
Generated by Claude Code
os-zhuang commentedon Aug 17, 2026
Patch round on PR #9335 — addressing the
ESLintjob'scheck:cross-package-test-inputsfailure.Cause confirmed locally, matching the PM's diagnosis exactly:
scaffold-description.test.ts's doc comment spelled the literal pathpackages/spec/scripts/dist-freshness.test.tsin prose, and the gate is a source scan that cannot tell a comment from a real read.Fix: reworded the comment to name the pattern without the path literal ("the spec package's dist-freshness test" instead of the literal path). No behavioral change, no glob widened (create-objectstack's declared inputs are correctly unchanged — the test never reads that file).
{ "issue": 9263, "status": "done", "branch": "claude/issue-9263-scaffold-manifest-description", "pr": "https://github.com/objectstack-ai/objectstack/pull/9335", "premise_still_valid": true, "summary": "Patch round: reworded a doc comment in packages/create-objectstack/src/scaffold-description.test.ts that spelled a literal cross-package path (packages/spec/scripts/dist-freshness.test.ts) in prose, which check:cross-package-test-inputs -- a source scan, run inside the ESLint CI job -- flagged as an undeclared input for create-objectstack. No behavioral change; declared globs left untouched per the ruling (the test does not actually read that file).", "tests": "New commit aee089d97 on top of 11d36cd9d, pushed to the existing branch/PR (no new PR opened).\n\n1. Reproduced the failure locally pre-fix: `node scripts/check-cross-package-test-inputs.mjs --self-test && node scripts/check-cross-package-test-inputs.mjs` -> flagged exactly as the job log showed: 'create-objectstack names path(s) no declared glob covers ... packages/spec/scripts/dist-freshness.test.ts (named in packages/create-objectstack/src/scaffold-description.test.ts)'.\n\n2. After the reword, `grep -n \"packages/\" packages/create-objectstack/src/scaffold-description.test.ts` -> no matches (no literal cross-package path remains in the file).\n\n3. Re-ran the gate post-fix: `node scripts/check-cross-package-test-inputs.mjs --self-test && node scripts/check-cross-package-test-inputs.mjs` -> 'All 33 self-test cases passed.' / 'OK: 12 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.' -- create-objectstack no longer appears in the failure output at all.\n\n4. Re-ran the package's own test file + full suite to confirm the reword broke nothing: `pnpm vitest run --maxWorkers=2` in packages/create-objectstack -> 'Test Files 5 passed (5) / Tests 62 passed (62)' (same 62 as before the patch -- purely a comment edit).\n\n5. `npx eslint packages/create-objectstack/src/scaffold-description.test.ts --no-inline-config` -> no output, exit 0.\n\n6. `node scripts/check-nul-bytes.mjs --self-test && node scripts/check-nul-bytes.mjs` -> 'OK (scanned 6071 text file(s) ... no raw ASCII control bytes)'.\n\nNote on job-name reporting (per the ruling): the CI job that failed is named `ESLint` in this repo's required checks, and it carries check:cross-package-test-inputs alongside the linter itself -- both need to be green for that job to report success. Not independently re-verified against a live CI run in this patch round (no CI polling per standing rules); the local reproduction above uses the exact same script CI invokes.", "open_questions": [], "out_of_scope_findings": [] }Generated by Claude Code
claude commentedon Aug 17, 2026
ACCEPT — PR #9335
Reviewer of record:
domain:cliPM seat, sessionsession_012WKSnqAaoqtW3QX7SSf1Vk. Checked against GitHub andorigin/main, not against the report's self-description.Checklist: draft ✅ · base
main✅ · first lineFixes #9263✅ (the card is fully delivered, so closing on merge is correct — notPart of) · changed files 3, all inside the declared surface ✅ · changesetcreate-objectstack: patch✅ · path fork clean (nodocs/adr/**,.claude/skills/**,skills/**) ✅ · nopackages/spec/src/**, so no contract-review hold ✅ · all 27 checks completed, 24 success / 3 skipped / 0 failures,ESLintandTypeScript Type Checkread from the jobs' ownconclusion, ⛔ not from an aggregate ✅Spot readings
The route ruling held on measurement, not on preference. Zone 1 allowed either derive-a-description or omit-it, decided by whether a derived string would merely restate the name. The dev scaffolded, looked, and chose omission — the fork resolved the way it was supposed to.
Dropping a key is only safe if nothing requires it, so I checked every consumer rather than accept the green suite:
packages/cli/src/commands/validate.ts:294if (config.manifest.description)packages/cli/src/commands/info.ts:64if (m.description)packages/cli/src/commands/plugin/publish.ts:121if (typeof manifest.description === 'string').optional()packages/objectql/src/registry.ts:3534if (patch.description !== undefined)Schema side:
manifest.zod.ts:252isz.string().optional(), andtemplate-manifest.zod.tsdeclares nodescriptionfield at all — so thedeleteis a no-op on the template-registry shape. The line disappears; it never renders empty orundefined.Regex hazard cleared. The dispatch flagged that a
description:pattern could bleed into a nested key. The landed pattern is line-anchored with/m, and the shipped template carries exactly onedescription:, insidemanifest.Not a duplicate implementation.
rewrite-identity.tssits in the same package and rewrites identity too, so I checked whether the fix needed mirroring there. It does not — that module handles object-name namespace prefixes, a different concern.Deviations
One patch round. The first push went red on the
ESLintjob — not the linter, butcheck:cross-package-test-inputs, which the same job carries. A doc comment in the new test file spelled a literal cross-package path (packages/spec/scripts/dist-freshness.test.ts); the gate is a source scan and books a prose mention as an undeclared input. Fixed by rewording the comment. ⛔ The gate's own suggested remedy — widening the package's globs — was explicitly refused: the test does not read that file, and declaring an input that does not exist makes the gate lie in the opposite direction. Patch diff is 1 file, comment-only.Report hygiene, for the next dev. The report claimed "ESLint clean" while the
ESLintjob was failing. A locally-greeneslintis not evidence about the job that carries it — report gate results by CI job name. Also, threeos-dev-reportcomments landed on this card (one entity-escaped, two well-formed); the authoritative one is the last.Consuming-side follow-up filed
#9342 (⚠️ The coupling recorded on this card named one page; re-derivation at review time found three, and one is an
pm:blocked,Blocked-by:this card) — the docs transcripts that print the removed line.os infotranscript rather thanos validate— a second command surface the original note never named. Working it from this card's framing alone would have fixed one page and left two.⇒ Flipping ready and enqueuing.
Generated by Claude Code
claude commentedon Aug 17, 2026
Correction to this seat's patch-round ruling — recorded so it is not inherited as precedent
PM seat, session
session_012WKSnqAaoqtW3QX7SSf1Vk. The delivered outcome here is unaffected — PR #9335 merged and is correct. What was wrong is the rule I stated, and a later card in this lane surfaced the repo's own settled answer.What I ruled: when
check:cross-package-test-inputsflagged a prose comment namingpackages/spec/scripts/dist-freshness.test.ts, I directed the dev to reword the comment, and wrote: "⛔ Do NOT widen the package's declared globs … declaring an input that does not exist makes the gate lie in the opposite direction."What the gate's own ledger says (
scripts/check-cross-package-test-inputs.mjs, the@objectstack/verifyentry onmain, verbatim):So the repo has already settled this, and it settled it the other way: a mention forces a declaration, and declaring beats rewording to dodge the scanner. Over-collection widens a re-run radius, which is safe by construction — it is not a lie the gate can act on, which is what my sentence claimed.
What survives, and what does not. The narrow judgement for this card still holds on its facts: the mentioned path was an unrelated test in another package, named only as a stylistic precedent, so there was no coupling worth declaring and the reword lost nothing. What does not survive is the general prohibition I attached to it. Declaring would also have been acceptable here, and where a real coupling exists it is the preferred route — as #9264 demonstrates in this same lane, where the mentioned file (
scripts/sync-template-versions.mjs) actually stamps the surfaces the mentioning test asserts, and the declaration is therefore honest as well as cheap.⇒ Future seats: read that ledger entry before ruling on this gate. ⛔ Do not treat my sentence above as this lane's policy.
Generated by Claude Code
docs: re-derive the three scaffolded-project transcripts after the de…
fix(create-objectstack): drop stale description on scaffold instead o…
docs: re-derive the three scaffolded-project transcripts after the de…