@sabaiway/agent-workflow-kit 10.3.0 → 10.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +85 -0
- package/README.md +5 -5
- package/SKILL.md +1 -1
- package/bridges/antigravity-cli-bridge/SKILL.md +7 -1
- package/bridges/antigravity-cli-bridge/bin/agy-review.sh +69 -17
- package/bridges/antigravity-cli-bridge/bin/agy-review.test.mjs +73 -2
- package/bridges/antigravity-cli-bridge/capability.json +2 -2
- package/bridges/antigravity-cli-bridge/references/review-prompt.md +3 -0
- package/bridges/codex-cli-bridge/SKILL.md +8 -1
- package/bridges/codex-cli-bridge/bin/codex-exec.sh +1 -1
- package/bridges/codex-cli-bridge/bin/codex-review-honesty.test.mjs +1 -1
- package/bridges/codex-cli-bridge/bin/codex-review.sh +89 -18
- package/bridges/codex-cli-bridge/bin/codex-review.test.mjs +55 -2
- package/bridges/codex-cli-bridge/capability.json +2 -2
- package/capability.json +1 -1
- package/package.json +1 -1
- package/references/agents/executor.md +40 -0
- package/references/agents/review-lens.md +5 -3
- package/references/modes/agents.md +9 -4
- package/references/modes/procedures.md +21 -8
- package/references/modes/recipes.md +7 -4
- package/references/modes/recommendations.md +3 -1
- package/references/modes/set-recipe.md +23 -6
- package/references/modes/status.md +2 -2
- package/references/modes/upgrade.md +1 -1
- package/references/modes/velocity.md +1 -0
- package/references/shared/composition-handoff.md +1 -1
- package/references/shared/deploy-tail.md +1 -1
- package/references/templates/orchestration.json +1 -1
- package/tools/autonomy-config.mjs +1 -1
- package/tools/bridge-posture.mjs +48 -0
- package/tools/carriers.mjs +152 -0
- package/tools/cheap-agents-read.mjs +234 -0
- package/tools/cheap-agents.mjs +101 -109
- package/tools/commands.mjs +3 -3
- package/tools/detect-backends.mjs +2 -2
- package/tools/direct-run.mjs +9 -0
- package/tools/family-registry.mjs +38 -18
- package/tools/flow-check.mjs +2 -7
- package/tools/fold-scope.mjs +5 -60
- package/tools/grounding.mjs +2 -2
- package/tools/inject-methodology.mjs +4 -0
- package/tools/orchestration-config.mjs +23 -61
- package/tools/orchestration-readme.mjs +70 -0
- package/tools/plan-shape-cli.mjs +112 -0
- package/tools/plan-shape-facts.mjs +204 -0
- package/tools/plan-shape.mjs +348 -0
- package/tools/procedures.mjs +197 -83
- package/tools/recipes.mjs +183 -230
- package/tools/recommendations.mjs +77 -11
- package/tools/renderers.mjs +27 -7
- package/tools/repo-lex.mjs +40 -0
- package/tools/review-roster-resolve.mjs +104 -0
- package/tools/review-roster.mjs +128 -0
- package/tools/review-rounds-cli.mjs +92 -0
- package/tools/review-rounds.mjs +115 -0
- package/tools/review-state.mjs +10 -11
- package/tools/set-recipe-roster.mjs +167 -0
- package/tools/set-recipe.mjs +138 -42
- package/tools/velocity-profile.mjs +8 -22
- package/tools/view-model.mjs +17 -3
|
@@ -2,19 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- opt-in-capability: none — an inspection surface that configures nothing -->
|
|
4
4
|
|
|
5
|
-
Read-only **orchestration advisor**. Answers *"how should I compose the optional execution-backends into plan → execute → review here, and which recipe fits?"* It **never writes, never commits, never runs a subscription CLI, and never executes a recipe** — the orchestrator (you) runs the chosen recipe through the bridge skills and makes the single commit; a backend is advisory or delegated, never autonomous.
|
|
5
|
+
Read-only **orchestration advisor**. Answers *"how should I compose the carriers of a step — the optional execution-backends and the full-tool executor subagent — into plan → execute → review here, and which recipe fits?"* It **never writes, never commits, never runs a subscription CLI, and never executes a recipe** — the orchestrator (you) runs the chosen recipe through the bridge skills or the executor vehicle and makes the single commit; a backend or a subagent is advisory or delegated, never autonomous.
|
|
6
6
|
|
|
7
|
-
The
|
|
7
|
+
The five recipes (defined over each bridge's `provides` roles — `codex`: execute + review; `agy`: review + probe — plus the `carry` role the placed **executor vehicle** provides), canonical narrative in the **installed engine** (`references/orchestration.md`):
|
|
8
8
|
|
|
9
9
|
- **Solo** — you plan, execute, and self-review; no backend (always available; the floor).
|
|
10
10
|
- **Reviewed** — you execute; **one** backend reviews the result (advisory). Prefers `codex` when both are ready (`agy` carries a standing health caveat).
|
|
11
11
|
- **Council** — **both** backends review independently; you synthesize the two opinions.
|
|
12
12
|
- **Delegated** — you hand a **bounded** execution sub-task to a backend (`codex exec`), then review the returned diff and commit.
|
|
13
|
+
- **Subagent** — a **full-tool frontier subagent**, dispatched from the placed executor vehicle (`.claude/agents/executor.md`), carries a **bounded, file-disjoint** slice — of execution, of plan/contract authoring, of an authoring fold, or of routine work; you verify the returned slice by running its suites yourself and you make the commit. It is **never a review backend**, **never a bridge substitute**, and is **never told to commit**; it **degrades to Solo** when the vehicle is `missing` or `unusable`, with the reason. Readiness here is the **vehicle FILE, never the host** — a **Claude Code lane**, like the `agents` writer itself: on a host that cannot dispatch the vehicle, the rendered dispatch form is an instruction you follow by hand and report as such, never as a subagent dispatch.
|
|
13
14
|
|
|
14
|
-
1. Run **`node ${CLAUDE_SKILL_DIR}/tools/recipes.mjs`** (the read-only planner; `--json` for the structured form). It runs the backend detector, lists the
|
|
15
|
+
1. Run **`node ${CLAUDE_SKILL_DIR}/tools/recipes.mjs`** (the read-only planner; `--json` for the structured form). It runs the backend detector, lists the five recipes, and prints — for the current environment — a **per-recipe dispatch plan that degrades with a stated reason** when a carrier isn't `ready` (Council → Reviewed → Solo; Delegated → Solo; Subagent → Solo when the executor vehicle is `missing`/`unusable`), plus advisory **quota/health notes** (prefer the cheapest model; Council spends two backends' quota; `agy` may stall on substantive prompts — Issue-001, prefer `codex`).
|
|
15
16
|
2. **Offer the choice** via **`AskUserQuestion` where your agent supports it** (`AskUserQuestion` in Claude Code) — one option per recipe, the `recommendRecipe` choice listed **first** — otherwise in prose. Then print `planRecipe(chosen, detection)` (the per-stage dispatch + degradation reasons + quota/health notes) so the user sees exactly what running it entails.
|
|
16
17
|
3. **Availability = `readiness === ready`, full stop.** Every other readiness supplies the human reason (needs-skill → "not installed — `/agent-workflow-kit setup`"; needs-cli → "install the CLI"; needs-credentials → "log in"; degraded → "wrapper not on PATH — `/agent-workflow-kit setup`"). This is set-up state only — **never** a claim that a backend's service is responsive (the detector cannot observe a runtime stall; `agy`'s Issue-001 is a *standing advisory*, not a readiness signal).
|
|
17
18
|
|
|
19
|
+
**The activities and their slots.** One registry (`tools/carriers.mjs`, re-exported by `recipes.mjs`) names **three** activities and their typed slots: `plan-authoring` → `author`, `fold`, `review`; `plan-execution` → `execute`, `review`; `routine` → `carrier`, `parallel`. Each slot's accepted values follow its TYPE — `review` accepts `solo|reviewed|council`; `execute` accepts `solo|delegated|subagent`; `carrier` accepts `solo|subagent`. **`parallel` is a flag (`on|off`), not a recipe:** it resolves OUTSIDE the recipe lattice, never degrades, and never reaches the planner. A silent slot takes its **computed default** — `solo` for `author`, `fold`, `execute` and `carrier` (placing the executor vehicle never flips a default; the subagent carrier is opt-in exactly as `delegated` is), `reviewed`/`solo` by readiness for `review`, and `on` for `parallel`.
|
|
20
|
+
|
|
18
21
|
**The configured-recipe line (`--active-line`, read-only).** `node ${CLAUDE_SKILL_DIR}/tools/recipes.mjs --active-line` prints exactly **one** machine-composed line: the **CONFIGURED** recipe of every activity/slot, resolved from the target project's `docs/ai/orchestration.json` (read from the current directory) + live readiness — each slot with its source (configured vs computed default), its degradation stated, and its dispatched wrapper set — explicitly contrasted with the readiness **recommendation** (which is informational; the configured recipes are what runs). Paste it verbatim: it fills the session-start discovery step (the deployed `agent_rules.md` §1.1) and the handover "Active recipes:" slot; `set-recipe` echoes the same line after every successful write. A malformed config fails loud (exit 1), never a silent fallback.
|
|
19
22
|
|
|
20
|
-
**Invariants:** read-only · never runs a subscription CLI · never commits · the orchestrator executes the recipe via the bridge skills, not the kit.
|
|
23
|
+
**Invariants:** read-only · never runs a subscription CLI · never commits · the orchestrator executes the recipe via the bridge skills or the executor vehicle, not the kit.
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
Requires: ${CLAUDE_SKILL_DIR}/references/shared/report-footer.md
|
|
6
6
|
|
|
7
|
-
The **read-only deployment advisor** — the deterministic section every `upgrade` run ends with, also invocable on its own. It computes what in THIS deployment is configured sub-optimally (allowlist not seeded, autonomy render drifted, sandbox unavailable, gates undeclared, a declared gate matrix that verifies nothing, bridge friction, sandbox-mask clutter, an unacknowledged sandbox recipe) and renders **verdict-first**: one composed verdict line, then each item as **{severity · what is sub-optimal · the benefit in ONE plain line · an optional `recipe:` line (the `sandbox-lane` live recipe — egress hosts + resolved writable dirs — the `worktrees-dir` hand-apply-first grant advice, or the `agents` hidden-mode reconcile follow-up) · the exact consent-gated apply one-liner}**. The tool computes deterministic English DATA; **you PRESENT the section in the user's conversational language** — every fact, count and item from the tool, nothing added or dropped; commands, paths, hosts and rule strings stay **byte-exact**; show the raw tool block on request (the AD-032 report-contract lane — the tool cannot know the dialogue language, so the language rendering is your presentation layer).
|
|
7
|
+
The **read-only deployment advisor** — the deterministic section every `upgrade` run ends with, also invocable on its own. It computes what in THIS deployment is configured sub-optimally (allowlist not seeded, autonomy render drifted, sandbox unavailable, gates undeclared, a declared gate matrix that verifies nothing, bridge friction, sandbox-mask clutter, an unacknowledged sandbox recipe, a configured `subagent` carrier whose executor vehicle cannot carry it) and renders **verdict-first**: one composed verdict line, then each item as **{severity · what is sub-optimal · the benefit in ONE plain line · an optional `recipe:` line (the `sandbox-lane` live recipe — egress hosts + resolved writable dirs — the `worktrees-dir` hand-apply-first grant advice, or the `agents` / `executor-vehicle` hidden-mode reconcile follow-up) · the exact consent-gated apply one-liner}**. The tool computes deterministic English DATA; **you PRESENT the section in the user's conversational language** — every fact, count and item from the tool, nothing added or dropped; commands, paths, hosts and rule strings stay **byte-exact**; show the raw tool block on request (the AD-032 report-contract lane — the tool cannot know the dialogue language, so the language rendering is your presentation layer).
|
|
8
8
|
|
|
9
9
|
**Live host/session facts are tool-composed only.** Every fact this section states about the current
|
|
10
10
|
host or session — prompts fired, sandbox scope, whether a bypass was needed, network reachability,
|
|
@@ -20,6 +20,8 @@ Run `node ${CLAUDE_SKILL_DIR}/tools/recommendations.mjs --cwd <project-root> [--
|
|
|
20
20
|
3. **The apply-through-agent lane — an explicit informed-consent checkpoint:** after presenting, OFFER to apply; the user selects items in plain language; for each selected item you **surface its posture note inline** where one exists (the per-item notes below cover exactly the risk-marked keys; for `sandbox-lane` the note INCLUDES the sandbox-lanes ladder — present the whole ladder inline at the consent moment, never as a bare pointer). The user **explicitly confirms**, and **only then** — **no command runs before confirmation** — do you run the rendered command. Do NOT infer safety from the presence or absence of an `--apply` flag: most items' rendered command IS the mutation and completes on that one run, **including a no-`--apply` mutation** such as `family-freshness`'s `npx … init`. Some items instead render a **dry-run preview** that changes nothing and prints an explicit follow-up `--apply`/mutating command to run NEXT (e.g. `sandbox-lane`'s ack-write — a NEUTRAL recipe fingerprint into the family-owned `docs/ai/acks.json`, never a security key — and the `gates-declaration` seeder): after the SAME confirmation you run that printed follow-up command — no second ask, no improvisation (each writer keeps its own consent semantics: previews, `--apply` flags, refusals). An item marked **HAND-APPLY** (e.g. the `read-lane` stale/missing arms) is **never run by you and never written by the kit** — hand the user its rendered line together with its posture note; HAND-APPLY is **maintainer territory wherever it renders** — the apply slot or the `recipe:` line. For `worktrees-dir` the two classes COMPOSE in one item and the ORDER is part of the contract: its `recipe:` line is the HAND-APPLY grant advice, its apply one-liner is the dry-run ack preview, and you **wait for the maintainer to confirm the grant is applied (or the terminal fallback chosen)** before running that preview and the exact `--apply` command it prints — the ack RECORDS a choice that must already have been made, and against a trusted host NO the item renders no ack at all.
|
|
21
21
|
4. Registry strings (benefits + item texts) are frozen tool data, fact-true, ONE line under the pinned shape cap — posture/risk detail lives in the notes below at the consent moment, never inline in the overview. The dual velocity+security wording (`safer — blast radius bounded by the OS sandbox, not human attention`) rides ONLY the items with a real security delta (the autonomy render, the sandbox provisioning); the bridge-wrappers item claims **velocity only**.
|
|
22
22
|
|
|
23
|
+
**The `executor-vehicle` item (the carrier the project already CONFIGURED — beside the `agents` offer, never the same item).** It fires on ONE pair of facts: a slot in `docs/ai/orchestration.json` names `subagent` while the executor vehicle (`.claude/agents/executor.md`) is `missing` or `unusable` — so every such slot silently degrades to solo. That is a configured declaration that is broken, hence **`attention`**, not an offer; its text names the state, its reason and how many configured slots are affected across the three activities. **`placed` and `customized` render NOTHING** (a working vehicle is not a recommendation), a config naming no `subagent` slot renders nothing, and a config the validated reader refuses is a stated **skip**, never an item computed over a shape nothing accepted. It is deliberately DISTINCT from its two neighbours, neither of which can observe this state: the `agents` offer converges the moment nothing is left to PLACE — which a present-but-unusable executor also satisfies — and the review-recipe probe skips a slot configured `subagent` by construction. Its apply is the agents writer's own `cheap-agents.mjs --apply` (the same writer the `agents` item previews; here the rendered line IS the mutation, so read the item's state before confirming), and its `recipe:` line is the hidden-mode reconcile follow-up (`hide-footprint --reconcile`), for a hidden deployment only. Honest limit on convergence: the apply places an ABSENT vehicle, so it clears `missing`; it never overwrites a diverged file and STOPs on a symlink, so on `unusable` the item's apply line carries a `HAND-APPLY:` precondition (fix or remove the file, then run the writer) and an `unusable` vehicle converges only after the maintainer fixes or deletes the file the reason names (the delete-to-reseed the agents mode documents) and applies then. Risk profile: create-only writes under `.claude/agents/` — never `settings.json`, never a commit.
|
|
24
|
+
|
|
23
25
|
**Per-item posture notes (the consent moment — surface BEFORE running or handing over the apply):**
|
|
24
26
|
|
|
25
27
|
- `sandbox-lane` — surface this note TOGETHER with the sandbox-lanes ladder below (the ladder IS the practical half of the note — inline, never a pointer). Pure DISCOVERABILITY: it surfaces the manifest-declared observed session-sandbox recipe (egress hosts ∪ resolved writable state dirs — `networkHosts` ∪ `writableDirs` of the wired bridges' `capability.json`, the single documentation source) and converges on a NEUTRAL fingerprint acknowledgement recorded by the consent-gated **ack writer** into the family-owned `docs/ai/acks.json` (`sandboxLaneAck`; a changed recipe re-fires the item). The store is family-owned so no host settings validator guards it (AD-055 relocated the ack off the Claude Code settings schema, which rejected the unknown key); the legacy `"agentWorkflow": { "sandboxLaneAck": … }` settings-scope key is still READ for one deprecation window (until the next kit MAJOR). It never claims the settings security keys take effect on any host class, never recommends writing them, and the kit never seeds `sandbox.network.allowedDomains` / `sandbox.filesystem.allowWrite` (bridge council 2026-07-11, both backends concur: a network pre-allow widens egress for EVERY sandboxed command; a write allowance on CLI state dirs would expose credential dirs). Posture history: an IDE-managed session sandbox was live-observed (2026-07-11/12) ignoring hand-applied settings security keys in BOTH scopes, and codex needs a writable HOME (EROFS `~/.codex` in-sandbox); whether a session's sandbox honors the settings keys is runtime-unknowable from the advisor (a denial-only signal) — which is exactly why the item states only detectable facts and no zero-prompt promise on any host class.
|
|
@@ -7,22 +7,39 @@ The **config writer** for `docs/ai/orchestration.json` — the answer to *"set m
|
|
|
7
7
|
|
|
8
8
|
**Map the user's plain language → explicit ops** (the kit ships no NL parser; it performs no `all`-magic, so you expand scope explicitly, asking when unclear):
|
|
9
9
|
|
|
10
|
-
The intents are listed in English; a user speaking any other language expresses the same
|
|
10
|
+
The intents are listed in English; a user speaking any other language expresses the same ones, and
|
|
11
11
|
mapping their wording onto them is your job, not a shipped phrase list.
|
|
12
12
|
|
|
13
13
|
| user means | op | scope |
|
|
14
14
|
|---|---|---|
|
|
15
15
|
| "both review" | `--set <activity>.review=council` | **disambiguate**: which activity? If both, pass `--set plan-authoring.review=council --set plan-execution.review=council`. |
|
|
16
16
|
| "one reviewer" | `--set <activity>.review=reviewed` | per the named activity, else ask |
|
|
17
|
-
| "delegate execution" | `--set plan-execution.execute=delegated` | execution only |
|
|
17
|
+
| "delegate execution" | `--set plan-execution.execute=delegated` | execution only (a **bridge** runs the change) |
|
|
18
|
+
| "let a subagent do it" | `--set <activity>.<slot>=subagent` | **disambiguate**: which work? `--set plan-execution.execute=subagent` (a slice of the change), `--set plan-authoring.author=subagent` (a plan/contract brief), `--set plan-authoring.fold=subagent` (the round's findings and dispositions), `--set routine.carrier=subagent` (a bounded chore) |
|
|
19
|
+
| "one slice at a time" | `--set routine.parallel=off` | `routine` only — `parallel` is a **flag**, not a recipe |
|
|
18
20
|
| "revert / do it myself" | `--unset <activity>.<slot>` | the named slot → its computed default |
|
|
19
21
|
|
|
20
|
-
Run **`node ${CLAUDE_SKILL_DIR}/tools/set-recipe.mjs [--set <activity>.<slot>=<
|
|
22
|
+
Run **`node ${CLAUDE_SKILL_DIR}/tools/set-recipe.mjs [--set <activity>.<slot>=<value>]… [--unset <activity>.<slot>]… [--write] [--json]`**:
|
|
21
23
|
|
|
22
|
-
1. **Grammar — always fully-qualified `<activity>.<slot>`** (the kit never guesses the activity; a bare `review=council` is rejected).
|
|
23
|
-
|
|
24
|
+
1. **Grammar — always fully-qualified `<activity>.<slot>`** (the kit never guesses the activity; a bare `review=council` is rejected). The writer takes **every slot of the three activities**, and `--help` prints the registry (`tools/carriers.mjs`) as two blocks:
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
Activities and their slots:
|
|
28
|
+
plan-authoring → author, fold, review
|
|
29
|
+
plan-execution → execute, review
|
|
30
|
+
routine → carrier, parallel
|
|
31
|
+
|
|
32
|
+
Accepted values per slot type:
|
|
33
|
+
review slots accept solo | reviewed | council
|
|
34
|
+
execute slots accept solo | delegated | subagent
|
|
35
|
+
carrier slots accept solo | subagent
|
|
36
|
+
switch slots accept on | off
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`parallel` is a **flag**, not a recipe: it resolves outside the recipe lattice and is never degraded. Examples: `--set routine.carrier=subagent`, `--set routine.parallel=off`, `--set plan-authoring.author=subagent`, `--set plan-authoring.fold=subagent`. A value outside its slot's list is a **usage** error (`2`) naming the accepted values, never a coercion to a neighbour.
|
|
40
|
+
2. **Preview by default** — prints `current → proposed` for the **changed** slots only, plus the **effective recipe resolved against live carrier readiness** (degradation stated honestly, e.g. *council requested, 1 ready reviewer → runs reviewed until a 2nd backend is ready*; *subagent requested, the executor vehicle is `missing` → runs solo until the vehicle is placed, naming the apply command*). A `parallel` value is reported as requested — a flag has nothing to degrade against. It writes **nothing**. Re-run with **`--write`** to apply (same effective/degradation note — a direct `--write` is never quieter than the preview). `--unset` returns a slot to its computed default (reverting needs no hand-edit either). A no-op `--set` (slot already equals) writes nothing and never re-seeds the onboarding note.
|
|
24
41
|
3. **`--write`** applies via a hardened, atomic write (deployment-gated — refuses to scatter a config into a repo with no `docs/ai`; exclusive-create temp + rename; symlink/TOCTOU-safe; last-writer-wins). It preserves the onboarding note + every untouched slot, normalizing to canonical 2-space JSON.
|
|
25
|
-
**Standing-consent advisory (after a successful `--write` only, and only when the written config names a `reviewed`/`council` recipe — a solo recipe gets NO advisory):** advise the ONE-TIME **hand-adds** to the maintainer's `.claude/settings.local.json` — `Bash(codex-review:*)` + `Bash(agy-review:*)` + `Bash(node <skill-dir>/tools/grounding.mjs:*)`, with the path your project actually reaches the kit by. State plainly: (a) auto-approving a review wrapper **spends subscription quota without a per-run prompt** — that is exactly what standing consent means here, so it is the maintainer's call; (b) the kit **never writes that file** — these stay hand-adds; (c) `grounding.mjs --out` writes ONE scratch facts file — that write is what the standing consent covers; (d) the entry must match the invocation **byte-form your project actually uses, INCLUDING quoting** — the procedures advisor renders a QUOTED
|
|
42
|
+
**Standing-consent advisory (after a successful `--write` only, and only when the written config names a `reviewed`/`council` recipe — a solo recipe gets NO advisory):** advise the ONE-TIME **hand-adds** to the maintainer's `.claude/settings.local.json` — `Bash(codex-review:*)` + `Bash(agy-review:*)` + `Bash(node <skill-dir>/tools/grounding.mjs:*)`, with the path your project actually reaches the kit by. State plainly: (a) auto-approving a review wrapper **spends subscription quota without a per-run prompt** — that is exactly what standing consent means here, so it is the maintainer's call; (b) the kit **never writes that file** — these stay hand-adds; (c) `grounding.mjs --out` writes ONE scratch facts file — that write is what the standing consent covers; (d) the entry must match the invocation **byte-form your project actually uses, INCLUDING quoting** — the procedures advisor renders a QUOTED path for the grounding pre-step (the readers-sweep command renders bare, matching the kit-tools tier), and a hand-add covering it must use the same spelling (a mismatched spelling is a dead rule that simply prompts).
|
|
26
43
|
4. **Exit codes:** `0` success (an explicit recipe that gracefully degrades is still `0`); `2` usage (a bare/duplicate op, or `--write` with no ops); `1` config error (malformed/unreadable config — the file is left **untouched**, never clobbered) or a write STOP (no deployment / a symlinked config). A `1`/`2` failure is loud; on a malformed config, offer to show the parse error so you can help the user fix the JSON.
|
|
27
44
|
|
|
28
45
|
Output is **English/structured** — **localize it to the user's conversational language** when you narrate. Surface the effective-recipe/degradation note plainly.
|
|
@@ -13,10 +13,10 @@ Run `node ${CLAUDE_SKILL_DIR}/tools/family-registry.mjs --json [--dir <project>]
|
|
|
13
13
|
> **Status reads `refresh`; the shared version block + the bootstrap/upgrade footers stay `notes`-based (unchanged this release).** `${CLAUDE_SKILL_DIR}/references/modes/status.md` has its OWN status-only render (above), keyed on `installed[].refresh.behind` / `refresh.recommend`. The shared **version block** (under *The version block + welcome mat* in `${CLAUDE_SKILL_DIR}/references/shared/report-footer.md`) and the bootstrap (step 11) + every upgrade (steps 4 / 8) report footer still consume `installed[].notes` verbatim — that wiring is deliberately **untouched** here (their migration onto `refresh` is deferred). Do not rewrite those footers onto `refresh`.
|
|
14
14
|
2. **Deployment (`--dir`)** (from `project`): whether `docs/ai/` is deployed + the deploy stamps by `display`; and **visibility** — render `project.visibility.state` in **user-safe words only**: *visible (tracked)* / *hidden (git-ignored, local-only)* / *unclear (uncommitted or partially set up)* — **never** the words "hidden fence" or any marker term. A `visibility.error` → surface it plainly. When `project.adrLayout` is **`old`** or **`old-unrotated`**, add a plain-language note that the project still uses an **older ADR layout** and should run the opt-in **`/agent-workflow-kit migrate-adr-store`** to move to the one-file-per-ADR store (preview first; it never commits) — the note is the SAME for both, they differ only in how the older layout was detected (`old` = a retired archive file is still on disk; `old-unrotated` = the project's deployed rotation script predates the store); `migrated` / `none` need no note. Then **one `specs` line, always** (from `project.specs` — the feature-spec layer's adoption state, judged from the store alone): `not adopted` (no `docs/ai/specs/` — the layer was never seeded), `adopting (N draft)` (a store with no live contract yet), `adopted (N live, M draft)`, or `could not be read — <reason>` (the store or a document in it could not be observed; never counted as any of the other three). A recorded decline (`project.specs.declined`) appends ` — declined` to the first two. The internal tokens are `not-adopted` / `adopting` / `adopted` / `unreadable`; render the plain phrases above, never the tokens. An envelope without the field says the installed kit predates it — say so rather than inventing a state.
|
|
15
15
|
3. **Settings (`--dir`, one line each)** (from `project.settings`):
|
|
16
|
-
- **recipes** — the effective recipe per slot (
|
|
16
|
+
- **recipes** — the effective recipe per slot, **one line per slot** over the three activities (`plan-authoring.author` / `.fold` / `.review`, `plan-execution.execute` / `.review`, `routine.carrier` / `.parallel`); each line also carries **where the value came from** (`source` — the project's config, an explicit override, or a computed default) and, when the resolved recipe is not the one that was asked for, **what it degraded from** (`degradedFrom`). Say both in plain words (never the field names): an effective recipe alone cannot tell a deliberate choice from a default, nor a degrade from a config that really names the resolved recipe — and a slot showing `solo` after a degrade from `subagent` is exactly the case the **subagents** line below explains. Detail → `/agent-workflow-kit procedures` / `recipes`; a `recipes.detectError` → say the backends couldn't be checked, so bridge-backed recipes floored at solo; the executor vehicle is unaffected.
|
|
17
17
|
- **attribution** — `includeCoAuthoredBy` effective; call out a **local override** only when `local` is non-null **and** differs from `project` (a `null` `local` means the key is absent there, so the project value stands — that is not an override).
|
|
18
18
|
- **velocity** — the effective `permissions.defaultMode` + whether an allowlist is seeded (detail → `/agent-workflow-kit velocity`).
|
|
19
|
-
- **subagents** — how many of the kit's
|
|
19
|
+
- **subagents** — how many of the kit's subagent vehicles are placed (`agents.placed` of `agents.bundled`; zero placed → the optional `/agent-workflow-kit agents` opt-in), then the **split the line always states separately**: the **read-only vehicles** (`agents.readOnly` — four; none of them grants a shell, three ride a cheap model for mechanical work and one is a read-only review lens) and the **ONE full-tool `executor`**, whose own state is `agents.executor`: *placed* / *customized* (both dispatchable) / *unusable* (`agents.executorReason` carries the reason — say it) / *missing*. That state is what decides whether a slot configured `subagent` can ride it: on **unusable** or **missing** every such slot runs solo instead, so surface it plainly beside the recipes lines and point at `/agent-workflow-kit agents` to place it. It is a claim about the FILE, never about the host. An envelope with no executor state says the installed kit predates the field — say so rather than inventing a state.
|
|
20
20
|
- **gate hook** — wired / hook file placed / declaration present, plus **`hook.declaredGates`** (0 = absent or an empty list; `null` = present but unreadable → say *couldn't be counted*, never a number; detail → `/agent-workflow-kit hook` and the gates guide).
|
|
21
21
|
- **bridge knobs** — a settings entry carrying **`retired`** is a key that is CONFIGURED but arms nothing: say so plainly (the reason is in the field) and point at `bridge-settings --unset <KEY> --apply` to clear it. Never render a retired key as an active setting.
|
|
22
22
|
- Any area's **`error`** field → surface it **loudly** in plain language; the rest of `status` still renders (never a crash).
|
|
@@ -17,7 +17,7 @@ Requires: ${CLAUDE_SKILL_DIR}/references/shared/report-footer.md · ${CLAUDE_SKI
|
|
|
17
17
|
6. `lens` — `node ${CLAUDE_SKILL_DIR}/tools/lens-region.mjs reconcile <project>/docs/ai/agent_rules.md` → per section: refreshed · already current · custom edit preserved · file absent / engine too old — skipped · over the line cap — refused · section absent — noted · a fully absent/invalid engine → hard STOP.
|
|
18
18
|
7. `bridge-settings` — `node ${CLAUDE_SKILL_DIR}/tools/bridge-settings.mjs --reconcile` → outcome line(s), pasted verbatim: ok · absent · flagged · duplicates · unusable.
|
|
19
19
|
|
|
20
|
-
**`pointers` — the bounded pointers.** ONE call reconciles **three** pointers — the **workflow-methodology** pointer, the **orchestration-recipes** pointer (Solo / Reviewed / Council / Delegated, routing to `/agent-workflow-kit recipes`) right below it, and the **autonomy-policy** pointer (the `docs/ai/autonomy.json` read contract, routing to `/agent-workflow-kit set-autonomy`) below that — and is filled on **every** upgrade, idempotently (zero-diff when all are already present + filled), so even a legacy or current equal-head deployment gains them **without a lineage-head bump or a migration** (the `agent-workflow-memory` **package** template did get docs-only headroom trims for the chained pointers, but no deployed-`docs/ai` structure changed). Per slot it inserts an empty pair at its anchor if absent, preserves a customized pair verbatim, and STOPs (never edits) on a malformed pair, a missing / duplicate anchor, or **when a fill is needed but the installed `agent-workflow-engine` is absent/invalid** (the fragments are read live from it — see the distinct outcomes below).
|
|
20
|
+
**`pointers` — the bounded pointers.** ONE call reconciles **three** pointers — the **workflow-methodology** pointer, the **orchestration-recipes** pointer (Solo / Reviewed / Council / Delegated / Subagent, routing to `/agent-workflow-kit recipes`) right below it, and the **autonomy-policy** pointer (the `docs/ai/autonomy.json` read contract, routing to `/agent-workflow-kit set-autonomy`) below that — and is filled on **every** upgrade, idempotently (zero-diff when all are already present + filled), so even a legacy or current equal-head deployment gains them **without a lineage-head bump or a migration** (the `agent-workflow-memory` **package** template did get docs-only headroom trims for the chained pointers, but no deployed-`docs/ai` structure changed). Per slot it inserts an empty pair at its anchor if absent, preserves a customized pair verbatim, and STOPs (never edits) on a malformed pair, a missing / duplicate anchor, or **when a fill is needed but the installed `agent-workflow-engine` is absent/invalid** (the fragments are read live from it — see the distinct outcomes below).
|
|
21
21
|
|
|
22
22
|
**Classify the exit — there are THREE non-zero exits + one soft in-band skip; handle each differently:**
|
|
23
23
|
|
|
@@ -35,6 +35,7 @@ Run `node ${CLAUDE_SKILL_DIR}/tools/velocity-profile.mjs [--dry-run | --apply] [
|
|
|
35
35
|
- `node ${CLAUDE_SKILL_DIR}/tools/release-scan.mjs <path>` (wildcard)
|
|
36
36
|
- `node ${CLAUDE_SKILL_DIR}/tools/repo-search.mjs --pattern <literal> --path <p>` (wildcard — the LITERAL search lane; for a pattern carrying a shell-significant byte use `--pattern-file <p>` instead, and for a TARGET PATH carrying one use `--paths-file <p>` — either way the bytes never enter the command string. one of the TWO tier tools that carry hook residual coverage — see the honesty note below)
|
|
37
37
|
- `node ${CLAUDE_SKILL_DIR}/tools/path-inventory.mjs --path <p>` (wildcard — the read-only inventory lane: exists / type / size / `wc -l` lines / one-level listing / small-file contents for N paths in ONE call, so small path questions never need a composed shell. `--paths-file <p>` is its out-of-band target lane; hook residual coverage as with repo-search)
|
|
38
|
+
- `node ${CLAUDE_SKILL_DIR}/tools/review-rounds-cli.mjs --artifact <plan>` (wildcard — the read-only round table over the review receipts, the verdict half of the per-round emission the `procedures` advisor names every round; the path argument is resolved and compared, never opened as a pattern, so it rides the settings-level posture like the rest)
|
|
38
39
|
- `node ${CLAUDE_SKILL_DIR}/tools/run-gates.mjs --cwd ${PROJECT_ROOT}` — **EXACT byte-string only**, and honestly **project-exec, not read-only**: it runs YOUR declared `docs/ai/gates.json` commands — the same trust boundary the opt-in hook grants byte-exact per-cmd. A wildcard would be BROADER than that boundary (`--cwd <dir>` executes another project's declared gates), so the bare cwd-defaulting form, any other `--cwd`, `--only`, and **`--final`** forms all still prompt (`--final` WRITES the final-run receipt into the core-evidence store — a recording run is never auto-approved).
|
|
39
40
|
- `node ${CLAUDE_SKILL_DIR}/tools/source-size-check.mjs --check` — **EXACT byte-string only**, and only this READ-ONLY mode: the tool's `--write-baseline` and `--adopt` modes WRITE (the recorded ratchet, and a gate declaration) and are deliberately uncovered — they still prompt, always. Every other form, `--cwd` included, keeps prompting. **What it covers, precisely:** the agent's own direct invocation. It is **not** the byte-string a DECLARED gate carries — the fill emits that path double-quoted (a kit path with a space must survive), and a seedable allow rule may carry no quotes at all, so the two spellings cannot be one string; a declared gate is auto-approved by the opt-in hook instead, byte-exact against `docs/ai/gates.json`.
|
|
40
41
|
- Writer previews, **exact arg-free dry-run byte-strings only** (the SEEDED tier byte-string is the arg-free preview of each): `node ${CLAUDE_SKILL_DIR}/tools/velocity-profile.mjs` · `node ${CLAUDE_SKILL_DIR}/tools/cheap-agents.mjs` · `node ${CLAUDE_SKILL_DIR}/tools/gate-hook.mjs` — every `--apply`/`--write`/`--yes` still prompts, always. (`gate-hook` also has a **`--read-lane`** flagged preview — the opt-in read-only compound lane, `${CLAUDE_SKILL_DIR}/references/modes/hook.md`; that flagged form is NOT the seeded arg-free byte-string, so it may **prompt once** — it IS a consent flow, stated, no silent cap.)
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
exists, run ONE command — `node ${CLAUDE_SKILL_DIR}/tools/inject-methodology.mjs reconcile
|
|
20
20
|
<project>/AGENTS.md` — which reconciles **three** bounded pointers in a single atomic write: the
|
|
21
21
|
**workflow-methodology** pointer (the plan → execute → review summary), the
|
|
22
|
-
**orchestration-recipes** pointer (the Solo / Reviewed / Council / Delegated vocabulary, routing to
|
|
22
|
+
**orchestration-recipes** pointer (the Solo / Reviewed / Council / Delegated / Subagent vocabulary, routing to
|
|
23
23
|
`/agent-workflow-kit recipes`) right below it, and the **autonomy-policy** pointer (the
|
|
24
24
|
`docs/ai/autonomy.json` read contract) below that. Each is **one atomic operation per slot**:
|
|
25
25
|
**ensure the slot exists**
|
|
@@ -37,7 +37,7 @@ The three setup choices — **visibility** (step 2), **conversational language**
|
|
|
37
37
|
10. **Honest `known_issues.md`.** Every bug with a workaround gets Impact + Plan so it isn't re-discovered later.
|
|
38
38
|
11. **One conversational language.** Talk to the user in the language chosen at bootstrap; keep code, paths, commands, and abbreviations in their source language. See *Communication contract*.
|
|
39
39
|
12. **Attribution is opt-in.** Honour the *Attribution* block: by default no agent/AI/model mention anywhere (commits, PRs, code, comments, docs), and no `Co-Authored-By` trailer. See *Attribution contract*.
|
|
40
|
-
13. **Orchestrate via a named recipe.** Compose execution through a named recipe (Solo / Reviewed / Council / Delegated) and **always commit yourself** —
|
|
40
|
+
13. **Orchestrate via a named recipe.** Compose execution through a named recipe (Solo / Reviewed / Council / Delegated / Subagent) and **always commit yourself** — every other carrier is advisory or delegated, never autonomous. Encoded via the reconciled `workflow:orchestration` pointer (it routes to `/agent-workflow-kit recipes` + the engine canon), not by bloating the entry point.
|
|
41
41
|
|
|
42
42
|
---
|
|
43
43
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"_README": "Per-project orchestration config: the recipe used at each step (slot) of each named activity. Easiest: tell the agent in plain language and run the `set-recipe` writer — it interprets your intent, previews the change, and writes valid JSON for you. You can still hand-edit this file directly whenever you prefer; that option never goes away.
|
|
2
|
+
"_README": "Per-project orchestration config: the recipe used at each step (slot) of each named activity. Easiest: tell the agent in plain language and run the `set-recipe` writer — it interprets your intent, previews the change, and writes valid JSON for you. You can still hand-edit this file directly whenever you prefer; that option never goes away. Three activities are configured independently, and so is each slot within them: 'plan-authoring' (slots author, fold, review), 'plan-execution' (slots execute, review) and 'routine' (slots carrier, parallel). A slot's value is a recipe: a 'review' slot accepts solo | reviewed | council (you self-review / one backend reviews / both review and you synthesize), or an explicit roster array such as [\"codex-review\", \"agy-review\", \"review-lens\"] in hand-edit form; an 'execute' slot accepts solo | delegated | subagent (you implement / a backend runs a bounded sub-task / a full-tool frontier subagent carries a bounded slice you verify); the carrier slots 'plan-authoring.author', 'plan-authoring.fold' and 'routine.carrier' accept solo | subagent. 'routine.parallel' is a flag rather than a recipe: it accepts on | off and decides whether file-disjoint subagent slices dispatch concurrently. The default below is 'solo' for every recipe and carrier slot, and 'on' for the parallel switch — no execution backend required. Raise a slot to reviewed or council for a second opinion, or to delegated to hand off execution; those need an execution backend set up first. 'subagent' needs the executor vehicle placed in this project — the composition root's `agents` writer places it; without it the slot resolves to solo with the reason stated. Remove a slot's line, or a whole activity block (or run `set-recipe --unset <activity>.<slot>`), to fall back to the computed default: reviewed when a review backend is ready and otherwise solo for a review slot, solo for author, fold, execute and carrier, on for parallel. Run the read-only procedures advisor to see an activity's steps plus the recipe resolved for your environment. Strict JSON — no comments.",
|
|
3
3
|
"plan-authoring": {
|
|
4
4
|
"review": "solo"
|
|
5
5
|
},
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
|
|
21
21
|
import { readFileSync, lstatSync } from 'node:fs';
|
|
22
22
|
import { join } from 'node:path';
|
|
23
|
-
import { ACTIVITIES } from './recipes.mjs';
|
|
23
|
+
import { POLICY_ACTIVITIES as ACTIVITIES } from './recipes.mjs';
|
|
24
24
|
|
|
25
25
|
// The hand-editable / agent-writable, per-project policy (strict JSON). cwd-relative — the error prefix
|
|
26
26
|
// uses this rel path so a user sees a path they can open, never an absolute temp/host path.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { DEFAULT_BUNDLE_ROOT } from './bridge-settings-read.mjs';
|
|
4
|
+
import { KNOWN_BACKENDS } from './detect-backends.mjs';
|
|
5
|
+
import { receiptIdOfCmd } from './carriers.mjs';
|
|
6
|
+
|
|
7
|
+
const oneLine = (value) => String(value).replace(/[\s]+/gu, ' ').trim();
|
|
8
|
+
|
|
9
|
+
const validPosture = (posture) => posture !== null && typeof posture === 'object' && !Array.isArray(posture)
|
|
10
|
+
&& typeof posture.model === 'string' && posture.model.length > 0
|
|
11
|
+
&& (!Object.hasOwn(posture, 'effort') || (typeof posture.effort === 'string' && posture.effort.length > 0))
|
|
12
|
+
&& (!Object.hasOwn(posture, 'tier') || posture.tier === null || (typeof posture.tier === 'string' && posture.tier.length > 0))
|
|
13
|
+
&& Object.keys(posture).every((key) => ['model', 'effort', 'tier'].includes(key));
|
|
14
|
+
|
|
15
|
+
const postureString = (posture, backend, settings) => {
|
|
16
|
+
const parts = [`model=${oneLine(posture.model)}`];
|
|
17
|
+
if (Object.hasOwn(posture, 'effort')) parts.push(`effort=${oneLine(posture.effort)}`);
|
|
18
|
+
if (Object.hasOwn(posture, 'tier')) {
|
|
19
|
+
const knob = (settings?.active ?? []).find((row) => row.key === 'CODEX_SERVICE_TIER' && row.bridge === backend.name);
|
|
20
|
+
parts.push(knob ? `tier=${oneLine(knob.value)} (bridge-settings)` : `tier=${posture.tier ?? 'standard'}`);
|
|
21
|
+
}
|
|
22
|
+
return parts.join(' ');
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const posturesByBackend = (ctx = {}) => {
|
|
26
|
+
const bundleRoot = ctx.bundleRoot ?? DEFAULT_BUNDLE_ROOT;
|
|
27
|
+
const read = ctx.readFile ?? readFileSync;
|
|
28
|
+
return Object.fromEntries(KNOWN_BACKENDS.flatMap((backend) => {
|
|
29
|
+
const cmd = backend.roleCmds?.review;
|
|
30
|
+
const receiptId = receiptIdOfCmd(cmd);
|
|
31
|
+
if (receiptId === null) return [];
|
|
32
|
+
const path = join(bundleRoot, backend.name, 'capability.json');
|
|
33
|
+
try {
|
|
34
|
+
const manifest = JSON.parse(String(read(path, 'utf8')));
|
|
35
|
+
if (!Object.hasOwn(manifest, 'posture')) {
|
|
36
|
+
return [[receiptId, { state: 'none', posture: null, path }]];
|
|
37
|
+
}
|
|
38
|
+
if (!validPosture(manifest.posture)) throw new Error('invalid posture block');
|
|
39
|
+
return [[receiptId, {
|
|
40
|
+
state: 'valid',
|
|
41
|
+
posture: postureString(manifest.posture, backend, ctx.settings ?? { active: [] }),
|
|
42
|
+
path,
|
|
43
|
+
}]];
|
|
44
|
+
} catch (error) {
|
|
45
|
+
return [[receiptId, { state: 'unreadable', posture: null, path, reason: oneLine(error?.message ?? error) }]];
|
|
46
|
+
}
|
|
47
|
+
}));
|
|
48
|
+
};
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
// carriers.mjs — the activity/slot registry plus the subagent carrier's readiness composition.
|
|
2
|
+
// ACTIVITIES + SLOT_RECIPES are the ONE table: recipes.mjs re-exports them, so no importer's path
|
|
3
|
+
// moves. The carrier half declares the `subagent` recipe, appends the executor vehicle to a
|
|
4
|
+
// readiness array as the single provider of the `carry` role, words the degrade a missing or
|
|
5
|
+
// unusable vehicle causes, and holds the dispatch-form wording every render prints (one source, a
|
|
6
|
+
// red line where it is a red line). Governing contract: docs/ai/specs/kit/carriers.md.
|
|
7
|
+
// Leaf — imports only the direct-run guard (recipes.mjs imports THIS), no fs, nothing on import.
|
|
8
|
+
import { refuseDirectRun } from './direct-run.mjs';
|
|
9
|
+
|
|
10
|
+
// The slot VALUE is the slot's TYPE; SLOT_RECIPES lists the values each type accepts. A `switch`
|
|
11
|
+
// slot is a flag, not a recipe: it resolves outside the recipe lattice and never degrades.
|
|
12
|
+
// `policy` marks an activity that is a SESSION with an autonomy level of its own; a routine chore
|
|
13
|
+
// runs inside such a session and carries none.
|
|
14
|
+
export const ACTIVITIES = {
|
|
15
|
+
'plan-authoring': { slots: { author: 'carrier', fold: 'carrier', review: 'review' }, policy: true },
|
|
16
|
+
'plan-execution': { slots: { execute: 'execute', review: 'review' }, policy: true },
|
|
17
|
+
routine: { slots: { carrier: 'carrier', parallel: 'switch' }, policy: false },
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const POLICY_ACTIVITIES = Object.fromEntries(
|
|
21
|
+
Object.entries(ACTIVITIES).filter(([, activity]) => activity.policy),
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
export const SLOT_RECIPES = {
|
|
25
|
+
review: ['solo', 'reviewed', 'council'],
|
|
26
|
+
execute: ['solo', 'delegated', 'subagent'],
|
|
27
|
+
carrier: ['solo', 'subagent'],
|
|
28
|
+
switch: ['on', 'off'],
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const SWITCH_SLOT = 'switch';
|
|
32
|
+
export const SWITCH_DEFAULT = 'on';
|
|
33
|
+
|
|
34
|
+
const CODEX = 'codex-cli-bridge';
|
|
35
|
+
const AGY = 'antigravity-cli-bridge';
|
|
36
|
+
|
|
37
|
+
export const DISPLAY_ALIASES = Object.freeze({ [CODEX]: 'codex', [AGY]: 'agy' });
|
|
38
|
+
export const BACKEND_PRIORITY = Object.freeze([CODEX, AGY]);
|
|
39
|
+
export const REVIEW_CMD_ALIASES = Object.freeze({
|
|
40
|
+
'codex-review': Object.freeze({ backend: CODEX, receiptId: 'codex' }),
|
|
41
|
+
'agy-review': Object.freeze({ backend: AGY, receiptId: 'agy' }),
|
|
42
|
+
});
|
|
43
|
+
export const receiptIdOfCmd = (cmd) => REVIEW_CMD_ALIASES[cmd]?.receiptId ?? null;
|
|
44
|
+
export const LENS_VERDICTS = Object.freeze(['ship', 'ship with nits', 'revise', 'rethink']);
|
|
45
|
+
|
|
46
|
+
export const isSwitchSlot = (slotType) => slotType === SWITCH_SLOT;
|
|
47
|
+
|
|
48
|
+
export const CARRY_ROLE = 'carry';
|
|
49
|
+
export const EXECUTOR_PROVIDER = 'executor';
|
|
50
|
+
|
|
51
|
+
export const SUBAGENT_RECIPE = {
|
|
52
|
+
id: 'subagent',
|
|
53
|
+
title: 'Subagent',
|
|
54
|
+
role: CARRY_ROLE,
|
|
55
|
+
minBackends: 1,
|
|
56
|
+
degradesTo: 'solo',
|
|
57
|
+
summary:
|
|
58
|
+
'a full-tool frontier subagent from the placed executor vehicle carries a bounded, file-disjoint slice; the orchestrator verifies it and commits.',
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// The readiness tokens the planner judges on. Mirrored from detect-backends.mjs rather than
|
|
62
|
+
// imported: this module is the leaf the planner itself imports.
|
|
63
|
+
const READY = 'ready';
|
|
64
|
+
const MISSING = 'missing';
|
|
65
|
+
|
|
66
|
+
const VEHICLE_READY_STATES = ['placed', 'customized'];
|
|
67
|
+
|
|
68
|
+
// withVehicle(readiness, survey) → a NEW array carrying the executor as the ONE provider of `carry`.
|
|
69
|
+
// Every caller that hands readiness to the resolver composes it this way; a role-filtered readiness
|
|
70
|
+
// computation is what keeps a placed executor from counting as a ready reviewer.
|
|
71
|
+
export const withVehicle = (readiness = [], survey = null) => [
|
|
72
|
+
...readiness.filter((entry) => entry?.name !== EXECUTOR_PROVIDER),
|
|
73
|
+
{
|
|
74
|
+
name: EXECUTOR_PROVIDER,
|
|
75
|
+
readiness: VEHICLE_READY_STATES.includes(survey?.state) ? READY : MISSING,
|
|
76
|
+
vehicle: survey,
|
|
77
|
+
},
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
export const EXECUTOR_APPLY = '/agent-workflow-kit agents';
|
|
81
|
+
|
|
82
|
+
// A survey reason may quote a file the user wrote; it is collapsed to one safe line before it rides
|
|
83
|
+
// the one-line render contracts: no escape sequences, control bytes or line breaks, and none of the
|
|
84
|
+
// characters those renders use as cell structure (the separator, parentheses, the equals sign).
|
|
85
|
+
export const safeLine = (text) => String(text ?? '')
|
|
86
|
+
.replace(/\x1b\[[0-?]*[ -/]*[@-~]/gu, '')
|
|
87
|
+
.replace(/[·()=]/gu, ' ')
|
|
88
|
+
.replace(/[\p{Cc}\p{Cf}\p{Zl}\p{Zp}\s]+/gu, ' ')
|
|
89
|
+
.trim();
|
|
90
|
+
|
|
91
|
+
export const vehicleDegradeReason = (survey, applyHint = EXECUTOR_APPLY) => {
|
|
92
|
+
const state = survey?.state ?? MISSING;
|
|
93
|
+
const reason = safeLine(survey?.reason);
|
|
94
|
+
const detail = reason ? ` (${reason})` : '';
|
|
95
|
+
const remedy = state === 'unusable'
|
|
96
|
+
? `fix or remove ${safeLine(survey?.rel) || '.claude/agents/executor.md'}, then place it with: ${applyHint}`
|
|
97
|
+
: `place it with: ${applyHint}`;
|
|
98
|
+
return `the executor vehicle is ${state}${detail} — ${remedy}`;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// ── the dispatch form: the ONE wording source every render prints ───────────────────
|
|
102
|
+
// Pure constants. `procedures.mjs` prints them for a slot resolved to `subagent`; the wording is a
|
|
103
|
+
// red line, so a render composes these strings and never re-words one.
|
|
104
|
+
|
|
105
|
+
// The slice noun is per SLOT — authoring and folding share an activity but carry different work.
|
|
106
|
+
export const SLICE_BY_SLOT = {
|
|
107
|
+
'plan-authoring.author': 'a slice is a brief naming the goal, the governing spec(s) and the ledger constraints; the subagent drafts the plan or the contract from it, and the orchestrator reviews the draft as its own',
|
|
108
|
+
'plan-authoring.fold': "a slice is the round's findings with their dispositions; the subagent edits the plan or the contract in place and returns; the orchestrator runs the self-consistency read itself",
|
|
109
|
+
'plan-execution.execute': 'a slice is a set of file-disjoint ledger rows; wording is copied verbatim where wording is a red line',
|
|
110
|
+
'routine.carrier': "a slice is a bounded mechanical task; a read-only one (a sweep, gate triage) rides its placed read-only vehicle, or is carried solo with a stated reason when that vehicle is absent; a write-capable one (a regeneration, a fixture build) rides the executor; the changelog stays the orchestrator's",
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export const VEHICLE_STATE_TOKEN = '<state>';
|
|
114
|
+
|
|
115
|
+
export const DISPATCH_LINES = [
|
|
116
|
+
`dispatch: the executor vehicle (.claude/agents/executor.md — ${VEHICLE_STATE_TOKEN}), in the background`,
|
|
117
|
+
'the orchestrator verifies every returned slice by running its suites itself',
|
|
118
|
+
'the subagent is never told to commit, never a review backend, never a bridge substitute',
|
|
119
|
+
'honest limit: a Claude Code lane — on a host that cannot dispatch the vehicle, follow this form by hand and say so',
|
|
120
|
+
];
|
|
121
|
+
|
|
122
|
+
export const PARALLEL_SOLO_NOTE = '(no effect while the carrier is solo)';
|
|
123
|
+
|
|
124
|
+
export const PARALLEL_LINES = {
|
|
125
|
+
on: 'parallel: on — file-disjoint slices dispatch concurrently',
|
|
126
|
+
off: 'parallel: off — one slice at a time',
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// The slot TYPES a subagent can carry — computed from the one value table, never a second list.
|
|
130
|
+
export const SUBAGENT_SLOT_TYPES = Object.entries(SLOT_RECIPES)
|
|
131
|
+
.filter(([, values]) => values.includes(SUBAGENT_RECIPE.id))
|
|
132
|
+
.map(([slotType]) => slotType);
|
|
133
|
+
|
|
134
|
+
// dispatchForm({ activity, slot, state }) → the lines a `subagent`-resolved slot renders: the
|
|
135
|
+
// slot's slice sentence, then the four shared lines with the surveyed vehicle state filled in.
|
|
136
|
+
// A slot whose type cannot hold `subagent` (a review slot) and an unknown activity render nothing.
|
|
137
|
+
export const dispatchForm = ({ activity, slot, state } = {}) => {
|
|
138
|
+
const slice = SLICE_BY_SLOT[`${activity}.${slot}`];
|
|
139
|
+
const slotType = ACTIVITIES[activity]?.slots?.[slot];
|
|
140
|
+
if (!slice || !SUBAGENT_SLOT_TYPES.includes(slotType)) return [];
|
|
141
|
+
return [slice, ...DISPATCH_LINES.map((line) => line.replace(VEHICLE_STATE_TOKEN, state ?? MISSING))];
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
// parallelLine({ value, carrier }) → the `routine` switch line. The concurrency claim is TRUE only
|
|
145
|
+
// while the effective carrier is `subagent`; under a solo carrier the flag states its own inertness.
|
|
146
|
+
export const parallelLine = ({ value, carrier } = {}) => {
|
|
147
|
+
const inert = carrier !== SUBAGENT_RECIPE.id ? ` ${PARALLEL_SOLO_NOTE}` : '';
|
|
148
|
+
if (value === 'on') return inert ? `parallel: on${inert}` : PARALLEL_LINES.on;
|
|
149
|
+
return `${PARALLEL_LINES.off}${inert}`;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
refuseDirectRun(import.meta.url);
|