Skip to content

Commit 15b63e8

Browse files
Elon Muskclaude
andauthored
fix(cli): retire the agent generator — os g agent now names ADR-0063 and points at skills (#11028)
* fix(cli): retire the `agent` generator, and make the refusal name skills `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 * docs(changeset): the `agent` generator is retired — breaking, and loud 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 --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent ecd06f6 commit 15b63e8

7 files changed

Lines changed: 312 additions & 31 deletions

File tree

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
---
2+
"@objectstack/cli": minor
3+
---
4+
5+
fix(cli): **BREAKING** — the `agent` generator is retired, and `os g agent` now says why and points at skills (ADR-0063 §2, #10359)
6+
7+
**⛔ If a script, a Makefile or a CI step in your project runs `os g agent`, it
8+
will now exit 1.** That is the intended outcome and the one way this change can
9+
interrupt you: the command is gone, deliberately, and the failure is how you
10+
find out. Everything it used to produce was already being discarded — read on.
11+
12+
`minor`, not `major`: during the launch window this stack ships breaking changes
13+
as `minor` (pre-1.0 semantics under lockstep versioning — see
14+
`scripts/check-changeset-no-major.mjs`).
15+
16+
**What the command actually did.** `os g agent <name>` scaffolded a typed
17+
`AI.Agent` into `src/agents/`. Per ADR-0063 §2 (which reversed ADR-0040 §3) the
18+
kernel ships exactly **two** agents — `ask` and `build` — bound by surface and
19+
never picked from a roster, and the runtime catalog **filters out every
20+
non-platform agent record**. So the scaffolded file parsed, passed
21+
`os validate`, published without complaint, and then never appeared anywhere.
22+
No error at any step. An author who followed the documented example got a file,
23+
a green validate, a successful publish, and nothing to show for it.
24+
25+
**Why the roster entry was not simply deleted.** A deleted type falls through to
26+
`Unknown type: agent` plus a list of what is left, which tells the author their
27+
spelling is not on the list and invites them to hunt for the right spelling of
28+
something that no longer exists — the same silence, one step earlier. `agent` is
29+
now a **retirement ledger entry** instead, and the refusal carries both halves:
30+
the decision that withdrew the surface, and the surface to author in its place.
31+
What you see:
32+
33+
```
34+
✗ `os g agent` was retired — agents are platform-internal (ADR-0063 §2).
35+
36+
The kernel ships exactly two agents, `ask` and `build`, bound by surface.
37+
An agent you author still parses and still publishes — and the runtime
38+
catalog then filters it out, so it never appears and nothing tells you.
39+
This command scaffolded exactly that file, so it is retired, not repaired.
40+
41+
Author a SKILL instead. Skills (plus tools / MCP) are the third-party
42+
extension primitive ADR-0063 names — the live surface this one was not.
43+
44+
There is no `os g skill` scaffolder yet. Write the file by hand:
45+
46+
src/skills/<name>.skill.ts
47+
import { defineSkill } from '@objectstack/spec/ai';
48+
49+
Docs: https://objectstack.ai/docs/ai/agents
50+
```
51+
52+
**There is no command to rewrite the call into.** A skill is a different
53+
artifact with a different schema, not a renamed agent, and `os g skill` does not
54+
exist yet — the skill scaffolder is tracked separately, and this message says so
55+
rather than promising it. Delete the call; author the skill file by hand.
56+
57+
The generator roster is now `object`, `view`, `action`, `flow`, `dashboard`,
58+
`app`. The docs that advertised the retired one — the `os g agent support`
59+
example, the `agent` / `src/agents/` row of the Available types table, and
60+
`os g agent sales-assistant` in the Typical Workflow block — are gone from
61+
`content/docs/deployment/cli.mdx`, which carries the retirement note instead;
62+
`packages/cli/README.md`'s type roster follows. The quick-start project-layout
63+
map, which listed `src/agents` as the directory an app author writes AI metadata
64+
into, now names `src/skills`.
65+
66+
<!-- adr-0087: not-required (no-migration-prescription) A CLI COMMAND NAME is an invocation surface, not authorable metadata. There is no authorable key, no `sys_metadata` row and no schema to tombstone here, so there is nothing for `objectstack migrate meta` to rewrite, nothing for `spec-changes.json` to project and no FROM -> TO spelling for the upgrade guide to carry: a skill is a different artifact rather than a renamed agent, and no replacement command exists to rewrite the call into. Nor is the ledger the only notification channel this time, which is the difference from `http-request-errors-total-retired` (where an operator's Grafana panel silently drew a flat zero and the entry was the sole way to say so): the command itself now refuses, exits 1, names ADR-0063 and points at skills at the exact moment and place of use. Same reasoning shape as ADR-0087's D7 addendum, one surface over — there the compiler carries the notice, here the CLI does. -->

content/docs/deployment/cli.mdx

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,7 +1027,6 @@ os g object customer # Generate a Customer object
10271027
os g view customer # Generate a Customer list view
10281028
os g action approve # Generate an action
10291029
os g flow customer # Generate an automation flow
1030-
os g agent support # Generate an AI agent
10311030
os g dashboard sales # Generate a dashboard
10321031
os g app crm # Generate an app definition
10331032

@@ -1043,10 +1042,23 @@ os g object task --dry-run # Preview without writing
10431042
| `view` | `src/views/` | List or form view definition |
10441043
| `action` | `src/actions/` | Button or batch action |
10451044
| `flow` | `src/flows/` | Automation flow |
1046-
| `agent` | `src/agents/` | AI agent |
10471045
| `dashboard` | `src/dashboards/` | Analytics dashboard |
10481046
| `app` | `src/apps/` | Application navigation |
10491047

1048+
<Callout type="warn" title="`os g agent` is retired">
1049+
There is no `agent` type. Running `os g agent <name>` fails with a message
1050+
naming [ADR-0063](https://github.com/objectstack-ai/objectstack/blob/main/docs/adr/0063-two-kernel-agents-skills-are-the-extension-primitive.md)
1051+
and pointing at skills, rather than the generic "unknown type" listing.
1052+
1053+
Agents are **platform-internal**: the kernel ships exactly two (`ask` and
1054+
`build`), and the runtime catalog filters out every other agent record. A
1055+
scaffolded `src/agents/*.ts` therefore passed `os validate`, published without
1056+
complaint, and never appeared — silently. Skills (plus tools / MCP) are the
1057+
third-party extension primitive, authored as `src/skills/<name>.skill.ts` with
1058+
`defineSkill`; see [AI Agents](/docs/ai/agents). There is no `os g skill`
1059+
scaffolder yet, so write that file by hand for now.
1060+
</Callout>
1061+
10501062
**Options:**
10511063
- `-d, --dir <directory>` — Override target directory
10521064
- `--dry-run` — Preview without writing files
@@ -1583,7 +1595,6 @@ os g object opportunity
15831595

15841596
# 3. Add business logic
15851597
os g flow lead-qualification
1586-
os g agent sales-assistant
15871598

15881599
# 4. Validate everything
15891600
os validate

content/docs/getting-started/quick-start.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ a project:
3737
| **Automation** | Flows, workflows, triggers, approvals (`src/flows`) | Event handlers, approval chains, scheduled jobs |
3838
| **Interface** | Views, apps, dashboards, actions (`src/views`, `src/apps`) | The Console UI, navigation, responsive layout |
3939
| **Access** | Roles, permissions, sharing, row-level security | Middleware, RLS policies, field masking |
40-
| **AI** | Agents, tools, RAG, MCP exposure (`src/agents`) | Chat, search indexes, an MCP server |
40+
| **AI** | Skills, tools, RAG, MCP exposure (`src/skills`) | Chat, search indexes, an MCP server |
4141

4242
The through-line: **you author intent once as metadata, and the runtime derives the
4343
database, the API, the UI, and the AI tool surface from it.** That's why an agent

packages/cli/README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,13 @@ os compile
5959
| `os generate <type> <name>` | Generate metadata files (alias: `os g`) |
6060
| `os create <type> [name]` | Create a new package/plugin/example from template |
6161

62-
Available generate types: `object`, `view`, `action`, `flow`, `agent`, `dashboard`, `app`
62+
Available generate types: `object`, `view`, `action`, `flow`, `dashboard`, `app`
63+
64+
`agent` is **retired** (ADR-0063 §2): agents are platform-internal, so a scaffolded
65+
`src/agents/*.ts` validated, published and was then filtered out of the runtime
66+
catalog without a word. `os g agent` now says so and points at skills — the
67+
third-party extension primitive — which are authored as `src/skills/<name>.skill.ts`
68+
with `defineSkill`. There is no `os g skill` scaffolder yet.
6369

6470
### Cloud — publish & install
6571

packages/cli/src/commands/generate.ts

Lines changed: 67 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Args, Command, Flags } from '@oclif/core';
44
import chalk from 'chalk';
55
import fs from 'fs';
66
import path from 'path';
7-
import { printHeader, printSuccess, printError, printInfo, printStep, createTimer } from '../utils/format.js';
7+
import { printHeader, printSuccess, printError, printInfo, printStep, createTimer, CLI_ALIAS } from '../utils/format.js';
88

99
// ─── Metadata Type Templates ────────────────────────────────────────
1010

@@ -125,30 +125,6 @@ export default ${toCamelCase(name)}Flow;
125125
`,
126126
},
127127

128-
agent: {
129-
description: 'AI agent',
130-
defaultDir: 'src/agents',
131-
generate: (name: string) => `import * as AI from '@objectstack/spec/ai';
132-
133-
/**
134-
* ${toTitleCase(name)} Agent
135-
*/
136-
const ${toCamelCase(name)}Agent: AI.Agent = {
137-
name: '${toSnakeCase(name)}_agent',
138-
label: '${toTitleCase(name)} Agent',
139-
role: '${toTitleCase(name)} assistant',
140-
instructions: 'You are a helpful ${toTitleCase(name).toLowerCase()} assistant.',
141-
model: {
142-
provider: 'openai',
143-
model: 'gpt-4o',
144-
},
145-
tools: [],
146-
};
147-
148-
export default ${toCamelCase(name)}Agent;
149-
`,
150-
},
151-
152128
dashboard: {
153129
description: 'Analytics dashboard',
154130
defaultDir: 'src/dashboards',
@@ -189,6 +165,58 @@ export default ${toCamelCase(name)}App;
189165
},
190166
};
191167

168+
// ─── Retired Generators ─────────────────────────────────────────────
169+
170+
/**
171+
* Scaffolder types that were withdrawn, and what this command says when one
172+
* of them is run.
173+
*
174+
* A retired type is NOT an unknown type, and deliberately does not fall
175+
* through to the `Unknown type:` branch in {@link runMetadataGeneration}.
176+
* That branch prints the surviving roster and nothing else, so an author
177+
* arriving from a doc page, a tutorial or a CI script that still names the
178+
* retired type would learn only that their spelling is not on the list —
179+
* and the natural next move is to hunt for the right spelling of something
180+
* that no longer exists.
181+
*
182+
* `agent` (ADR-0063 §2, which reversed ADR-0040 §3): the kernel ships exactly
183+
* two agents, `ask` and `build`, bound by surface and never picked from a
184+
* roster. Tenant / app-package agents were withdrawn, and the runtime catalog
185+
* filters out every non-platform agent record. The file this generator wrote
186+
* into `src/agents/` therefore passed `os validate`, published without
187+
* complaint, and was then dropped on the floor: no error at any step, the
188+
* agent simply never appeared. Retiring the command silently would have moved
189+
* that silence one step earlier instead of ending it, which is why each entry
190+
* owes both halves — the decision that withdrew the surface, and the surface
191+
* to author instead.
192+
*/
193+
const RETIRED_GENERATORS: Record<string, {
194+
/** Reason clause completing "`os g <type>` was retired — …". */
195+
reason: string;
196+
/** Body lines, printed in order; an empty string prints a blank line. */
197+
detail: string[];
198+
}> = {
199+
agent: {
200+
reason: 'agents are platform-internal (ADR-0063 §2).',
201+
detail: [
202+
'The kernel ships exactly two agents, `ask` and `build`, bound by surface.',
203+
'An agent you author still parses and still publishes — and the runtime',
204+
'catalog then filters it out, so it never appears and nothing tells you.',
205+
'This command scaffolded exactly that file, so it is retired, not repaired.',
206+
'',
207+
'Author a SKILL instead. Skills (plus tools / MCP) are the third-party',
208+
'extension primitive ADR-0063 names — the live surface this one was not.',
209+
'',
210+
'There is no `os g skill` scaffolder yet. Write the file by hand:',
211+
'',
212+
' src/skills/<name>.skill.ts',
213+
" import { defineSkill } from '@objectstack/spec/ai';",
214+
'',
215+
'Docs: https://objectstack.ai/docs/ai/agents',
216+
],
217+
},
218+
};
219+
192220
// ─── Helpers ────────────────────────────────────────────────────────
193221

194222
function toCamelCase(str: string): string {
@@ -307,6 +335,19 @@ function generateTypesFromConfig(config: Record<string, unknown>): string {
307335
async function runMetadataGeneration(type: string, name: string, flags: { dir?: string; dryRun?: boolean }): Promise<void> {
308336
printHeader('Generate');
309337

338+
// A withdrawn type answers for itself, ahead of the roster lookup — see
339+
// RETIRED_GENERATORS for why "unknown type" is the wrong answer here.
340+
const retired = RETIRED_GENERATORS[type];
341+
if (retired) {
342+
printError(`\`${CLI_ALIAS} g ${type}\` was retired — ${retired.reason}`);
343+
console.log('');
344+
for (const line of retired.detail) {
345+
console.log(line ? chalk.dim(` ${line}`) : '');
346+
}
347+
console.log('');
348+
process.exit(1);
349+
}
350+
310351
const generator = GENERATORS[type];
311352
if (!generator) {
312353
printError(`Unknown type: ${type}`);
@@ -874,7 +915,7 @@ export default class Generate extends Command {
874915
static override aliases = ['g'];
875916

876917
static override args = {
877-
type: Args.string({ description: 'Metadata type to generate (object, view, action, flow, agent, dashboard, app)', required: true }),
918+
type: Args.string({ description: 'Metadata type to generate (object, view, action, flow, dashboard, app)', required: true }),
878919
name: Args.string({ description: 'Name for the metadata (use kebab-case)', required: false }),
879920
};
880921

0 commit comments

Comments
 (0)