diff --git a/.gitignore b/.gitignore index ec06bab..93d633d 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,7 @@ coverage.out # Runtime data under .uf/ (databases, caches, locks, logs) .uf/workflows/ .uf/artifacts/ +.uf/feedback/ .uf/dewey/graph.db .uf/dewey/graph.db-shm .uf/dewey/graph.db-wal diff --git a/internal/agentkit/agentkit_test.go b/internal/agentkit/agentkit_test.go index 66bb1da..436a6d6 100644 --- a/internal/agentkit/agentkit_test.go +++ b/internal/agentkit/agentkit_test.go @@ -212,6 +212,190 @@ func TestHandoffMD_StructuralHardening(t *testing.T) { } } +func TestCoordinatorPrompt_StructuralResilience(t *testing.T) { + // Verify structural properties of coordinator.md that ensure + // compression resilience: identity-first opening, constraints + // before protocol, uppercase keywords, and behavioral parity. + data, err := content.ReadFile("content/agents/coordinator.md") + if err != nil { + t.Fatalf("read coordinator.md: %v", err) + } + + text := string(data) + + // Parse front matter and body once for all sub-tests. + if !strings.HasPrefix(text, "---\n") { + t.Fatal("coordinator.md: missing opening frontmatter delimiter") + } + endIdx := strings.Index(text[4:], "\n---") + if endIdx < 0 { + t.Fatal("coordinator.md: missing closing frontmatter delimiter") + } + frontmatter := text[4 : 4+endIdx] + body := text[4+endIdx+4:] // skip past "\n---\n" + + // Pre-compute shared indices used by multiple sub-tests. + constraintsIdx := strings.Index(body, "## Critical Constraints") + protocolIdx := strings.Index(body, "## Protocol") + toolsIdx := strings.Index(body, "## Available Tools") + bodyLower := strings.ToLower(body) + + constraintsSection := "" + if constraintsIdx >= 0 && protocolIdx >= 0 { + constraintsSection = body[constraintsIdx:protocolIdx] + } + + t.Run("YAML_frontmatter", func(t *testing.T) { + fmChecks := []string{"name: coordinator", "mode: subagent", "description:"} + for _, want := range fmChecks { + if !strings.Contains(frontmatter, want) { + t.Errorf("frontmatter missing %q", want) + } + } + }) + + t.Run("identity_first_opening", func(t *testing.T) { + // Find the first non-heading paragraph after the heading. + paragraphs := strings.Split(body, "\n\n") + firstParagraph := "" + for _, p := range paragraphs { + trimmed := strings.TrimSpace(p) + if trimmed == "" || strings.HasPrefix(trimmed, "#") { + continue + } + firstParagraph = trimmed + break + } + if firstParagraph == "" { + t.Fatal("coordinator.md: no non-heading paragraph found in body") + } + if !strings.Contains(firstParagraph, "NEVER") { + t.Error("first paragraph missing uppercase 'NEVER' keyword") + } + if !strings.Contains(strings.ToLower(firstParagraph), "reserve") { + t.Error("first paragraph missing file reservation prohibition ('reserve')") + } + }) + + t.Run("section_ordering", func(t *testing.T) { + if constraintsIdx < 0 { + t.Error("missing '## Critical Constraints' section") + } + if protocolIdx < 0 { + t.Error("missing '## Protocol' section") + } + if toolsIdx < 0 { + t.Error("missing '## Available Tools' section") + } + + if constraintsIdx >= 0 && protocolIdx >= 0 && constraintsIdx >= protocolIdx { + t.Error("'## Critical Constraints' must appear before '## Protocol'") + } + if protocolIdx >= 0 && toolsIdx >= 0 && protocolIdx >= toolsIdx { + t.Error("'## Protocol' must appear before '## Available Tools'") + } + }) + + t.Run("behavioral_rule_markers", func(t *testing.T) { + // All 7 behavioral rules (6 original + 1 codified) must be present (FR-006). + // Use a slice for deterministic iteration order. + type ruleCheck struct { + marker string + desc string + } + checks := []ruleCheck{ + {"comms_init", "comms init rule"}, + {"reserve", "file reservation rule"}, + {"edit code", "code editing prohibition rule"}, + {"forge_review", "review completions rule"}, + {"hivemind_store", "store learnings rule"}, + {"comms_inbox", "check inbox rule"}, + {"forge_broadcast", "broadcast context rule"}, + } + for _, rc := range checks { + if !strings.Contains(bodyLower, strings.ToLower(rc.marker)) { + t.Errorf("missing behavioral rule marker %q (%s)", rc.marker, rc.desc) + } + } + }) + + t.Run("uppercase_RFC2119_keywords", func(t *testing.T) { + if constraintsSection == "" { + t.Skip("no constraints section found") + } + if !strings.Contains(constraintsSection, "NEVER") { + t.Error("Critical Constraints section missing uppercase 'NEVER' keyword") + } + if !strings.Contains(constraintsSection, "MUST") { + t.Error("Critical Constraints section missing uppercase 'MUST' keyword") + } + // Verify every bullet line in constraints has an uppercase keyword. + for _, line := range strings.Split(constraintsSection, "\n") { + if strings.HasPrefix(strings.TrimSpace(line), "-") { + hasKeyword := strings.Contains(line, "NEVER") || + strings.Contains(line, "MUST") || + strings.Contains(line, "ALWAYS") + if !hasKeyword { + t.Errorf("constraint line missing uppercase RFC 2119 keyword: %q", line) + } + } + } + }) + + t.Run("review_before_complete_ordering", func(t *testing.T) { + // FR-003: forge_review MUST be called BEFORE forge_complete. + if constraintsSection == "" { + t.Skip("no constraints section found") + } + // Verify each required element is present individually. + if !strings.Contains(body, "forge_review") { + t.Error("missing forge_review reference in body") + } + if !strings.Contains(body, "forge_complete") { + t.Error("missing forge_complete reference in body") + } + // Verify the constraint line containing forge_review also mentions + // forge_complete with explicit ordering language (BEFORE), confirming + // the ordering is stated in a single rule rather than inferred from + // unrelated occurrences of "BEFORE". + foundOrderingRule := false + for _, line := range strings.Split(constraintsSection, "\n") { + if strings.Contains(line, "forge_review") && + strings.Contains(line, "forge_complete") && + strings.Contains(line, "BEFORE") { + foundOrderingRule = true + break + } + } + if !foundOrderingRule { + t.Error("no single constraint line connects forge_review, BEFORE, and forge_complete (FR-003)") + } + // Verify forge_review appears before forge_complete in constraints section. + reviewIdx := strings.Index(constraintsSection, "forge_review") + completeIdx := strings.Index(constraintsSection, "forge_complete") + if reviewIdx >= 0 && completeIdx >= 0 && reviewIdx >= completeIdx { + t.Error("forge_review must appear before forge_complete in Critical Constraints") + } + }) + + t.Run("compression_resilience", func(t *testing.T) { + bodyLines := strings.Split(body, "\n") + halfLen := len(bodyLines) / 2 + firstHalf := strings.Join(bodyLines[:halfLen], "\n") + firstHalfLower := strings.ToLower(firstHalf) + + if !strings.Contains(firstHalf, "NEVER") || !strings.Contains(firstHalfLower, "reserve") { + t.Error("first 50%% of body lines must contain file reservation prohibition (NEVER + reserve)") + } + if !strings.Contains(firstHalfLower, "forge_review") || !strings.Contains(firstHalfLower, "forge_complete") { + t.Error("first 50%% of body lines must contain review-before-complete ordering (forge_review + forge_complete)") + } + if !strings.Contains(firstHalfLower, "edit code") { + t.Error("first 50%% of body lines must contain code editing prohibition ('edit code')") + } + }) +} + func TestSkillTemplates_HaveNameField(t *testing.T) { // Walk the embedded content filesystem and verify every SKILL.md // has a "name: " field in its YAML frontmatter. diff --git a/internal/agentkit/content/agents/coordinator.md b/internal/agentkit/content/agents/coordinator.md index ba0b590..985aa87 100644 --- a/internal/agentkit/content/agents/coordinator.md +++ b/internal/agentkit/content/agents/coordinator.md @@ -6,16 +6,25 @@ mode: subagent # Forge Coordinator -Orchestrates work: decomposes tasks, spawns workers, monitors progress, reviews results. +You orchestrate workers but NEVER reserve files or edit code directly. You decompose tasks, spawn workers, monitor progress, and review results. Workers reserve their own files and make code changes — you coordinate and verify. -## Rules +## Critical Constraints -- Always initialize comms first (`comms_init`) -- Never reserve files (workers reserve their own) -- Review every worker completion (`forge_review`) -- Store learnings after forge completion (`hivemind_store`) -- Check inbox regularly for blocked workers (`comms_inbox`) -- Use `forge_broadcast` to share context updates with all workers +- NEVER reserve files — workers reserve their own +- NEVER edit code directly — workers handle all code changes +- MUST call `forge_review` for every worker completion BEFORE calling `forge_complete` +- MUST initialize comms first (`comms_init`) before any other operations + +## Protocol + +1. Initialize comms (`comms_init`) +2. Decompose task via `forge_decompose` or `forge_plan_prompt` +3. Spawn workers with `forge_spawn_subtask` +4. Check inbox regularly for blocked workers (`comms_inbox`) +5. Use `forge_broadcast` to share context updates with all workers +6. Review every worker completion (`forge_review`) +7. Mark completion ONLY after review passes (`forge_complete`) +8. Store learnings after forge completion (`hivemind_store`) ## Available Tools diff --git a/openspec/changes/coordinator-prompt-hardening/.openspec.yaml b/openspec/changes/coordinator-prompt-hardening/.openspec.yaml new file mode 100644 index 0000000..63fe932 --- /dev/null +++ b/openspec/changes/coordinator-prompt-hardening/.openspec.yaml @@ -0,0 +1,2 @@ +schema: unbound-force +created: 2026-08-02 diff --git a/openspec/changes/coordinator-prompt-hardening/design.md b/openspec/changes/coordinator-prompt-hardening/design.md new file mode 100644 index 0000000..e6a1a65 --- /dev/null +++ b/openspec/changes/coordinator-prompt-hardening/design.md @@ -0,0 +1,79 @@ +## Context + +The coordinator agent prompt at `internal/agentkit/content/agents/coordinator.md` is a 22-line embedded markdown file that defines the coordinator's behavioral contract. Analysis of all four agent prompt files in the project reveals a clear robustness hierarchy: + +| File | Structure | Compression Resilience | +|------|-----------|----------------------| +| `coordinator.md` | Single "Rules" section, 6 bullets | Fragile | +| `worker.md` | Checklist + Constraints sections | Moderate | +| `background-worker.md` | Constraints-first, capability negations | Moderate | +| `SKILL.md` (forge-coordination) | Multi-section, MUST/NEVER keywords, numbered protocols | Reference standard | + +The coordinator prompt is the most fragile. Its single critical negative constraint ("Never reserve files") sits at line 14 of 22 — 64% through the file — where a 50% truncation would drop it entirely. + +## Goals / Non-Goals + +### Goals +- Restructure `coordinator.md` so critical constraints survive context compression (DCP, summary, truncation) +- Adopt patterns proven in `worker.md`, `background-worker.md`, and the forge coordination skill +- Enforce explicit ordering: `forge_review` MUST precede `forge_complete` +- Maintain behavioral parity for existing rules; make the implicit `forge_review` → `forge_complete` ordering explicit + +### Non-Goals +- Changing the coordinator's actual behavioral contract (no removed rules — the `forge_review` → `forge_complete` ordering and the `NEVER edit code directly` prohibition are codifications of existing implicit behavior: the forge coordination skill defines review-before-complete ordering, and the coordinator's identity as a non-coding orchestrator implies it should not edit code directly) +- Hardening other agent prompts (`worker.md`, `background-worker.md`) — those are separate changes +- Adding runtime enforcement of constraints (e.g., tool-level guards blocking out-of-order calls) +- Modifying the forge coordination skill (`SKILL.md`) — it already follows the reference pattern + +## Decisions + +### 1. Identity-first opening with embedded constraints + +The first sentence of the file will state who the coordinator is AND what it must not do. Compressors prioritize opening content — an identity statement like "You are a coordinator. You orchestrate workers but NEVER reserve files or edit code directly." survives any reasonable summarization. + +**Rationale**: The forge coordination skill (`SKILL.md`) demonstrates this pattern at scale with its multi-section design. `background-worker.md` places constraints before capabilities, which is structurally sound though it does not embed constraints in the identity opening itself. + +### 2. Dedicated "Critical Constraints" section before workflow + +Negative constraints (NEVER reserve files, NEVER edit code directly) and mandatory ordering (MUST review before complete) move to a dedicated section with a strong header, positioned before the workflow protocol. + +**Rationale**: Position matters for compression. Content appearing earlier in a document is more likely to survive truncation. The forge coordination skill (`SKILL.md`) uses this pattern with its "File Reservation Rules" section. + +### 3. Uppercase severity keywords (MUST/NEVER/ALWAYS) + +All constraints use RFC 2119-style uppercase keywords for severity signaling. This matches the convention established in the forge coordination skill and the project constitution. + +**Rationale**: Uppercase keywords serve as compression-resistant markers. A compressor summarizing "NEVER reserve files" is more likely to preserve the prohibition than one summarizing "Never reserve files" in lowercase. + +### 4. Numbered protocol replacing unordered bullet list + +The current 6-bullet unordered list becomes a numbered checklist with explicit ordering. This makes the `forge_review` → `forge_complete` dependency visible and enforceable. + +**Rationale**: `worker.md` uses a 7-step numbered checklist that clearly conveys ordering. The forge coordination skill uses numbered protocols for both coordinator and worker flows. + +### 5. Preserve YAML front matter and "Available Tools" section + +The existing YAML front matter (`name`, `description`, `mode`) and the "Available Tools" footer remain unchanged. These are structural elements consumed by the agent framework. + +**Rationale**: Composability First — the file's interface with the agentkit embed system must not change. + +## Risks / Trade-offs + +### Longer prompt consumes more context window + +The restructured prompt will be approximately 30-40 lines (up from 22). This adds ~18 lines of context to every coordinator session. + +**Mitigation**: The added lines are structural (section headers, numbered steps) rather than new information. The constraint count grows from 6 to 7 (adding the explicit "NEVER edit code directly" prohibition, which codifies the coordinator's existing non-coding role). The trade-off is acceptable: 18 lines of context is trivial compared to the risk of a coordinator that silently drops quality gates. + +### Compression-resistance is heuristic, not guaranteed + +No prompt structure can guarantee survival under all possible compression strategies. The patterns used here (identity-first, constraints-before-workflow, uppercase keywords) are empirically effective but not provably optimal. + +**Mitigation**: This is a defense-in-depth measure. The forge coordination skill (`SKILL.md`) provides a redundant statement of the same constraints. Even if the coordinator prompt is compressed, the skill's constraints may survive in a separate part of the context. + +### No runtime enforcement + +This change relies on prompt engineering, not code-level guards. A sufficiently degraded context could still produce constraint violations. + +**Mitigation**: Runtime enforcement (e.g., blocking `forge_complete` if `forge_review` wasn't called) is a valid follow-up but is explicitly out of scope for this change. The prompt hardening provides immediate value with zero runtime risk. If the restructured prompt causes behavioral regression, the fix is to revert `coordinator.md` to its previous version — a single-file modification with no runtime dependencies, making rollback trivial. + diff --git a/openspec/changes/coordinator-prompt-hardening/proposal.md b/openspec/changes/coordinator-prompt-hardening/proposal.md new file mode 100644 index 0000000..e79f92e --- /dev/null +++ b/openspec/changes/coordinator-prompt-hardening/proposal.md @@ -0,0 +1,63 @@ +## Why + +The coordinator agent prompt (`internal/agentkit/content/agents/coordinator.md`) is 22 lines with all behavioral constraints expressed as 6 bullet points in a single "Rules" section. When tools like DCP compress session context, critical constraints — particularly the prohibition on file reservation and the mandatory review-before-complete ordering — are likely to be lost or weakened. + +This is the same class of vulnerability identified in [unbound-force/unbound-force#346](https://github.com/unbound-force/unbound-force/issues/346). A coordinator that silently drops constraints under compression could reserve files (causing deadlocks with workers), skip reviews (bypassing quality gates), or call `forge_complete` before `forge_review` (circumventing verification). + +Fixes [#46](https://github.com/unbound-force/replicator/issues/46). + +## What Changes + +Restructure the coordinator agent prompt to survive context compression by applying patterns already proven in the project's other agent files (`worker.md`, `background-worker.md`) and the forge coordination skill (`SKILL.md`). + +Specific changes: +1. Add an opening identity statement that embeds key constraints inline, ensuring compressors retain them in any summary's opening sentence. +2. Create a dedicated "Critical Constraints" section with uppercase severity keywords (MUST/NEVER) positioned before the workflow section. +3. Add an explicit ordered protocol (numbered checklist) that enforces `forge_review` before `forge_complete`. +4. Separate boundary rules (what the coordinator must NOT do) from procedural steps (what it does). + +## Capabilities + +### New Capabilities +- None — this change modifies an existing embedded asset, not runtime code. + +### Modified Capabilities +- `coordinator agent prompt`: Restructured for compression resilience with explicit ordering, identity reinforcement, and prominent negative constraints. + +### Removed Capabilities +- None. + +## Impact + +- **File**: `internal/agentkit/content/agents/coordinator.md` (single file change) +- **Embedded asset**: The file is embedded via `go:embed` into the binary; changes take effect at next build. +- **Behavioral**: No runtime code changes. The coordinator's behavioral contract is preserved — this change makes existing constraints more explicit, not different. +- **Testing**: The agent prompt is an embedded text asset. Existing parity tests and embed tests cover the file's inclusion. No new test infrastructure needed, though a structure validation test could verify constraint positioning. + +## Constitution Alignment + +Assessed against the Replicator constitution (`.specify/memory/constitution.md`), which extends the Unbound Force org constitution v1.1.0. + +### I. Autonomous Collaboration + +**Assessment**: PASS + +The coordinator prompt defines how the coordinator collaborates with workers through well-defined MCP tools and comms messaging. This change reinforces that contract by making the separation of concerns (coordinator orchestrates, workers reserve files and edit code) more explicit and compression-resistant. + +### II. Composability First + +**Assessment**: N/A + +This change modifies an embedded text asset. It does not affect standalone functionality or introduce dependencies. + +### III. Observable Quality + +**Assessment**: PASS + +The restructured prompt explicitly requires the coordinator to call `forge_review` for every worker completion before `forge_complete`, strengthening the quality gate. The use of uppercase MUST/NEVER keywords aligns with the severity signaling convention used in the forge coordination skill. + +### IV. Testability + +**Assessment**: PASS + +The change affects a single embedded markdown file. The file is already covered by embed tests. A structural test MUST be added to verify that critical constraints appear before workflow steps, ensuring the compression-resilience property is maintained over time. diff --git a/openspec/changes/coordinator-prompt-hardening/specs/coordinator-prompt-structure.md b/openspec/changes/coordinator-prompt-hardening/specs/coordinator-prompt-structure.md new file mode 100644 index 0000000..b0d2b52 --- /dev/null +++ b/openspec/changes/coordinator-prompt-hardening/specs/coordinator-prompt-structure.md @@ -0,0 +1,113 @@ +## ADDED Requirements + +### FR-001: Identity-first opening statement + +The coordinator prompt MUST begin (after YAML front matter) with an identity statement that embeds critical constraints inline. The opening paragraph MUST include the coordinator's role AND at least one key prohibition (e.g., NEVER reserves files). + +#### Scenario: Opening paragraph contains prohibition + +- **GIVEN** the coordinator prompt after YAML front matter +- **WHEN** the first paragraph is extracted +- **THEN** it MUST contain the uppercase keyword "NEVER" and the phrase "reserve files" (or equivalent prohibition) + +### FR-002: Dedicated critical constraints section + +The coordinator prompt MUST contain a section titled "Critical Constraints" (or equivalent strong header) that appears BEFORE any workflow or protocol section. This section MUST contain all negative constraints and mandatory ordering rules using uppercase RFC 2119 keywords (MUST, NEVER, ALWAYS). + +#### Scenario: Critical constraints appear in first half of file + +- **GIVEN** the coordinator prompt has N total lines after YAML front matter +- **WHEN** only the first floor(N/2) lines after front matter are retained +- **THEN** the retained lines MUST contain: (1) the file reservation prohibition (`NEVER` + `reserve`), (2) the review-before-complete ordering (`forge_review` + `forge_complete`), and (3) the code editing prohibition (`NEVER` + `edit code`) — these are the three critical constraints that must survive truncation + +Note: The 50% threshold is a conservative structural heuristic, not a measured DCP compression ratio. The defense-in-depth rationale (redundancy with the forge coordination skill) mitigates the inherent non-determinism of LLM-based compression. + +### FR-003: Explicit review-before-complete ordering + +The coordinator prompt MUST state that `forge_review` MUST be called for every worker completion BEFORE calling `forge_complete`. This ordering constraint MUST appear in both the critical constraints section and the numbered protocol. This codifies an ordering that was previously implicit in the forge coordination skill (steps 8→9 in the Coordinator Protocol) but absent from the coordinator prompt itself. + +#### Scenario: Ordering constraint present in file structure + +- **GIVEN** the restructured coordinator prompt +- **WHEN** the file content is searched +- **THEN** it MUST contain both the strings `forge_review` and `forge_complete` with explicit ordering language (e.g., "BEFORE", "prior to", sequential numbering where review precedes complete) + +### FR-004: Uppercase severity keywords + +All behavioral constraints in the coordinator prompt MUST use uppercase RFC 2119 keywords (MUST, MUST NOT, NEVER, ALWAYS, SHALL) for severity signaling. + +#### Scenario: Constraint keyword casing + +- **GIVEN** the coordinator prompt contains behavioral constraints +- **WHEN** lines containing prohibitions or mandatory behaviors are extracted +- **THEN** each such line MUST contain at least one uppercase RFC 2119 keyword (NEVER, MUST, ALWAYS) rather than lowercase equivalents + +### FR-005: YAML front matter preservation + +The restructured coordinator prompt MUST preserve the original YAML front matter values. + +#### Scenario: Front matter parity + +- **GIVEN** the restructured coordinator prompt +- **WHEN** the YAML front matter is parsed +- **THEN** it MUST contain `name: coordinator`, a `description` field, and `mode: subagent` + +### FR-006: Behavioral rule presence markers + +Each of the 7 behavioral rules (6 original + 1 codified from the coordinator's implicit non-coding role) MUST be verifiable by the presence of specific string patterns in the restructured file: + +| Rule | Required Pattern | +|------|-----------------| +| comms init | `comms_init` | +| no file reservation | `NEVER` + `reserve` | +| no code editing | `NEVER` + `edit code` | +| review completions | `forge_review` | +| store learnings | `hivemind_store` | +| check inbox | `comms_inbox` | +| broadcast context | `forge_broadcast` | + +#### Scenario: Automated parity check + +- **GIVEN** the restructured coordinator prompt +- **WHEN** file content is searched for each required pattern in the table above +- **THEN** all 7 patterns MUST be found in the file + +## MODIFIED Requirements + +### FR-007: Coordinator behavioral rules restructuring + +Previously: Six unordered bullet points in a single "Rules" section with lowercase constraint language ("Always", "Never"). + +The coordinator's behavioral rules MUST be restructured into: +1. A "Critical Constraints" section containing prohibitions and mandatory orderings (positioned first) +2. A numbered "Protocol" section containing the ordered workflow steps + +The 6 original behavioral rules MUST remain with equivalent semantics. Additionally, the implicit `forge_review` → `forge_complete` ordering (present in the forge coordination skill but absent from the coordinator prompt) and the `NEVER edit code directly` prohibition (implicit in the coordinator's non-coding orchestrator role) are made explicit — these are codifications of existing behavior, bringing the total to 7 verifiable rules. + +#### Scenario: Behavioral parity verification + +- **GIVEN** the original coordinator prompt contains 6 rules (comms init, no file reservation, review completions, store learnings, check inbox, broadcast context) plus the implicit no-code-editing role +- **WHEN** the restructured prompt is compared to the original +- **THEN** all 7 rules MUST still be present (verified by pattern matching per FR-006), and the `forge_review` → `forge_complete` ordering MUST be explicitly stated + +### FR-008: Coordinator prompt structure ordering + +Previously: Single flat structure (front matter → header → description → rules → tools). + +The coordinator prompt MUST follow this section ordering: +1. YAML front matter +2. Identity heading and opening statement (with embedded constraints) +3. Critical Constraints section +4. Protocol section (numbered workflow steps) +5. Available Tools section + +#### Scenario: Section ordering validation + +- **GIVEN** the restructured coordinator prompt +- **WHEN** section headers (lines starting with `##`) are parsed in document order +- **THEN** the "Critical Constraints" header MUST appear before the "Protocol" header, and both MUST appear before the "Available Tools" header + +## REMOVED Requirements + +None. No existing requirements are removed by this change. + diff --git a/openspec/changes/coordinator-prompt-hardening/tasks.md b/openspec/changes/coordinator-prompt-hardening/tasks.md new file mode 100644 index 0000000..be18d20 --- /dev/null +++ b/openspec/changes/coordinator-prompt-hardening/tasks.md @@ -0,0 +1,51 @@ + + +## 1. Restructure coordinator prompt + +- [x] 1.1 Rewrite `internal/agentkit/content/agents/coordinator.md` with the hardened structure: + - Preserve existing YAML front matter (`name: coordinator`, `description`, `mode: subagent`) + - Add identity-first opening statement embedding key constraints (NEVER reserves files, NEVER edits code directly) + - Add "Critical Constraints" section with uppercase MUST/NEVER keywords, positioned before workflow + - Include explicit ordering: MUST call `forge_review` for every worker BEFORE `forge_complete` (codifying implicit ordering from forge coordination skill) + - Convert unordered rules list to numbered "Protocol" section with explicit workflow ordering + - Preserve "Available Tools" section at the end + - Maintain behavioral parity: all 7 rules (6 original + 1 codified: comms init, no file reservation, no code editing, review completions, store learnings, check inbox, broadcast context) MUST remain with equivalent semantics — verified by the presence of these markers: `comms_init`, `reserve`, `edit code`, `forge_review`, `hivemind_store`, `comms_inbox`, `forge_broadcast` + +## 2. Automated structural test + +- [x] 2.1 [P] Write a structural test in `internal/agentkit/agentkit_test.go` that reads the embedded `coordinator.md` and verifies: + - YAML front matter contains `name: coordinator` and `mode: subagent` + - The first paragraph after front matter contains the uppercase keyword "NEVER" and the phrase "reserve" (identity-first prohibition) + - A "Critical Constraints" section header appears before a "Protocol" section header + - Both appear before the "Available Tools" section header + - All 7 behavioral rule markers are present: `comms_init`, `reserve`, `edit code`, `forge_review`, `hivemind_store`, `comms_inbox`, `forge_broadcast` + - Constraint lines use uppercase RFC 2119 keywords (MUST, NEVER, ALWAYS) + - The first 50% of lines after front matter contain both the file reservation prohibition and the review-before-complete ordering + +## 3. Build verification + +- [x] 3.1 Run `make build` to verify the embedded asset compiles without errors +- [x] 3.2 Run `make test` to verify all tests pass (including the new structural test) + +## 4. Documentation + +- [x] 4.1 [P] Verify AGENTS.md does not need updates (coordinator prompt structure is an internal detail, not a convention documented in AGENTS.md) +- [x] 4.2 [P] Verify GoDoc comments on the agentkit embed package do not need updates + +## 5. Constitution alignment + +- [x] 5.1 Verify the change aligns with Constitution Principle I (Autonomous Collaboration): the restructured prompt reinforces artifact-based collaboration and separation of concerns between coordinator and workers +- [x] 5.2 Verify the change aligns with Constitution Principle IV (Testability): the structural test verifies all compression-resilience properties are maintained + + +