Uh oh!
There was an error while loading. Please reload this page.
fix(cli): retire the agent generator — os g agent now names ADR-0063 and points at skills - #11028
Conversation
`os g agent` scaffolded into `src/agents`, a surface ADR-0063 §2 withdrew. The kernel ships exactly two agents (`ask`, `build`) and the runtime catalog filters out every other agent record, so the generated file passed `os validate`, published without complaint, and then never appeared — no error at any step. That is the silent-strip failure mode, arriving through the scaffolder. Deleting the roster entry alone would have moved the silence one step earlier rather than ending it: `Unknown type: agent` plus the surviving roster tells the author their spelling is not on the list, and the natural next move is to hunt for the right spelling of something that no longer exists. So `agent` moves to a `RETIRED_GENERATORS` ledger whose entries owe both halves — the decision that withdrew the surface, and the surface to author instead. The refusal names ADR-0063, the two platform agents, the silent strip, and skills (`src/skills/<name>.skill.ts`, `defineSkill`) as the live extension primitive, and it exits 1 so a CI script that still calls the command stops. Advertising sites swept, not fixed by line number: the generator entry and the `type` argument's roster string in `generate.ts`, `packages/cli/README.md`'s "Available generate types" line, and three sites in `content/docs/deployment/cli.mdx` (the `os g agent support` example, the `agent` / `src/agents/` row of the Available types table, and `os g agent sales-assistant` in Typical Workflow — the last of which the card did not name). `content/docs/getting-started/quick-start.mdx`'s project-layout map advertised `src/agents` as the AI area's authoring directory; it now names `src/skills`, which is what ADR-0063 says an app author writes. `scripts/adr-anchors/packages__cli__src__commands__generate.ts.json` pins ADR-0063 to the file, so the absence of an `agent` generator reads as a decision rather than an oversight to the next author. `os g skill` is deliberately NOT in this change — see the follow-up card. The message says the scaffolder does not exist yet rather than promising it. Part of #10359 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019bmVFqoQPq63zhKrxdYG1r
…d about it Declares the removal as breaking (shipped `minor` under the launch-window convention) and states the consequence a release-notes reader needs: a CI step that still calls `os g agent` starts exiting 1. The ADR-0087 disposition is `no-migration-prescription`, and it is a positive finding rather than a detector miss: a CLI command name is an invocation surface with no authorable key, no stored row and no schema tombstone, so no ledger reader has anything to carry — and unlike the metric retirement that category is usually weighed against, the ledger is not the only notification channel here, because the command itself now refuses at the point of use. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019bmVFqoQPq63zhKrxdYG1r
📓 Docs Drift CheckThis PR changes 1 package(s): 3 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
What this run could not see
Coarse fallback — 23 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin d3ade473e42cbd8ba67a5bf09d064f7448d56ea5 && git checkout d3ade473e42cbd8ba67a5bf09d064f7448d56ea5
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 8d21f7a76912df531c144f17bc3f5bedd07f79c5 567a1a036c00fe647f58a160e117318de6e23c9a && git checkout -B drift-repro 8d21f7a76912df531c144f17bc3f5bedd07f79c5 && git merge --no-ff 567a1a036c00fe647f58a160e117318de6e23c9a
node scripts/docs-audit/affected-docs.mjs --json 8d21f7a76912df531c144f17bc3f5bedd07f79c5
|
Uh oh!
There was an error while loading. Please reload this page.
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 32567403033 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 失败的 job(日志抽取,best effort):
跨 PR 相同签名(24h,按失败测试文件聚合):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
Part of #10359 — deliberately
Part of, not a closing keyword. The maintainer's ruling was Option A (retire theagentgenerator and add askillgenerator) with explicit latitude to split: "The implementer is permitted to split the skill-generator half into its own card if that scopes better; the retirement half proceeds either way." This PR is the retirement half. The skill-generator half is filed as #11025 with the ADR-0063 context, not dropped. Whether #10359 closes on this merge or stays open until #11025 lands is the PM's call, so this PR does not decide it.What was wrong
os g agent NAMEscaffolded a typedAI.Agentintosrc/agents/. ADR-0063 §2 (reversing ADR-0040 §3) withdrew that surface: the kernel ships exactly two agents,askandbuild, bound by surface, and the runtime catalog filters out every non-platform agent record. So the generated file parsed, passedos validate, published without complaint, and then never appeared. No error at any step. That is the silent-strip failure mode the #4001 campaign spent itself closing, arriving through the scaffolder instead of through a schema key.The retirement is loud, and it names the replacement
Deleting the roster entry alone would have moved the silence one step earlier rather than ending it:
Unknown type: agentplus the surviving roster tells an author their spelling is not on the list, and the natural next move is to hunt for the right spelling of something that no longer exists. Soagentmoves into aRETIRED_GENERATORSledger whose entries owe both halves — the decision that withdrew the surface, and the surface to author instead. Real output, from the CLI as a user runs it:Nothing is written — no
src/agents/, no barrel index.Every advertising site, swept — not repaired by line number
The card cited one doc row. There were six live sites, and the sweep found two the card did not name:
packages/cli/src/commands/generate.tsagententry inGENERATORS,defaultDir: 'src/agents'packages/cli/src/commands/generate.tstypeargument's own description string, listingagentin the rosterpackages/cli/README.mdAvailable generate types: … , 'agent', …content/docs/deployment/cli.mdxos g agent support # Generate an AI agentin theos generateexample blockcontent/docs/deployment/cli.mdxagent/src/agents/row of the Available types table (the row the card cited)content/docs/deployment/cli.mdxos g agent sales-assistantin the Typical Workflow block — not named anywhere on the cardSites 2 and 3 were found by grepping the roster strings rather than the command spelling; site 6 by sweeping
content/**for the command. Retiring the generator while any of them stood would have shipped documentation for a command that no longer exists — the same defect class this change is closing.content/docs/deployment/cli.mdxnow carries a retirement Callout in place of the row, so a reader arriving from an old tutorial gets the same explanation the CLI gives, rather than finding a silently shorter table.One bounded in-place repair, declared
content/docs/getting-started/quick-start.mdx's project-layout map — the first table a new reader meets — listed the AI area as "Agents, tools, RAG, MCP exposure (src/agents)", naming the withdrawn surface as the directory an app author writes AI metadata into. It now reads "Skills, tools, RAG, MCP exposure (src/skills)".This is one line outside the card's stated file surface, taken in place because all four conditions held: same defect class (advertising the ADR-0063-withdrawn agent-authoring surface, and specifically the very directory the retired generator wrote into); the correct shape is already pinned by existing repo evidence (
content/docs/ai/index.mdx,content/docs/ai/skills-reference.mdxandcontent/docs/concepts/metadata-lifecycle.mdxall state that agents are platform-owned and skills are the third-party extension primitive); no other PR holds that file (checked against the three open PRs); and it is inside the same docs gate family this PR already runs. Named here rather than folded in silently.ADR anchor
scripts/adr-anchors/packages__cli__src__commands__generate.ts.jsonpins ADR-0063 togenerate.ts, so the absence of anagentgenerator reads to the next author as a decision rather than an oversight — and so does the requirement that the refusal keep pointing at skills. Per Prime Directive #13.Non-vacuity — reverse-verified in both directions
packages/cli/test/generate-agent-retired.e2e.test.tsspawns the real CLI throughbin/run-dev.jsand asserts on a real child process's exit status and stdout, for the reasonsinvocation-loudness.e2e.test.tsdocuments (aprocess.exitCodeinside a vitest worker is not an exit status;utils/format.tsprints throughconsole.log). It pins the content of the refusal, not only the absence from the roster — a bare "unknown generator" passes every "the type is gone" assertion and fails this file.Predicted direction before running: red. Observed, with
packages/cli/src/commands/generate.tsrestored toorigin/mainand everything else left in place:The failure diff literally reproduces the defect —
✓ Created src/agents/support.ts/✓ Created src/agents/index.ts. The two survivors are the deliberate controls (the surviving roster still lists six types;os g object … --dry-runstill previews).Restored: 9 passed (9).
No rebuild was needed on either leg, and that is a property of this pair rather than an omission:
bin/run-dev.jsrunspackages/cli/src/**throughtsxdirectly, so the mutated subject is the source the spawn executes — there is nodist/between the edit and the measurement. The workspace closure the CLI's dependencies resolve through was built before both legs.Release discipline
minoron@objectstack/cli. This removes a published capability, sopatchwould understate it;majoris refused byscripts/check-changeset-no-major.mjs, whose header states the convention this follows — during the launch window breaking changes ship asminorunder pre-1.0 lockstep semantics. Same level as the closest precedent in the pending stock,http-request-errors-total-retired(a published-capability retirement).The changeset declares
**BREAKING**and leads with the consequence, so it reaches the release notes rather than being buried: a script, Makefile or CI step that runsos g agentnow exits 1. The ruling accepted that outcome (「响亮,但会报」).Retirement protocol / registry — does one apply here? No, and the changeset says so in writing rather than staying silent. The ADR-0087 disposition is
not-required (no-migration-prescription), verified green byscripts/check-adr-0087-registration.mjs. The reasoning: a CLI command name is an invocation surface, not authorable metadata — no authorable key, nosys_metadatarow, no schema to tombstone — so there is nothing forobjectstack migrate metato rewrite, nothing forspec-changes.jsonto project, and no FROM→TO spelling for the upgrade guide, because a skill is a different artifact rather than a renamed agent and no replacement command exists to rewrite the call into. That is also the difference fromhttp-request-errors-total-retired, where the ledger was the only channel (a Grafana panel silently drew a flat zero): here the command itself refuses at the point of use. Same reasoning shape as ADR-0087's D7 addendum, one surface over — there the compiler carries the notice, here the CLI does. Thespec-property-retirementskill is likewise out of scope: it governs authorablepackages/specproperties under ADR-0049 enforce-or-remove, and this diff touches nopackages/specfile.Related, deliberately out of scope here
ai/agents.mdxstill documentsagent.toolsas a live field — it was tombstoned in protocol 17 #10355 —content/docs/ai/agents.mdxdocumenting a tombstonedagent.tools. Same ADR-0063 docs family, different site. Triage asked for a linkage note rather than a repair; this is that note, and that site is untouched.os g skill— no scaffolder exists for the extension primitive ADR-0063 names #11025 — theos g skillscaffolder. Split out with reasons: the generator harness writesNAME.tswhile the registry'sfilePatternsfor every type are**/*.TYPE.ts, so a naive skill scaffold would produce a file the loader never matches (reproducing this card's own defect through its fix);toolsis required with no default;surfaceis lint-checked and ADR-0064-sensitive; and the obvious copy source —SkillSchema's own@example— is itself broken.skill.zod.ts's two@exampleblocks pass the tombstonedtriggerPhrasesand throw when run #11026 — that broken example. Both@exampleblocks inpackages/spec/src/ai/skill.zod.tspasstriggerPhrases, aretiredKey()tombstone that rejects on parse; verified by running one. Filed as a sub-issue of [finding] No gate type-checks TSDoc@exampleblocks inpackages/spec/src— the ADR-0033 channel is uncompiled #10924, the card about@exampleblocks inpackages/spec/srcbeing uncompiled — that gate cannot land green over this instance. Out of this card's file surface, so untouched here.Verification
Every line below is a verdict the gate printed, at
567a1a036c(this PR's head; the union was derived and run after the final commit). Exit codes were captured before any pipe.Gate family derived from the real change set —
node scripts/pm/dispatch-gates.mjswith no path arguments, so the 7 changed paths come from the merge base rather than from a hand-written list.pnpm --filter @objectstack/cli exec vitest runTest Files 153 passed (153)·Tests 1691 passed (1691)pnpm --filter @objectstack/cli typechecktsc --noEmit, exit 0, no diagnosticseslint(both changed TS files)errors= 0 warnings= 0on each, confirmed via--format jsonso the run is not vacuouspnpm check:type-check-debtcheck-type-check-coverage --re-measure: OK — 33 ledger entr(ies) re-measured in 262.0s, 1908 raw tsc error(s) total, none above its recorded number.— the new test file lands in this package's TEST_DEBT layer, recorded 146 "exactly, no bootstrap margin", and adds nothing to itpnpm check:type-check-coveragenode scripts/check-adr-0087-registration.mjs✓ 1 declared-breaking changeset(s), each carrying an ADR-0087 disposition.node scripts/check-changeset-no-major.mjsmajorbump.node scripts/check-empty-changeset.mjs✓ No empty-frontmatter changeset introduced by this diff (1 declaring changeset(s) added).pnpm check:changeset-gate-self-testspnpm check:objectui-changesetpnpm check:adr-anchorscheck-adr-anchors: OK (52 anchored file(s), every governing ADR still referenced; …)pnpm check:doc-anchors✅ 279 internal #fragment link(s) across 407 source file(s) all resolve to a real headingpnpm check:doc-authoring✓ doc authoring guard: 389 files clean — no bare metadata literals.pnpm check:docs-audit-scope✓ docs-accuracy-audit scope is in sync with content/docs/: 189 hand-written doc(s).pnpm check:docs-redirectscheck-docs-redirects: OK (apps/docs/redirects.mjs: 92 entries …)pnpm check:published-readme-links✓ 175 outbound link(s) across 60 published markdown file(s): … 102/102 relative target(s) found in the tree.node scripts/check-doc-frontmatter.mjs✓ 402 page(s) under content/docs parse with yaml@2.9.0 …pnpm check:role-wordpnpm --filter @objectstack/spec run check:skill-examples✅ 227 prose examples type-check against @objectstack/specpnpm --filter @objectstack/lint run check:doc-security-posture✅ 22 ObjectSchema.create example(s) in 191 marked block(s) across 189 docs file(s) carry an os validate-clean security posturepnpm --filter @objectstack/lint run check:doc-formula-expressionsnode scripts/docs-audit/check-affected-docs.mjs✓ affected-docs self-test: 339 cases pass.)pnpm --filter @objectstack/spec run check:liveness/check:empty-state/check:strictness-ledger/check:variant-docspnpm check:nul-bytescheck-nul-bytes: OK (scanned 6378 text file(s) … no raw ASCII control bytes).pnpm check:cross-package-test-inputsOK: 13 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.pnpm check:test-source-aliaspnpm check:engine-double-contract·check:where-matcher·check:query-options-erasurepnpm check:entry-guard·check:parse-guard·check:slot-lookup·check:type-source-resolution·check:ci-filter-parity·check:plugin-teardown-shapeNo gate was weakened: no threshold moved, no baseline raised, no test skipped or quarantined. The
check:type-check-debtrun reports one pre-existing surplus on@objectstack/plugin-auth(records 109, measures 97) that this PR neither created nor touches.Generated by Claude Code