From d6a1804c8d03cf90301b7c918e0049853217a3c3 Mon Sep 17 00:00:00 2001 From: admin Date: Wed, 23 Sep 2026 19:12:23 +0700 Subject: [PATCH 1/2] fix(onboarding): render user-typed skill names with the harness skill prefix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `{{INVOKE}}` is a shell command — `bun /tools/aidlc.ts` on a bun install, `aidlc` on a native one. The harness onboarding glued skill suffixes onto it, so a bun install told the reader to type `bun .claude/tools/aidlc.ts-session-cost`, which is not a command. `{{SKILL_INVOKE}}` already exists for this: renderOnboarding substitutes each harness's own `fills.invoke`, so the same line renders `/aidlc-session-cost` for the six harnesses that declare `/aidlc` and `$aidlc-session-cost` for Codex. No template used it. Move the eight command names onto it and leave the five real shell invocations on `{{INVOKE}}`. Copilot embeds the harness skeleton in its root AGENTS.md, so its shipped bytes change; its previously shipped hash moves into the manifest so a copy-channel install carrying it stays adoptable. The five harnesses that project the skeleton to a separate file ship identical AGENTS.md bytes and need no entry. Closes #1341 --- core/templates/onboarding-harness.md | 6 +++--- harness/copilot/manifest.ts | 3 +++ tests/unit/t151-onboarding-skeleton.test.ts | 19 +++++++++++++++++++ tests/unit/t243-install-mechanism.test.ts | 1 + 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/core/templates/onboarding-harness.md b/core/templates/onboarding-harness.md index 60f5d3f127..b69118410f 100644 --- a/core/templates/onboarding-harness.md +++ b/core/templates/onboarding-harness.md @@ -12,9 +12,9 @@ ## AI-DLC Structure - **Skill**: `{{HARNESS_DIR}}/skills/aidlc/` — Orchestrator (`SKILL.md`), stage protocol, and the stage files across the phase directories (the enabled set depends on the composed plugins: see the compiled `{{HARNESS_DIR}}/tools/data/stage-graph.json` or run `{{INVOKE}} --doctor`) -- **Document skill** (user-invocable): `{{HARNESS_DIR}}/skills/aidlc-knowledge/`, typed as `{{INVOKE}}-knowledge`; the framework CLI also exposes `{{INVOKE}} knowledge `. Also standalone — outside the lifecycle graph — but classified `read-write`, unlike the read-only session skills below: it changes the document catalog and emits document audit events. It never advances the workflow stage pointer and never approves a gate. See "Document knowledge" under "Where things live" in the project's root `AGENTS.md` (Claude Code and Copilot readers find it under "Shared AI-DLC onboarding" in this file). -- **Session skills** (read-only, user-invocable): `{{HARNESS_DIR}}/skills/aidlc-session-cost/`, `{{HARNESS_DIR}}/skills/aidlc-replay/`, `{{HARNESS_DIR}}/skills/aidlc-outcomes-pack/` — typed as `{{INVOKE}}-session-cost`, `{{INVOKE}}-replay`, `{{INVOKE}}-outcomes-pack`. Each pulls every count from `{{INVOKE}} engine runtime summary --json` (no LLM-side counting). Classified `read-only`: they never advance the workflow stage pointer and never emit audit events. `aidlc-session-cost` and `aidlc-replay` print to the terminal only; `aidlc-outcomes-pack` is the only one that writes a file (`OUTCOMES.md`). -- **Stage-runner skills** (user-invocable): `{{HARNESS_DIR}}/skills/aidlc-/` — one per runnable core stage, typed as `{{INVOKE}}-` (e.g. `{{INVOKE}}-domain-design`, `{{INVOKE}}-code-generation`); plugin-owned stages use their bare plugin-prefixed command name. Each runs that single stage in isolation via the engine's `--single` mode (`aidlc-orchestrate next --stage --single`) and **never advances your main workflow's `Current Stage`** — `next --single` records only the synthetic start boundary and `report --single` closes that same attempt. They are opt-in packaging: the same stage is reachable via `{{INVOKE}} --stage --single` without a runner. The runner set is generated from the compiled stage graph by `{{INVOKE}} engine gen runners` and kept in sync by its `check` drift guard, so adding a stage file and regenerating adds its runner. The three bootstrap **initialization** stages ship no per-stage runner (they have no standalone meaning); the whole initialization phase is packaged as `{{INVOKE}}-init`, which creates the first workflow record and its starting state in one step. (This is opt-in packaging: describing what to build normally sets up the first piece of work by itself — no separate initialization command is needed.) +- **Document skill** (user-invocable): `{{HARNESS_DIR}}/skills/aidlc-knowledge/`, typed as `{{SKILL_INVOKE}}-knowledge`; the framework CLI also exposes `{{INVOKE}} knowledge `. Also standalone — outside the lifecycle graph — but classified `read-write`, unlike the read-only session skills below: it changes the document catalog and emits document audit events. It never advances the workflow stage pointer and never approves a gate. See "Document knowledge" under "Where things live" in the project's root `AGENTS.md` (Claude Code and Copilot readers find it under "Shared AI-DLC onboarding" in this file). +- **Session skills** (read-only, user-invocable): `{{HARNESS_DIR}}/skills/aidlc-session-cost/`, `{{HARNESS_DIR}}/skills/aidlc-replay/`, `{{HARNESS_DIR}}/skills/aidlc-outcomes-pack/` — typed as `{{SKILL_INVOKE}}-session-cost`, `{{SKILL_INVOKE}}-replay`, `{{SKILL_INVOKE}}-outcomes-pack`. Each pulls every count from `{{INVOKE}} engine runtime summary --json` (no LLM-side counting). Classified `read-only`: they never advance the workflow stage pointer and never emit audit events. `aidlc-session-cost` and `aidlc-replay` print to the terminal only; `aidlc-outcomes-pack` is the only one that writes a file (`OUTCOMES.md`). +- **Stage-runner skills** (user-invocable): `{{HARNESS_DIR}}/skills/aidlc-/` — one per runnable core stage, typed as `{{SKILL_INVOKE}}-` (e.g. `{{SKILL_INVOKE}}-domain-design`, `{{SKILL_INVOKE}}-code-generation`); plugin-owned stages use their bare plugin-prefixed command name. Each runs that single stage in isolation via the engine's `--single` mode (`aidlc-orchestrate next --stage --single`) and **never advances your main workflow's `Current Stage`** — `next --single` records only the synthetic start boundary and `report --single` closes that same attempt. They are opt-in packaging: the same stage is reachable via `{{INVOKE}} --stage --single` without a runner. The runner set is generated from the compiled stage graph by `{{INVOKE}} engine gen runners` and kept in sync by its `check` drift guard, so adding a stage file and regenerating adds its runner. The three bootstrap **initialization** stages ship no per-stage runner (they have no standalone meaning); the whole initialization phase is packaged as `{{SKILL_INVOKE}}-init`, which creates the first workflow record and its starting state in one step. (This is opt-in packaging: describing what to build normally sets up the first piece of work by itself — no separate initialization command is needed.) - **Agents**: `{{HARNESS_DIR}}/agents/` — the base framework ships 14 agents: 11 domain-expert personas (product, design, delivery, architect, aws-platform, compliance, devsecops, developer, quality, pipeline-deploy, operations), 2 review-only agents (product-lead, architecture-reviewer), and the adaptive-workflows composer. A plugin install may add more; the enabled set is discovered from the files present under that directory. {{SLOT:agents_note}} - **Sensors**: `{{HARNESS_DIR}}/sensors/`: automatic checks that run on matching writes or once per existing deliverable at the approval gate. Gate-fired sensors may be advisory or blocking; blocking failures require an explicit audited override before the gate opens. Ships with framework defaults (`aidlc-claim-sources.md`, `aidlc-required-sections.md`, `aidlc-upstream-coverage.md`, `aidlc-traceability.md`, `aidlc-linter.md`, `aidlc-type-check.md`); forks may add custom `aidlc-.md` manifests. Stages declare which sensors fire via the frontmatter `sensors: []` list — a pull import resolved at compile time. - **Knowledge**: `{{HARNESS_DIR}}/knowledge/` — Methodology reference. Per-agent under `aidlc--agent/` subfolders; `aidlc-shared/` holds cross-agent material. Ships with framework. diff --git a/harness/copilot/manifest.ts b/harness/copilot/manifest.ts index cad5cf2f6c..093a327c83 100644 --- a/harness/copilot/manifest.ts +++ b/harness/copilot/manifest.ts @@ -73,6 +73,9 @@ const manifest: HarnessManifest = { "sha256:55b31ba55f6e7ebc47fe76a00039e2ec16e020503fb63791cbd8665438ff32ac", // The pre-Guards shipped variant (the onboarding gained its Guards section). "sha256:7a3a19981ba7a3c447b54eb0d0b1e96f8c9931687595967103cb5dfbb3c2b309", + // The pre-skill-prefix shipped variant (#1341: user-typed skill + // names rendered the shell invocation instead of the skill command). + "sha256:622ebad60ee4fed6a2a9811e7378ccbff6b76d651aaee00fd079b02471d8cf06", ], }, }, diff --git a/tests/unit/t151-onboarding-skeleton.test.ts b/tests/unit/t151-onboarding-skeleton.test.ts index e824887e24..eefd021d0e 100644 --- a/tests/unit/t151-onboarding-skeleton.test.ts +++ b/tests/unit/t151-onboarding-skeleton.test.ts @@ -60,6 +60,25 @@ describe("t151 neutral and native onboarding", () => { ]); }); + test("user-typed skill names carry the harness's skill prefix, not the shell invocation", () => { + // `{{INVOKE}}` is a shell command (`bun /tools/aidlc.ts`, or `aidlc` on + // a native install). Gluing a skill suffix onto it renders a command that + // does not exist; the skill prefix each harness declares is what a person + // types. Any `{{INVOKE}}-` is that mistake. + expect(HARNESS).not.toMatch(/\{\{INVOKE\}\}-/); + + const skills = ["session-cost", "replay", "outcomes-pack", "knowledge", "init"]; + for (const invoke of ["/aidlc", "$aidlc"]) { + const rendered = renderOnboarding(HARNESS, { invoke, slots: {} }) + .replaceAll("{{HARNESS_DIR}}", ".foo") + .replaceAll("{{INVOKE}}", "bun .foo/tools/aidlc.ts"); + for (const skill of skills) { + expect(rendered, `${invoke}-${skill}`).toContain(`${invoke}-${skill}`); + } + expect(rendered).not.toContain("aidlc.ts-"); + } + }); + test("a new harness gets complete onboarding without editing either skeleton", () => { const fills: OnboardingFills = { invoke: "@aidlc", diff --git a/tests/unit/t243-install-mechanism.test.ts b/tests/unit/t243-install-mechanism.test.ts index e3142bd9e5..bdcc07e501 100644 --- a/tests/unit/t243-install-mechanism.test.ts +++ b/tests/unit/t243-install-mechanism.test.ts @@ -5171,6 +5171,7 @@ describe("t243 projection channel", () => { "sha256:55b31ba55f6e7ebc47fe76a00039e2ec16e020503fb63791cbd8665438ff32ac", "sha256:7a3a19981ba7a3c447b54eb0d0b1e96f8c9931687595967103cb5dfbb3c2b309", "sha256:622ebad60ee4fed6a2a9811e7378ccbff6b76d651aaee00fd079b02471d8cf06", + "sha256:c4cf9e2412d16921b27820542fdc4876fc5b38d2fe179b4f43d4a0820e1c18b3", ], }, }; From aba29e783dea44c2eb87edc22fd21c0f2b9e62b9 Mon Sep 17 00:00:00 2001 From: Arden Packeer <2102737+apackeer@users.noreply.github.com> Date: Thu, 24 Sep 2026 22:00:29 +0000 Subject: [PATCH 2/2] fix(onboarding): point the stage and knowledge examples at commands that route Two more runtime examples in the harness skeleton rendered commands the CLI rejects. `{{INVOKE}} --stage --single` became `bun /tools/aidlc.ts --stage ...`, which fails with "unknown command '--stage'": `--stage --single` is an orchestrator-skill flag, so it now uses `{{SKILL_INVOKE}}`, matching the guide's `/aidlc --stage --single`. `{{INVOKE}} knowledge ` failed with "unknown command 'knowledge'": knowledge is an engine noun, so it now reads `{{INVOKE}} engine knowledge `, the form t293 and t326 drive. t151 pinned the broken knowledge form. It now pins `engine knowledge ` and ` --stage --single` for every harness in both channels, and guards the skeleton against either `{{INVOKE}}` form returning. Copilot's root AGENTS.md changes again; the previous current hash never shipped, so t243 swaps it instead of adding a legacy signature. --- core/templates/onboarding-harness.md | 4 ++-- tests/unit/t151-onboarding-skeleton.test.ts | 12 +++++++++++- tests/unit/t243-install-mechanism.test.ts | 2 +- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/core/templates/onboarding-harness.md b/core/templates/onboarding-harness.md index b69118410f..2b54a16d4d 100644 --- a/core/templates/onboarding-harness.md +++ b/core/templates/onboarding-harness.md @@ -12,9 +12,9 @@ ## AI-DLC Structure - **Skill**: `{{HARNESS_DIR}}/skills/aidlc/` — Orchestrator (`SKILL.md`), stage protocol, and the stage files across the phase directories (the enabled set depends on the composed plugins: see the compiled `{{HARNESS_DIR}}/tools/data/stage-graph.json` or run `{{INVOKE}} --doctor`) -- **Document skill** (user-invocable): `{{HARNESS_DIR}}/skills/aidlc-knowledge/`, typed as `{{SKILL_INVOKE}}-knowledge`; the framework CLI also exposes `{{INVOKE}} knowledge `. Also standalone — outside the lifecycle graph — but classified `read-write`, unlike the read-only session skills below: it changes the document catalog and emits document audit events. It never advances the workflow stage pointer and never approves a gate. See "Document knowledge" under "Where things live" in the project's root `AGENTS.md` (Claude Code and Copilot readers find it under "Shared AI-DLC onboarding" in this file). +- **Document skill** (user-invocable): `{{HARNESS_DIR}}/skills/aidlc-knowledge/`, typed as `{{SKILL_INVOKE}}-knowledge`; the framework CLI also exposes `{{INVOKE}} engine knowledge `. Also standalone — outside the lifecycle graph — but classified `read-write`, unlike the read-only session skills below: it changes the document catalog and emits document audit events. It never advances the workflow stage pointer and never approves a gate. See "Document knowledge" under "Where things live" in the project's root `AGENTS.md` (Claude Code and Copilot readers find it under "Shared AI-DLC onboarding" in this file). - **Session skills** (read-only, user-invocable): `{{HARNESS_DIR}}/skills/aidlc-session-cost/`, `{{HARNESS_DIR}}/skills/aidlc-replay/`, `{{HARNESS_DIR}}/skills/aidlc-outcomes-pack/` — typed as `{{SKILL_INVOKE}}-session-cost`, `{{SKILL_INVOKE}}-replay`, `{{SKILL_INVOKE}}-outcomes-pack`. Each pulls every count from `{{INVOKE}} engine runtime summary --json` (no LLM-side counting). Classified `read-only`: they never advance the workflow stage pointer and never emit audit events. `aidlc-session-cost` and `aidlc-replay` print to the terminal only; `aidlc-outcomes-pack` is the only one that writes a file (`OUTCOMES.md`). -- **Stage-runner skills** (user-invocable): `{{HARNESS_DIR}}/skills/aidlc-/` — one per runnable core stage, typed as `{{SKILL_INVOKE}}-` (e.g. `{{SKILL_INVOKE}}-domain-design`, `{{SKILL_INVOKE}}-code-generation`); plugin-owned stages use their bare plugin-prefixed command name. Each runs that single stage in isolation via the engine's `--single` mode (`aidlc-orchestrate next --stage --single`) and **never advances your main workflow's `Current Stage`** — `next --single` records only the synthetic start boundary and `report --single` closes that same attempt. They are opt-in packaging: the same stage is reachable via `{{INVOKE}} --stage --single` without a runner. The runner set is generated from the compiled stage graph by `{{INVOKE}} engine gen runners` and kept in sync by its `check` drift guard, so adding a stage file and regenerating adds its runner. The three bootstrap **initialization** stages ship no per-stage runner (they have no standalone meaning); the whole initialization phase is packaged as `{{SKILL_INVOKE}}-init`, which creates the first workflow record and its starting state in one step. (This is opt-in packaging: describing what to build normally sets up the first piece of work by itself — no separate initialization command is needed.) +- **Stage-runner skills** (user-invocable): `{{HARNESS_DIR}}/skills/aidlc-/` — one per runnable core stage, typed as `{{SKILL_INVOKE}}-` (e.g. `{{SKILL_INVOKE}}-domain-design`, `{{SKILL_INVOKE}}-code-generation`); plugin-owned stages use their bare plugin-prefixed command name. Each runs that single stage in isolation via the engine's `--single` mode (`aidlc-orchestrate next --stage --single`) and **never advances your main workflow's `Current Stage`** — `next --single` records only the synthetic start boundary and `report --single` closes that same attempt. They are opt-in packaging: the same stage is reachable via `{{SKILL_INVOKE}} --stage --single` without a runner. The runner set is generated from the compiled stage graph by `{{INVOKE}} engine gen runners` and kept in sync by its `check` drift guard, so adding a stage file and regenerating adds its runner. The three bootstrap **initialization** stages ship no per-stage runner (they have no standalone meaning); the whole initialization phase is packaged as `{{SKILL_INVOKE}}-init`, which creates the first workflow record and its starting state in one step. (This is opt-in packaging: describing what to build normally sets up the first piece of work by itself — no separate initialization command is needed.) - **Agents**: `{{HARNESS_DIR}}/agents/` — the base framework ships 14 agents: 11 domain-expert personas (product, design, delivery, architect, aws-platform, compliance, devsecops, developer, quality, pipeline-deploy, operations), 2 review-only agents (product-lead, architecture-reviewer), and the adaptive-workflows composer. A plugin install may add more; the enabled set is discovered from the files present under that directory. {{SLOT:agents_note}} - **Sensors**: `{{HARNESS_DIR}}/sensors/`: automatic checks that run on matching writes or once per existing deliverable at the approval gate. Gate-fired sensors may be advisory or blocking; blocking failures require an explicit audited override before the gate opens. Ships with framework defaults (`aidlc-claim-sources.md`, `aidlc-required-sections.md`, `aidlc-upstream-coverage.md`, `aidlc-traceability.md`, `aidlc-linter.md`, `aidlc-type-check.md`); forks may add custom `aidlc-.md` manifests. Stages declare which sensors fire via the frontmatter `sensors: []` list — a pull import resolved at compile time. - **Knowledge**: `{{HARNESS_DIR}}/knowledge/` — Methodology reference. Per-agent under `aidlc--agent/` subfolders; `aidlc-shared/` holds cross-agent material. Ships with framework. diff --git a/tests/unit/t151-onboarding-skeleton.test.ts b/tests/unit/t151-onboarding-skeleton.test.ts index eefd021d0e..b3ac19b5a8 100644 --- a/tests/unit/t151-onboarding-skeleton.test.ts +++ b/tests/unit/t151-onboarding-skeleton.test.ts @@ -79,6 +79,15 @@ describe("t151 neutral and native onboarding", () => { } }); + test("runtime examples on {{INVOKE}} name commands the CLI routes", () => { + // `--stage --single` is an orchestrator-skill flag the CLI rejects + // as an unknown command, and `knowledge` is an engine noun, so neither is + // reachable as a top-level `{{INVOKE}}` command. The shipped forms are pinned + // per harness and channel in "every harness ships complete onboarding". + expect(HARNESS).not.toMatch(/\{\{INVOKE\}\} --stage /); + expect(HARNESS).not.toMatch(/\{\{INVOKE\}\} knowledge /); + }); + test("a new harness gets complete onboarding without editing either skeleton", () => { const fills: OnboardingFills = { invoke: "@aidlc", @@ -127,8 +136,9 @@ describe("t151 neutral and native onboarding", () => { ); expect(setup, harness.name).not.toContain(`${fills.invoke} engine`); expect(setup, harness.name).toContain( - `${native ? "aidlc" : `bun ${harness.manifest.harnessDir}/tools/aidlc.ts`} knowledge `, + `${native ? "aidlc" : `bun ${harness.manifest.harnessDir}/tools/aidlc.ts`} engine knowledge `, ); + expect(setup, harness.name).toContain(`\`${fills.invoke} --stage --single\``); if (harness.manifest.onboarding?.harnessDst) { expect(root, harness.name).toBe(NEUTRAL); expect(setup, harness.name).not.toContain("## Where things live"); diff --git a/tests/unit/t243-install-mechanism.test.ts b/tests/unit/t243-install-mechanism.test.ts index bdcc07e501..6fdaff2fbb 100644 --- a/tests/unit/t243-install-mechanism.test.ts +++ b/tests/unit/t243-install-mechanism.test.ts @@ -5171,7 +5171,7 @@ describe("t243 projection channel", () => { "sha256:55b31ba55f6e7ebc47fe76a00039e2ec16e020503fb63791cbd8665438ff32ac", "sha256:7a3a19981ba7a3c447b54eb0d0b1e96f8c9931687595967103cb5dfbb3c2b309", "sha256:622ebad60ee4fed6a2a9811e7378ccbff6b76d651aaee00fd079b02471d8cf06", - "sha256:c4cf9e2412d16921b27820542fdc4876fc5b38d2fe179b4f43d4a0820e1c18b3", + "sha256:a25a15052889fe6b5900f0fef5262cc50cb00bb436e52f1eb1abe62db35b2f50", ], }, };