diff --git a/.agents/skills/secondmate-provisioning/SKILL.md b/.agents/skills/secondmate-provisioning/SKILL.md index 39641f5cf..10baf0cbe 100644 --- a/.agents/skills/secondmate-provisioning/SKILL.md +++ b/.agents/skills/secondmate-provisioning/SKILL.md @@ -17,12 +17,15 @@ Keep the always-inline routing rules in `AGENTS.md` authoritative: route by natu ## Routing table -`data/secondmates.md` has one line per persistent domain supervisor: +`data/secondmates.md` has one parser-compatible line per persistent domain supervisor: ```markdown -- - (home: ; scope: ; projects: , ; added ) +- - (home: ; scope: ; projects: , ; added ) ``` +Each registry entry stays concise and single-line: the summary is one sentence naming the durable charter, `scope:` is the natural-language intake responsibility, `projects:` is the non-exclusive clone list, and any extra prose is limited to genuinely domain-specific hard rules that change routing or safety for that secondmate. +The `home:` path points to the seeded home containing `data/charter.md`; no extra registry pointer field is needed. +The home-seeded `data/charter.md` is the sole owner of boilerplate idle-by-default behavior, the normal delegation lifecycle, and standard escalation contracts, so point to that charter rather than restating those contracts in the registry entry. The `scope:` field is used during intake. The `projects:` field is a non-exclusive clone list, not ownership. @@ -41,9 +44,9 @@ Pass `--no-projects` instead of a project list to scaffold a project-less charte `--no-projects` is mutually exclusive with a project list, and omitting both still fails loudly, so an accidental omission is never mistaken for a deliberate project-less seed. Re-seeding a populated home as project-less is refused non-destructively when the home contains project clones or `data/projects.md` entries. Retire or clean that home first, and re-scaffold a stale project-bearing charter with `--no-projects` before seeding. -Keep the charter focused on the persistent responsibility, available project clones, escalation back to the main firstmate status file, and the requests-from-main-firstmate contract. -The scaffold's definition of done encodes the idle-by-default contract: on startup the secondmate reconciles only its own in-flight work and then waits for routed tasks, never self-initiating a survey or audit. -Preserve that wording when filling the charter, including the marker rule that marked supervisor requests return through status or a doc pointer while unmarked captain messages stay conversational. +Keep custom charter text focused on the persistent responsibility, available project clones, and genuinely domain-specific hard rules. +The scaffolded charter, later copied to `data/charter.md`, owns the standard lifecycle and escalation wording. +Preserve the generated charter sections unless the domain genuinely needs a hard rule. Provision the persistent home and registry entry after the charter is filled: diff --git a/docs/architecture.md b/docs/architecture.md index 4c47f6152..044149810 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -161,7 +161,7 @@ An explicit per-spawn harness or raw launch command does not inherit model or ef `config/crew-dispatch.json` is inherited too; secondmates use the same natural-language dispatch profiles when spawning their own crewmates. `config/backlog-backend` is inherited too; absent or `tasks-axi` selects the default tasks-axi backlog backend, while `manual` forces routine backlog updates to hand-editing across the fleet without disabling validated handoff delegation. -The `data/secondmates.md` line schema and the secondmate environment variables are documented in [configuration.md](configuration.md). +The `data/secondmates.md` line contract is owned by the [`secondmate-provisioning` skill](../.agents/skills/secondmate-provisioning/SKILL.md#routing-table), and the secondmate environment variables are documented in [configuration.md](configuration.md). ## Project modes are explicit diff --git a/docs/configuration.md b/docs/configuration.md index 8256048c9..6dd0a1d0e 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -121,7 +121,8 @@ The file is created lazily on first learning and follows the same dated, evidenc ## Secondmate routes (data/secondmates.md) Persistent secondmate routes live locally in `data/secondmates.md`. -Each line records the secondmate id, charter summary, absolute home path, natural-language scope, project clone list, and added date; `fm-home-seed.sh validate` refuses duplicate ids, duplicate homes, and nested or overlapping homes. +The concise single-line route contract is owned by the [`secondmate-provisioning` skill](../.agents/skills/secondmate-provisioning/SKILL.md#routing-table), including the parser-compatible fields, one-sentence summary requirement, `home:` pointer to the seeded charter, and limit on extra registry prose. +`fm-home-seed.sh validate` refuses duplicate ids, duplicate homes, and nested or overlapping homes. The main first mate routes by reading those scopes with judgment; the project list is provisioning data, not exclusive ownership. Use `fm-home-seed.sh - {...|--no-projects}` to lease a fresh firstmate worktree for the secondmate home. Use the deliberate `--no-projects` signal only for a firstmate-repo domain that needs no separate project clones. @@ -134,6 +135,7 @@ Secondmate routes cover `no-mistakes` and `direct-PR` projects; `local-only` pro For `no-mistakes` projects, seeding initializes only projects newly cloned into a secondmate home and refuses to mutate a preexisting clone that is not already initialized. After creating a secondmate, move existing main-backlog queued items that you have judged in-scope with `fm-backlog-handoff.sh ...`; it is idempotent and refuses In flight, Done, or non-secondmate homes. Set `FM_SECONDMATE_CHARTER` to seed from inline charter text when no filled charter brief exists; set `FM_SECONDMATE_SCOPE` when the routing scope should differ from the charter text. +The seeded home's `data/charter.md` owns the standard secondmate lifecycle and escalation contract; the route file points to it through the existing `home:` field instead of adding another pointer. Each seed writes an `.fm-secondmate-home` identity marker at the home root. The tracked root `.gitignore` ignores that marker, so validation can read it without making a freshly seeded home appear dirty to porcelain-based safety checks. This does not relax protection for any other untracked file. diff --git a/tests/fm-instruction-owners.test.sh b/tests/fm-instruction-owners.test.sh index 82a0e5105..62a0e114b 100755 --- a/tests/fm-instruction-owners.test.sh +++ b/tests/fm-instruction-owners.test.sh @@ -12,6 +12,7 @@ PROJECT="$ROOT/.agents/skills/project-management/SKILL.md" HARNESS="$ROOT/.agents/skills/harness-adapters/SKILL.md" CODING="$ROOT/.agents/skills/firstmate-coding-guidelines/SKILL.md" RECOVERY="$ROOT/.agents/skills/stuck-crewmate-recovery/SKILL.md" +SECONDMATE="$ROOT/.agents/skills/secondmate-provisioning/SKILL.md" CONFIG="$ROOT/docs/configuration.md" AGENTS="$ROOT/AGENTS.md" BRIEF="$ROOT/bin/fm-brief.sh" @@ -111,6 +112,46 @@ test_shared_authoring_requirements_are_owned() { pass "firstmate-coding-guidelines owns compatibility review and deterministic enforcement" } +test_secondmate_registry_contract_stays_concise() { + local guidance routing_section schema_line + routing_section=$(awk ' + /^## Routing table$/ { found = 1 } + found && /^## Charter and seed$/ { exit } + found { print } + ' "$SECONDMATE") + guidance=$(awk ' + /^## Routing table$/ { found = 1 } + found && /^## Backlog handoff$/ { exit } + found { print } + ' "$SECONDMATE") + schema_line="- - (home: ; scope: ; projects: , ; added )" + assert_contains "$routing_section" "$schema_line" \ + "secondmate routing table lost the parser-compatible single-line schema" + assert_contains "$routing_section" "Each registry entry stays concise and single-line" \ + "secondmate routing table no longer requires concise single-line entries" + assert_contains "$routing_section" "genuinely domain-specific hard rules" \ + "secondmate routing table no longer limits extra prose to domain-specific hard rules" + assert_contains "$routing_section" "The home-seeded \`data/charter.md\` is the sole owner of boilerplate idle-by-default behavior, the normal delegation lifecycle, and standard escalation contracts" \ + "secondmate routing table lost the explicit charter ownership pointer" + assert_contains "$routing_section" "no extra registry pointer field is needed" \ + "secondmate routing table no longer explains why the existing home field is the charter pointer" + for phrase in \ + "go idle and wait silently" \ + "Act only on tasks" \ + "never spawn a survey" \ + "run normal firstmate bootstrap" \ + "escalation back to the main firstmate status file" \ + "requests-from-main-firstmate contract" \ + "waits for routed tasks, never self-initiating a survey or audit" \ + "marked supervisor requests return through status" \ + "unmarked captain messages stay conversational"; do + if printf '%s\n' "$guidance" | grep -F "$phrase" >/dev/null; then + fail "secondmate provisioning guidance restated charter boilerplate: $phrase" + fi + done + pass "secondmate registry guidance keeps concise routes and points to the charter" +} + test_state_startup_and_ordinary_recovery_placement() { assert_grep "single owner of the top-level operational-home layout" "$CONFIG" \ "configuration docs do not own the operational state layout" @@ -185,6 +226,7 @@ test_diagnostic_owner_covers_causal_procedure test_project_management_owner_covers_guarded_operations test_generic_effort_fallback_respects_precedence test_shared_authoring_requirements_are_owned +test_secondmate_registry_contract_stays_concise test_state_startup_and_ordinary_recovery_placement test_compressed_agents_owner_map test_compressed_agents_retains_authority_and_supervision_safety