diff --git a/.agents/skills/design-profile/SKILL.md b/.agents/skills/design-profile/SKILL.md index a2217ca6589..f480e41332a 100644 --- a/.agents/skills/design-profile/SKILL.md +++ b/.agents/skills/design-profile/SKILL.md @@ -1,6 +1,6 @@ --- name: design-profile -description: Agent-only supervisor contract for dispatching and supervising an interactive design task whose tracked deliverable is an ADR. Load before scaffolding, dispatching, answering, completing, or cleaning up a kind=design task. +description: Agent-only supervisor contract for dispatching and supervising a one-conversation design task whose tracked deliverable is a short ADR. Load before scaffolding, dispatching, answering, completing, or cleaning up a kind=design task. user-invocable: false metadata: internal: true @@ -8,27 +8,48 @@ metadata: # Design Profile -Use this profile when the requested product is an interactive decision process ending in an architectural decision record. +Use this profile when the requested product is one planning conversation ending in a short architectural decision record. Use a ship when implementation is already authorized and remaining design uncertainty cannot materially change what to build. Use a scout when the result is knowledge or a recommendation rather than a tracked ADR. +Do not stack a Kun research pass, a Matt interview, and an ADR task as three workflows. +Do not add a design interview or visual review in front of well-specified authorized implementation. +Do not rewrite a live task's already generated brief; this contract applies at the next scaffold and dispatch. -`bin/fm-brief.sh --design` owns the worker-facing interview and ADR contract. +`bin/fm-brief.sh --design` owns the worker-facing planning and ADR contract. `bin/fm-spawn.sh --design` owns task-kind metadata, delivery posture, branch identity, and verified harness launch. This skill owns the supervisor decisions around those mechanics. +## One planning conversation + +Keep research, optional visual proposals, selective questioning, and the ADR in the same design task. +Research facts from the repository and established evidence before asking anyone a decision. +When an ambiguous choice is clearer as a diagram or interactive proposal, the worker may use `lavish-axi` in this conversation. +Visual review is optional and never a completion gate for a well-specified ADR ask. +Do not open a separate visual-review scout unless the captain asked for that knowledge deliverable. + +Use the dispatch-pinned Matt `grilling` skill only for its design-tree and frontier: ask the next unblocked decision. +Firstmate's one-keyed-question protocol takes precedence over grilling's "ask the whole frontier in one round". +Use the dispatch-pinned `domain-modeling` skill to sharpen terms and to judge whether an ADR is warranted. +Do not import that plugin's `grill-with-docs`, `to-spec`, `to-tickets`, `wayfinder`, `implement`, or `CONTEXT.md` lifecycle. +Its `to-tickets` is firstmate's backlog plus `bin/fm-brief.sh`. +Its `implement` is firstmate's ship task and its selected delivery mode. +Its `code-review` is firstmate's own validation pipeline. +Importing those would give one contract two owners. + ## Dependency boundary -The profile uses the installed `mattpocock-skills@mattpocock` plugin. +The profile uses the installed `mattpocock-skills@mattpocock` plugin as thinking tools, not as a second planner. `bin/fm-design-skills.sh` is the single owner of resolving named skills from that plugin. It only reads the registry and skill files. It never installs, updates, copies, vendors, pins, or modifies the plugin. -Only the captain upgrades that dependency through their own `/plugin` action. +Workers never own plugin lifecycle. +Plugin install and refresh are captain-owned outside this repository; do not add a competing updater here. Run `bin/fm-design-skills.sh check` before scaffolding. -If it refuses because the install or a required skill is absent, stop and ask the captain to refresh the plugin. +If it refuses because the install or a required skill is absent, stop and report that the captain-owned plugin install needs a refresh. Never substitute copied skill text or run an installer from a worker. -That plugin auto-updates, so `fm-spawn.sh --design` resolves it once at dispatch, binds the worker-facing brief to that result's exact skill paths, and records the release in task metadata so the durable completion manifest carries it past cleanup ([`docs/fleet-data-contracts.md`](../../../docs/fleet-data-contracts.md#the-design-tasks-plugin-release)). +The installed plugin can change between tasks, so `fm-spawn.sh --design` resolves it once at dispatch, binds the worker-facing brief to that result's exact skill paths, and records the release in task metadata so the durable completion manifest carries it past cleanup ([`docs/fleet-data-contracts.md`](../../../docs/fleet-data-contracts.md#the-design-tasks-plugin-release)). A design result that surprises you is therefore traceable to the exact instructions that informed it. Do not repeat that release in the ADR: the manifest owns the fact, and the ADR is a project deliverable rather than a record of firstmate's tooling. @@ -36,17 +57,9 @@ When the captain names that plugin's skills for a task, read the resolved `ask-m Account for the choice in the same reply that reports what is being dispatched: which skills were selected and why. The router recommends skills that are not invokable as skills. Reaching one means resolving its path and reading the file, exactly as the design brief already does for grilling and domain-modeling. +Select only thinking tools that serve this one conversation; do not dispatch a second Matt workflow. -Do not import that plugin's lifecycle, file layout, or scratch-tracker conventions. -Its `to-tickets` is firstmate's backlog plus `bin/fm-brief.sh`. -Its `implement` is firstmate's ship task and its selected delivery mode. -Its `code-review` is firstmate's own validation pipeline. -Its `CONTEXT.md` is refused outright by this profile's ADR-only rule. -Importing those would give one contract two owners. -The router is for choosing that plugin's thinking tools. -Firstmate's lifecycle stays firstmate's. - -The worker brief tells every harness to read the resolved skill files directly. +The worker brief tells every harness to read the dispatch-pinned skill files directly. This avoids depending on harness-specific command spelling while preserving one exact installed dependency for Claude, Codex, and Pi. Those dependencies supply thinking tools only. The profile's ADR-only contract takes precedence over any dependency direction to create or update `CONTEXT.md`. @@ -69,6 +82,7 @@ Restart a live design worker with `bin/fm-control.sh relaunch`, which keeps `kin The design worker investigates factual questions from repository evidence and asks one decision question at a time. Every question carries a stable key, evidence, and a recommended answer. The worker stops until firstmate returns an answer with the same key. +Preserve that keyed inventory for in-flight design tasks as well as new ones. Load `ask-user-authority` before answering any design question. An answer that follows directly from accepted intent, repository evidence, an established rule, or a decision already returned in the same session is a correction within accepted intent. @@ -80,6 +94,11 @@ When the interview has converged, require the worker to state the resulting deci ## ADR completion +Commit a short ADR only when the dispatched domain-modeling skill's ADR bar is met: hard to reverse, surprising without context, and a real trade-off. +Routine configuration changes are ships, not ADRs. +If a design interview shows the change does not meet that bar, stop for a keyed decision rather than padding a ceremonial ADR. +Existing in-flight ADR work keeps its already generated brief and continues to delivery. + Use an existing project ADR convention when one exists. Otherwise the worker uses `docs/adr/NNNN-.md`, incrementing the highest existing number. The ADR must stand alone with context, decision, rationale, relevant alternatives, and non-obvious consequences. diff --git a/AGENTS.md b/AGENTS.md index 3bc3988b3c1..f15ba1dadf1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -315,9 +315,12 @@ A brain result is a nearest indexed page, not an answer: a miss is absence of a Classify the deliverable: - **Ship** is the default and produces an authorized project change through the selected delivery mode; once implementation is authorized, dispatch a ship and keep any remaining bounded research inside it unless unresolved uncertainty could materially change whether or what to build. -- **Design** runs an interactive decision interview whose only tracked project change is an ADR; load `design-profile` before scaffolding, dispatching, answering, completing, or cleaning up one. +- **Design** is an explicit ADR task for a consequential architectural tradeoff whose only tracked project change is that ADR; load `design-profile` before scaffolding, dispatching, answering, completing, or cleaning up one. - **Scout** produces knowledge in `data//report.md`, never a PR, and is appropriate for investigation, diagnosis, planning, reproduction, or audit work when the captain explicitly requests a separate knowledge deliverable or unresolved uncertainty could materially change whether or what to build. +Keep planning in one conversation: research and optional visual proposals where they change the decision, and selective dependency-aware questioning, rather than stacking separate Kun, Matt, and ADR workflows. +Do not add a design interview or visual review in front of already authorized, well-specified implementation. +Routine configuration changes are ships, not ADRs. If established evidence already answers an informational question, relay it without a design-only scout; when implementation intent is unclear, answer and ask one concise implementation question when useful rather than dispatching speculative design work. Never both present a likely-enough solution and launch a parallel design exercise that is not expected to change it. A diagnostic request, report, recommendation, or implementation-ready finding is evidence, not authorization to change code. @@ -424,7 +427,7 @@ Retire one only on an explicit captain or main-firstmate decision, after loading ### Design outcome A completed design task leaves a tracked ADR and no implementation. -Load `design-profile` before treating the ADR as complete; it owns the interview, dependency boundary, harness-independent skill resolution, decision inventory, delivery, and cleanup contract. +Load `design-profile` before treating the ADR as complete; it owns the one-conversation planning contract, dependency boundary, harness-independent skill resolution, decision inventory, delivery, and cleanup. ### Scout outcome and promotion @@ -593,7 +596,7 @@ These skills are not captain-invocable; load them only at their precise triggers - `bootstrap-diagnostics` - load whenever the session-start digest's bootstrap or network-checks section prints an actionable diagnostic line (`MISSING:`, `MISSING_MANUAL:`, `BACKEND_INVALID:`, `NEEDS_GH_AUTH`, `TANGLE:`, `VAULT_DRIFT:`, `UPSTREAM:`, `GBRAIN_SERVING_CREDENTIAL:`, `GBRAIN_PIN:`, `GBRAIN_CAPTURE:`, `STARTUP_MEMORY_BUDGET:`, `CREW_DISPATCH:` (invalid or backend mismatch), `FLEET_SYNC:`, `BOARD_SWEEP:`, `NETWORK_CHECKS:`, `HOME_SUMMARY:`, `BACKLOG_RECONCILE:`, `ENDPOINT_BINDING_MIGRATION:`, `RUN_ATTRIBUTION:`, `SECONDMATE_SYNC:`, `SECONDMATE_LIVENESS:`, `SECONDMATE_HANDOFF:`, `NUDGE_SECONDMATES:`, `USAGE_STORE:`, or `FMX:`), or when `BOOTSTRAP_INFO:` says an interrupted backlog cleanup may have left an endpoint or local copy; silence and other `BOOTSTRAP_INFO:` facts need no load. - `diagnostic-reasoning` - load before scoping a reported bug and before acting on a diagnostic report. -- `design-profile` - load before scaffolding, dispatching, answering, completing, or cleaning up an interactive design task whose tracked deliverable is an ADR. +- `design-profile` - load before scaffolding, dispatching, answering, completing, or cleaning up a one-conversation design task whose tracked deliverable is a short ADR. - `ask-user-authority` - load before deciding any ask-user finding, regardless of the project's `yolo` posture. - `quota-array-dispatch` - load before choosing among a matched crew-dispatch profile array from current quota-axi default TOON. - `harness-adapters` - load before spawning or recovering a crewmate or secondmate, handling a trust dialog, sending a harness-specific skill invocation, interrupting or exiting an agent, resuming an exited agent, or verifying a new harness adapter. diff --git a/README.md b/README.md index a88f7ef4d45..86333cfeff0 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ Launching a supported harness inside it for your primary session instantiates yo - **One liaison** - you talk only to the first mate; it dispatches, supervises, escalates only real decisions, and reports plain outcomes. - **A visible crew** - every crewmate works in its own tmux window, Herdr tab, or experimental zellij tab, cmux workspace, or Orca terminal you can watch or type into; the first mate reconciles. - **Disposable worktrees** - each task runs in a clean [treehouse](https://github.com/kunchenguid/treehouse) git worktree, or an Orca-managed worktree when `backend=orca`, so parallel work on one repo never collides. -- **Three task shapes** - ship tasks deliver authorized changes; design tasks run an interactive decision interview and land an ADR; scout tasks leave standalone investigation reports when the intake contract warrants separate research. +- **Three task shapes** - ship tasks deliver authorized changes; design tasks run one planning conversation and land a short ADR for a consequential tradeoff; scout tasks leave standalone investigation reports when the intake contract warrants separate research. - **Explicit project modes** - each project ships via `no-mistakes`, `direct-PR`, or `local-only`, with an optional `+yolo` merge-autonomy flag. - **Optional secondmates** - opt in to persistent second mates that run from isolated firstmate homes with their own `FM_HOME`, state, projects, and session lock, either locally or as a whole home on an SSH-reachable host, with guarded updates and recovery that never turns an unavailable remote route into a local replacement. - **Event-driven, zero-token supervision** - a bash watcher sleeps on the fleet and wakes the first mate only when something needs you; verified primary harnesses also get a turn-end backstop that blocks or follows up on a blind stop when work is under way and supervision is not live. diff --git a/bin/fm-brief.sh b/bin/fm-brief.sh index 02687ea2ad3..329153c05e8 100755 --- a/bin/fm-brief.sh +++ b/bin/fm-brief.sh @@ -48,11 +48,14 @@ # Either way fm-spawn.sh copies the explicit markers into task metadata, where # bin/fm-issue-comment.sh reads the recorded PR target to decide whether it may # write firstmate's own living status comment to that tracker. -# --design writes the interactive design contract: the worker reads the -# installed mattpocock grilling and domain-modeling skills, asks one -# evidence-first question at a time through firstmate, and produces a tracked -# ADR through the selected delivery mode. The plugin is read in place and is -# never installed, updated, copied, vendored, pinned, or modified here. +# --design writes one planning conversation whose only tracked project +# deliverable is a short ADR: the worker researches facts, may use an optional +# visual proposal, and reads the installed mattpocock grilling and +# domain-modeling skills selectively for dependency-aware questioning and +# terms. It asks one evidence-first question at a time through firstmate and +# ships the ADR through the selected delivery mode. The plugin is read in +# place and is never installed, updated, copied, vendored, pinned, or +# modified here. Plugin lifecycle is captain-owned outside this repository. # --scout writes the scout contract instead: the deliverable is a report at # data//report.md (no branch, no push, no PR) and the worktree is scratch. # --secondmate writes a persistent secondmate charter. The project list @@ -436,7 +439,7 @@ BRIEF="$DATA/$ID/brief.md" [ -e "$BRIEF" ] && { echo "error: $BRIEF already exists" >&2; exit 1; } if [ "$KIND" = design ]; then "$FM_ROOT/bin/fm-design-skills.sh" check >/dev/null || { - echo "error: --design requires the captain-installed mattpocock grilling and domain-modeling skills; do not install or copy them from a worker" >&2 + echo "error: --design requires the captain-owned mattpocock grilling and domain-modeling skills; do not install or copy them from a worker" >&2 exit 1 } fi @@ -1049,26 +1052,34 @@ if [ "$KIND" = design ]; then OUTPUT_KIND=design IFS= read -r -d '' DESIGN_SECTION <]: {one question} Recommendation: {answer and evidence}\`, then stop and wait. Never batch questions, answer on behalf of firstmate, or proceed while the current key is unresolved. When an answer arrives, append \`resolved [key=]: {decision returned by firstmate}\` and \`working: continuing the design interview\` in the same breath, then capture the decision in the ADR. State the converged decision back to firstmate before drafting the ADR. +Write a short ADR only when the dispatched domain-modeling skill's ADR bar is met. +If the conversation shows a routine configuration change rather than a consequential architectural tradeoff, append \`needs-decision\` rather than padding a ceremonial ADR. Use an existing project ADR convention when one exists. Otherwise use \`docs/adr/NNNN-.md\`, incrementing the highest existing number. The ADR must stand alone with context, decision, rationale, relevant alternatives, and non-obvious consequences. diff --git a/bin/fm-design-skills-lib.sh b/bin/fm-design-skills-lib.sh index 85c5edd434c..c398476e6c9 100644 --- a/bin/fm-design-skills-lib.sh +++ b/bin/fm-design-skills-lib.sh @@ -25,7 +25,8 @@ design_skill_path_is_safe() { # } # adopt_relaunch_design_skills: reuse the dispatch pin already recorded for # this design task. Never call fm-design-skills.sh resolve here; a later -# plugin auto-update must not silently rebind the interview. +# change to the captain-owned plugin install must not silently rebind the +# interview. adopt_relaunch_design_skills() { local RELAUNCH_META=$1 ID=$2 local recorded_plugin recorded_version recorded_updated tasktmp dispatch_brief diff --git a/bin/fm-design-skills.sh b/bin/fm-design-skills.sh index f0abb04e99d..b9b5866cd58 100755 --- a/bin/fm-design-skills.sh +++ b/bin/fm-design-skills.sh @@ -1,7 +1,8 @@ #!/usr/bin/env bash # Resolve named skills from the installed mattpocock plugin for a Firstmate # design task without installing, updating, copying, pinning, or modifying it. -# The captain owns plugin lifecycle through Claude's /plugin action. +# The captain owns plugin lifecycle outside this repository. +# Workers never install, update, copy, pin, or modify the plugin. # # Usage: # fm-design-skills.sh resolve [skill-name...] @@ -41,7 +42,7 @@ command -v jq >/dev/null 2>&1 || { REGISTRY=${FM_MATTPOCOCK_PLUGIN_REGISTRY:-${CLAUDE_CONFIG_DIR:-${HOME:?}/.claude}/plugins/installed_plugins.json} [ -f "$REGISTRY" ] && [ ! -L "$REGISTRY" ] || { - echo "error: mattpocock plugin registry is unavailable at $REGISTRY; the captain must install or refresh it with /plugin" >&2 + echo "error: mattpocock plugin registry is unavailable at $REGISTRY; refresh the captain-owned plugin install. Workers must not install or copy it" >&2 exit 1 } @@ -55,7 +56,7 @@ ENTRY=$(jq -cer ' | select(. != null) | {installPath, version: (.version // "unknown"), lastUpdated} ' "$REGISTRY" 2>/dev/null) || { - echo "error: no active mattpocock-skills@mattpocock install is recorded; the captain must install or refresh it with /plugin" >&2 + echo "error: no active mattpocock-skills@mattpocock install is recorded; refresh the captain-owned plugin install. Workers must not install or copy it" >&2 exit 1 } @@ -71,7 +72,7 @@ case "$INSTALL_PATH" in ;; esac INSTALL_PATH=$(CDPATH='' cd -- "$INSTALL_PATH" 2>/dev/null && pwd -P) || { - echo "error: recorded mattpocock plugin install is missing: $INSTALL_PATH; the captain must refresh it with /plugin" >&2 + echo "error: recorded mattpocock plugin install is missing: $INSTALL_PATH; refresh the captain-owned plugin install. Workers must not install or copy it" >&2 exit 1 } @@ -125,7 +126,7 @@ add_skill_record() { skill_path= for required in grilling domain-modeling ask-matt; do if ! skill_path=$(lookup_skill_path "$required"); then - echo "error: installed mattpocock plugin lacks required design skill $required; the captain must refresh it with /plugin" >&2 + echo "error: installed mattpocock plugin lacks required design skill $required; refresh the captain-owned plugin install. Workers must not install or copy it" >&2 exit 1 fi add_skill_record "$required" "$skill_path" diff --git a/bin/fm-spawn.sh b/bin/fm-spawn.sh index 4d308240893..fae68a6e2e4 100755 --- a/bin/fm-spawn.sh +++ b/bin/fm-spawn.sh @@ -177,13 +177,14 @@ # secondmate receives the primary's read-only shared captain-preference file # (fm-config-inherit-lib.sh). A successful launch clears pending inherited # config reread generations because the new agent reads the converged files. -# --design records kind=design in the task's meta (interactive ADR deliverable; -# see the design-profile skill) and, from the one bin/fm-design-skills.sh -# resolve that gates a fresh dispatch, records design_skills_plugin=, -# design_skills_version=, and design_skills_updated= so the auto-updating -# plugin release that informed the interview stays readable after cleanup. -# A --relaunch of that same design task reuses the recorded release and the -# dispatch-pinned skill paths rather than resolving again +# --design records kind=design in the task's meta (ADR deliverable from one +# planning conversation; see the design-profile skill) and, from the one +# bin/fm-design-skills.sh resolve that gates a fresh dispatch, records +# design_skills_plugin=, design_skills_version=, and design_skills_updated= +# so the plugin release that informed the interview stays readable after +# cleanup even if the captain-owned install later changes. A --relaunch of +# that same design task reuses the recorded release and the dispatch-pinned +# skill paths rather than resolving again # (docs/fleet-data-contracts.md "The design task's plugin release"). # --scout records kind=scout (report deliverable, # scratch worktree; see AGENTS.md task lifecycle); --secondmate records @@ -1976,7 +1977,7 @@ fi [ -f "$BRIEF" ] || { echo "error: task $ID has no brief at inaccessible data path $BRIEF" >&2; exit 1; } SOURCE_BRIEF=$BRIEF # A design task reads the installed mattpocock skill files live during its -# interview, and the captain has that plugin on auto-update, so the instructions +# interview, and the captain-owned install can change between tasks, so the instructions # behind one design result need not be the instructions behind the next. One # resolve call serves as both the dispatch gate and the provenance record, so # what is recorded is exactly what was verified present at dispatch. Reading the @@ -1999,7 +2000,7 @@ if [ "$KIND" = design ]; then adopt_relaunch_design_skills "$RELAUNCH_META" "$ID" || exit 1 else DESIGN_SKILLS_RECORD=$("$FM_ROOT/bin/fm-design-skills.sh" resolve) || { - echo "error: design spawn requires the captain-installed mattpocock design skills; do not install or copy them from a worker" >&2 + echo "error: design spawn requires the captain-owned mattpocock design skills; do not install or copy them from a worker" >&2 exit 1 } DESIGN_SKILLS_PLUGIN=$(design_skills_field "$DESIGN_SKILLS_RECORD" plugin) @@ -3120,8 +3121,9 @@ if [ "$KIND" = design ]; then '# Dispatch-pinned design skills' \ 'Firstmate resolved this binding once at dispatch and recorded its plugin release in the task metadata.' \ 'Use your read tool to load exactly the `grilling` and `domain_modeling` paths in this JSON record.' \ + 'Use those skills selectively inside this task'\''s one planning conversation; do not run a second Kun or Matt workflow.' \ 'Do not run `fm-design-skills.sh resolve` or substitute another installed release.' \ - 'If either exact pinned path is missing, unreadable, or a symlink, append `blocked: dispatch-pinned mattpocock design skill is unavailable; the captain must refresh the plugin and relaunch the task` and stop.' \ + 'If either exact pinned path is missing, unreadable, or a symlink, append `blocked: dispatch-pinned mattpocock design skill is unavailable; refresh the captain-owned plugin install and relaunch the task` and stop.' \ '' \ '```json' \ "$DESIGN_SKILLS_BINDING" \ diff --git a/docs/architecture.md b/docs/architecture.md index a0e9e842796..3c9c160ab8d 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -267,8 +267,8 @@ The helper's header owns the exact signal detection, relocated-home limitation, ## Three task shapes -Ship tasks change projects and ship by project mode (`no-mistakes`, `direct-PR`, or `local-only`); design tasks use the same delivery modes for an interactive interview's ADR and never implement it; scout tasks leave standalone investigation reports at `data//report.md` and never push. -The intake and authority contract in `AGENTS.md` owns when design work or separate scout research is warranted, while the `design-profile` skill owns the ADR interview and installed-plugin boundary. +Ship tasks change projects and ship by project mode (`no-mistakes`, `direct-PR`, or `local-only`); design tasks use the same delivery modes for one planning conversation's ADR and never implement it; scout tasks leave standalone investigation reports at `data//report.md` and never push. +The intake and authority contract in `AGENTS.md` owns when design work or separate scout research is warranted, while the `design-profile` skill owns the unified planning conversation, ADR-only deliverable, and installed-plugin boundary. ## Dispatch profiles diff --git a/docs/examples/crew-dispatch.json b/docs/examples/crew-dispatch.json index 631d96c33bd..016545fd2a6 100644 --- a/docs/examples/crew-dispatch.json +++ b/docs/examples/crew-dispatch.json @@ -1,7 +1,7 @@ { "rules": [ { - "when": "The task is an interactive design interview whose tracked deliverable is an architectural decision record.", + "when": "The task is a design planning conversation whose tracked deliverable is an architectural decision record.", "use": [ { "harness": "codex", "effort": "xhigh" }, { "harness": "pi", "effort": "xhigh" }, diff --git a/docs/fleet-data-contracts.md b/docs/fleet-data-contracts.md index 523141d5d21..489aba8e362 100644 --- a/docs/fleet-data-contracts.md +++ b/docs/fleet-data-contracts.md @@ -74,7 +74,7 @@ A home with no brain has nothing to capture, so it never republishes and its sin ### The design task's plugin release -A design interview reads two skill files from the captain's installed `mattpocock-skills@mattpocock` plugin live, and that plugin auto-updates, so the instructions behind one design result need not be the instructions behind the next. +A design task reads two skill files from the captain-owned `mattpocock-skills@mattpocock` install, and that install can change between tasks, so the instructions behind one design result need not be the instructions behind the next. `design_skills` records the `plugin`, `version`, and `last_updated` that [`bin/fm-design-skills.sh`](../bin/fm-design-skills.sh) reported to the one resolve call that gates the dispatch, and the worker-facing brief carries the concrete skill paths from that same result rather than resolving again. The recorded release is therefore definitionally the one whose files informed the interview; if either pinned file moves before it can be read, the task stops loudly instead of falling forward to another release. A relaunch of the same design task reuses that recorded release and those pinned paths rather than resolving again. diff --git a/docs/scripts.md b/docs/scripts.md index b9c67198ea9..421d150d086 100644 --- a/docs/scripts.md +++ b/docs/scripts.md @@ -42,8 +42,8 @@ The shared no-mistakes gate refusal for fleet lifecycle entrypoints is summarize | `fm-backlog-receive.sh` | Idempotently ingest one confined remote handoff outbox through tasks-axi | | `fm-captain-hold.sh` | Hold tasks for the captain, record answers, gate completion, and report status/backlog divergence | | `fm-decision-hold.sh` | One-release compatibility shim mapping retired decision commands onto `fm-captain-hold.sh` | -| `fm-design-skills.sh` | Resolve named skills from the captain-installed mattpocock plugin without changing it | -| `fm-brief.sh` | Scaffold ship or interactive ADR design briefs with explicit `--mode`, plus scout, secondmate-charter, and Herdr-lab briefs, with intent/spec subsections and opt-in work-item traceability | +| `fm-design-skills.sh` | Resolve named skills from the captain-owned mattpocock plugin install without changing it | +| `fm-brief.sh` | Scaffold ship or one-conversation ADR design briefs with explicit `--mode`, plus scout, secondmate-charter, and Herdr-lab briefs, with intent/spec subsections and opt-in work-item traceability | | [`fm-dod-lib.sh`](../bin/fm-dod-lib.sh) | Own ship/design/scout worker role scope, task definitions of done, and the no-mistakes `--intent` contract | | `fm-herdr-lab.sh` | Provision and guardedly operate an isolated, never-default Herdr lab session | | `fm-install-herdr.sh` | Install CI's exact-version Herdr pin with official asset URL, SHA-256, and protocol checks | diff --git a/docs/verification/runtime-backends.md b/docs/verification/runtime-backends.md index 2258b244e27..1816b337112 100644 --- a/docs/verification/runtime-backends.md +++ b/docs/verification/runtime-backends.md @@ -55,7 +55,7 @@ The model names are representative test strings that verify axis transport; they ### Design dependency provenance -The `mattpocock-skills@mattpocock` plugin a design interview reads auto-updates under the captain's own setting, so the release that informed one design result need not be the release that informs the next. +The captain-owned `mattpocock-skills@mattpocock` install a design task reads can change between tasks, so the release that informed one design result need not be the release that informs the next. The 2026-08-26 addition makes `bin/fm-spawn.sh --design` resolve that plugin once at dispatch, bind the worker-facing brief to that result's concrete skill paths, and record `design_skills_plugin=`, `design_skills_version=`, and `design_skills_updated=` in the task's metadata, from where the completion manifest carries them past cleanup. A 2026-09-11 control-plane change makes `bin/fm-control.sh relaunch` keep that recorded release for a live `kind=design` worker instead of resolving again. [`docs/fleet-data-contracts.md`](../fleet-data-contracts.md#the-design-tasks-plugin-release) owns the recorded contract. diff --git a/tests/fm-brief.test.sh b/tests/fm-brief.test.sh index 1cd634d3a0d..3cfac063e8b 100755 --- a/tests/fm-brief.test.sh +++ b/tests/fm-brief.test.sh @@ -182,6 +182,8 @@ test_help_includes_entire_header() { "fm-brief.sh --help omitted the continue-branch argument" assert_contains "$help" "a branch held by another worktree blocks checkout, not push" \ "fm-brief.sh --help omitted the checkout-versus-push rule" + assert_contains "$help" "one planning conversation whose only tracked project" \ + "fm-brief.sh --help omitted the unified design planning contract" pass "fm-brief.sh: --help renders the complete header" } @@ -1485,7 +1487,7 @@ test_design_brief_is_harness_independent_and_adr_only() { assert_contains "$out" "(design, mode=no-mistakes" \ "design scaffold did not identify its task shape" brief="$home/data/design-task/brief.md" - assert_grep 'This is an interactive DESIGN task' "$brief" \ + assert_grep 'This is one DESIGN planning conversation' "$brief" \ "design brief did not declare the profile" assert_grep 'identical on Claude, Codex, and Pi' "$brief" \ "design brief did not carry the harness-independent binding contract" @@ -1495,8 +1497,18 @@ test_design_brief_is_harness_independent_and_adr_only() { "design brief told the worker to resolve a potentially different plugin release" assert_grep 'Never install, update, copy, vendor, pin, or modify that plugin' "$brief" \ "design brief allowed worker-owned plugin lifecycle" - assert_grep 'Use those skills for modeling and interrogation only' "$brief" \ + assert_grep 'Use those skills selectively for the design tree' "$brief" \ "design brief did not constrain the dependency capabilities" + assert_grep 'Do not require a visual review' "$brief" \ + "design brief made visual review mandatory" + assert_grep 'next unblocked decision on the design tree' "$brief" \ + "design brief did not keep dependency-aware questioning" + assert_grep 'rather than padding a ceremonial ADR' "$brief" \ + "design brief still required an ADR for routine configuration" + assert_grep 'Plugin lifecycle is captain-owned outside this repository' "$brief" \ + "design brief did not keep plugin lifecycle outside the worker" + assert_no_grep '/plugin' "$brief" \ + "design brief prescribed a competing /plugin updater" # shellcheck disable=SC2016 # Backticks are literal generated Markdown. assert_grep 'Do not create or update `CONTEXT.md`' "$brief" \ "design brief allowed the dependency to create a second tracked deliverable" @@ -1556,6 +1568,17 @@ test_design_brief_is_harness_independent_and_adr_only() { "design refusal did not preserve plugin ownership" assert_absent "$home/data/missing-design/brief.md" \ "design scaffold wrote a brief despite a missing dependency" + + out=$(FM_HOME="$home" "$ROOT/bin/fm-brief.sh" ship-no-planning-stack sample-ship --mode no-mistakes 2>&1) + rc=$? + expect_code 0 "$rc" "a well-specified ship brief should scaffold (got: $out)" + [ -s "$home/data/ship-no-planning-stack/brief.md" ] || fail "ship scaffold did not write a nonempty brief" + assert_no_grep 'grilling' "$home/data/ship-no-planning-stack/brief.md" \ + "a well-specified ship brief required a Matt interview" + assert_no_grep 'lavish-axi' "$home/data/ship-no-planning-stack/brief.md" \ + "a well-specified ship brief required a visual planning review" + assert_no_grep 'planning conversation' "$home/data/ship-no-planning-stack/brief.md" \ + "a well-specified ship brief inherited the design planning conversation" pass "fm-brief.sh: design profile is ADR-only and resolves identically across supported harnesses" } diff --git a/tests/fm-design-skills.test.sh b/tests/fm-design-skills.test.sh index 6aaa06510e8..8f5bb55aa2a 100755 --- a/tests/fm-design-skills.test.sh +++ b/tests/fm-design-skills.test.sh @@ -73,8 +73,10 @@ test_check_refuses_missing_capability() { expect_code 1 "$rc" "resolver should refuse a plugin missing domain-modeling" assert_contains "$out" "lacks required design skill" \ "resolver did not name the missing capability" - assert_contains "$out" "captain must refresh it with /plugin" \ + assert_contains "$out" "refresh the captain-owned plugin install" \ "resolver did not preserve captain-owned plugin lifecycle" + assert_contains "$out" "Workers must not install or copy it" \ + "resolver allowed a worker-owned plugin install" pass "design skill resolver refuses an incomplete plugin without working around it" } @@ -83,8 +85,10 @@ test_missing_registry_is_actionable() { out=$(FM_MATTPOCOCK_PLUGIN_REGISTRY="$TMP_ROOT/absent.json" "$RESOLVER" resolve 2>&1) rc=$? expect_code 1 "$rc" "resolver should refuse an absent plugin registry" - assert_contains "$out" "captain must install or refresh it with /plugin" \ + assert_contains "$out" "refresh the captain-owned plugin install" \ "absent-registry refusal did not name the owner action" + assert_contains "$out" "Workers must not install or copy it" \ + "absent-registry refusal allowed a worker-owned plugin install" pass "design skill resolver reports the captain-owned dependency action" } @@ -279,10 +283,11 @@ SH chmod +x "$fake_root/bin/fm-design-skills.sh" } -# The plugin auto-updates, so the release behind one design result need not be -# the release behind the next. The dispatch is the only moment that can observe -# the one the interview will actually read: by cleanup the plugin may have moved, -# and recording the wrong release is worse than recording none. +# The captain-owned plugin install can change between tasks, so the release +# behind one design result need not be the release behind the next. The dispatch +# is the only moment that can observe the one the interview will actually read: +# by cleanup the plugin may have moved, and recording the wrong release is worse +# than recording none. test_design_dispatch_records_the_release_it_resolved() { local rec home proj wt fakebin registry install meta out @@ -357,7 +362,7 @@ EOF meta="$home/state/design-binding.meta" dispatch_brief="$(meta_field "$meta" tasktmp)/brief.md" [ "$(cat "$count_file")" = 1 ] \ - || fail "design dispatch resolved the auto-updating plugin more than once" + || fail "design dispatch resolved the plugin more than once" [ "$(meta_field "$meta" design_skills_version)" = 1.2.0 ] \ || fail "task metadata did not record the single resolver result" [ -f "$dispatch_brief" ] \ @@ -373,6 +378,10 @@ EOF || fail "worker-facing brief did not carry the domain-modeling path from the recorded resolve" assert_not_contains "$(cat "$dispatch_brief")" "then use your read tool" \ "worker-facing brief retained the legacy instruction to resolve the plugin again" + assert_contains "$(cat "$dispatch_brief")" "one planning conversation" \ + "worker-facing brief did not keep grilling and domain-modeling inside one conversation" + assert_contains "$(cat "$dispatch_brief")" "refresh the captain-owned plugin install and relaunch the task" \ + "worker-facing brief lost captain-owned plugin refresh wording" assert_not_contains "$(cat "$dispatch_brief")" "$plugin_two" \ "worker-facing brief silently moved to a later resolver result" pass "one resolver result supplies both recorded provenance and pinned brief paths" @@ -455,8 +464,10 @@ EOF "$TMP_ROOT/spawn-refuse/absent.json" design-refused "$proj") status=$? expect_code 1 "$status" "a design spawn should refuse an unresolvable plugin" - assert_contains "$out" "captain must install or refresh it with /plugin" \ + assert_contains "$out" "refresh the captain-owned plugin install" \ "the refusal did not preserve captain-owned plugin lifecycle" + assert_contains "$out" "Workers must not install or copy it" \ + "the refusal allowed a worker-owned plugin install" assert_absent "$home/state/design-refused.meta" \ "a design task was dispatched without recording which plugin informed it" pass "a design dispatch refuses rather than launching an untraceable interview" diff --git a/tests/fm-outcome-manifest.test.sh b/tests/fm-outcome-manifest.test.sh index 2e97a9ea54f..f98e171d87b 100755 --- a/tests/fm-outcome-manifest.test.sh +++ b/tests/fm-outcome-manifest.test.sh @@ -174,8 +174,8 @@ test_design_manifest_is_valid() { pass "design is a valid durable outcome manifest kind" } -# The mattpocock plugin a design interview reads auto-updates, so the release -# that informed one design result need not be the one that informs the next. +# The captain-owned mattpocock install a design task reads can change, so the +# release that informed one design result need not be the one that informs the next. # Spawn resolves it at dispatch and records it in task metadata; the manifest is # what carries it past cleanup, which is the only reason it is still readable. test_design_manifest_carries_the_plugin_release() {