carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

Seeded Instances, Plan 1: Copy Deck and Gate Implementation Plan

> For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Pin every user-facing string of the seeded-instances series in one reviewed deck, get Paul's copy review on all of it before any code uses a string, and add the gate that keeps the code and the deck in step.

Architecture: The deck is a markdown table (docs/superpowers/plans/2026-10-08-seeded-instances-copy.md, written with this plan) in the moves copy record's format. A new repo-root test file, approved_seeded_copy_test.go, parses it with the moves record's parser (generalised to take an id pattern), pins the inventory, refuses em dashes, requires every row approved, and checks that every row a later plan has wired appears in the file the row names. The review itself runs through the copy-review skill.

Tech Stack: Go 1.25 tests at the repo root (package main), the existing approved_moves_copy_test.go helpers (copyRow, checkMovesInventory, checkMovesApplied, checkMovesNoEmDash, movesTextApplied).

Spec: docs/superpowers/specs/2026-10-08-seeded-instances-design.md rev 7, §7 ("Every CLI and console string goes through copy review before it's written into the code"). Index: 2026-10-08-seeded-instances-0-index.md.

Global Constraints

  • Every user-facing string (CLI output, errors, console messages, docs prose) is copy-reviewed by Paul before it is written into code; implementation tasks use only strings from the copy deck. Copy voice: no em dashes; full stops and new sentences instead.
  • CI cross-compiles darwin and windows: linux-only code needs //go:build linux plus a !linux stub.
  • Local test commands: targeted tests (go test ./internal/<pkg>/ -run TestX), not the full suite in a loop. Inside the sandbox the default Go build cache may be read-only; if so use GOCACHE=$TMPDIR/gocache re-exported per command. Socket tests need a short TMPDIR (TMPDIR=/tmp/g). The local gate may need GOFLAGS=-buildvcs=false.
  • Heavy loops run under nice; no -race full suite until the end of a plan.
  • Production use needs nested boxes; the claim-armed path is refused here and left to #250 (spec §3.4).
  • Commits end with Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>.
  • Do not change any moves row, id or sentence. The moves gate's own tests (go test . -run 'TestMoves|TestParseMovesCopyTable|TestCopyRowPath') pass unchanged after Task 1.

Review Focus

  1. A deck row whose text holds a pipe (a | b) must parse as one text cell, as the moves parser already does. Test in Task 1 (TestParseSeededCopyTableKeepsPipes).
  2. A row deleted from the deck by mistake, or a new row added without pinning it, must fail the inventory check both ways. Test in Task 1 (TestSeededCopyInventoryBitesBothWays).
  3. A wired id whose text was later reworded in code (or never written) must fail, naming the id and the file. Test in Task 1 (TestSeededWiredCheckBites).
  4. Paul's edits are applied verbatim: a reworded row keeps its id, and a row he deletes is removed from both the deck and seededCopyIDs in the same commit. Checked by Task 2 Step 5's inventory run.
  5. A deck that still holds a pending row after the review must fail TestSeededCopyIsApproved, so no later plan can start on unreviewed text. Test in Task 2.

---

File structure

| path | responsibility | |---|---| | docs/superpowers/plans/2026-10-08-seeded-instances-copy.md | the deck (already written; Task 2 updates statuses and text) | | approved_moves_copy_test.go | parseMovesCopyTable becomes a wrapper over the new parseCopyTable | | approved_seeded_copy_test.go | the seeded gate: inventory, em dash, approval, wired rows applied |

---

Task 1: The seeded copy gate

Files:

  • Modify: approved_moves_copy_test.go:105-164 (parseMovesCopyTable)
  • Create: approved_seeded_copy_test.go

Interfaces:

  • Consumes: copyRow, checkMovesInventory(rows []copyRow, want []string) []string, checkMovesApplied(rows []copyRow, root string) []string, checkMovesNoEmDash(rows []copyRow) []string, reportAll(t, problems) (all in approved_moves_copy_test.go).
  • Produces: parseCopyTable(src, name string, ids *regexp.Regexp, idKind string) ([]copyRow, error); seededCopyRecordPath; seededCopyIDs []string (SD-N1 to SD-N116); seededWiredIDs []string (empty here; each later plan's wiring task appends to it); mustParseSeededRecord(t) []copyRow; checkSeededWired(rows []copyRow, wired []string, root string) []string; checkSeededApproved(rows []copyRow) []string.
  • [ ] Step 1: Write the failing tests

Create approved_seeded_copy_test.go:

// approved_seeded_copy_test.go
package main

import (
	"regexp"
	"strconv"
	"strings"
	"testing"
)

// The copy gate of the seeded-instances series (#260, #261). The deck is
// reviewed BEFORE any code speaks a sentence (the reverse of the moves
// record, where code came first), so the gate has two lists:
//
//   - seededCopyIDs: every row the deck holds, pinned both ways;
//   - seededWiredIDs: the rows code already speaks. Each plan's wiring task
//     appends the ids it wired, and the gate checks each one's text is in
//     the file its row names.

const seededCopyRecordPath = "docs/superpowers/plans/2026-10-08-seeded-instances-copy.md"

var seededCopyIDPattern = regexp.MustCompile(`^SD-N[0-9]+$`)

var seededCopyIDs = func() []string {
	ids := make([]string, 0, 116)
	for n := 1; n <= 116; n++ {
		ids = append(ids, "SD-N"+strconv.Itoa(n))
	}
	return ids
}()

// seededWiredIDs grows as the plans wire rows. Plan 1 wires none.
var seededWiredIDs = []string{}

func mustParseSeededRecord(t *testing.T) []copyRow {
	t.Helper()
	rows, err := parseSeededCopyRecord(seededCopyRecordPath)
	if err != nil {
		t.Fatal(err)
	}
	if len(rows) == 0 {
		t.Fatalf("%s has no rows", seededCopyRecordPath)
	}
	return rows
}

func TestSeededCopyRecordIsComplete(t *testing.T) {
	reportAll(t, checkMovesInventory(mustParseSeededRecord(t), seededCopyIDs))
}

func TestSeededCopyHasNoEmDash(t *testing.T) {
	reportAll(t, checkMovesNoEmDash(mustParseSeededRecord(t)))
}

func TestSeededWiredCopyIsApplied(t *testing.T) {
	reportAll(t, checkSeededWired(mustParseSeededRecord(t), seededWiredIDs, "."))
}

func TestParseSeededCopyTableKeepsPipes(t *testing.T) {
	src := "| id | file | text | status |\n|---|---|---|---|\n" +
		"| SD-N1 | a.go | one | two | pending |\n"
	rows, err := parseCopyTable(src, "x.md", seededCopyIDPattern, "an SD-N<n>")
	if err != nil {
		t.Fatal(err)
	}
	if len(rows) != 1 || rows[0].Text != "one | two" {
		t.Fatalf("rows = %+v", rows)
	}
	if _, err := parseCopyTable(strings.Replace(src, "SD-N1", "MV-N1", 1), "x.md", seededCopyIDPattern, "an SD-N<n>"); err == nil ||
		!strings.Contains(err.Error(), "is not an SD-N<n> id") {
		t.Fatalf("a moves id in the seeded deck: err = %v", err)
	}
}

func TestSeededCopyInventoryBitesBothWays(t *testing.T) {
	rows := []copyRow{{"SD-N1", "a.go", "x", "pending"}, {"SD-N3", "a.go", "y", "pending"}}
	got := checkMovesInventory(rows, []string{"SD-N1", "SD-N2"})
	if len(got) != 2 {
		t.Fatalf("problems = %q, want a missing SD-N2 and an extra SD-N3", got)
	}
}

func TestSeededWiredCheckBites(t *testing.T) {
	dir := t.TempDir()
	writeFile(t, dir, "a.go", "package a\n\nvar s = \"hello there\"\n")
	rows := []copyRow{
		{"SD-N1", "a.go", "hello there", "approved"},
		{"SD-N2", "a.go", "goodbye", "approved"},
		{"SD-N3", "a.go", "hello there", "pending"},
	}
	if got := checkSeededWired(rows, []string{"SD-N1"}, dir); len(got) != 0 {
		t.Errorf("a wired, applied row: problems = %q", got)
	}
	if got := checkSeededWired(rows, []string{"SD-N2"}, dir); len(got) != 1 || !strings.Contains(got[0], "SD-N2") {
		t.Errorf("a wired row whose text is missing: problems = %q", got)
	}
	if got := checkSeededWired(rows, []string{"SD-N3"}, dir); len(got) != 1 || !strings.Contains(got[0], "pending") {
		t.Errorf("a wired pending row: problems = %q", got)
	}
	if got := checkSeededWired(rows, []string{"SD-N9"}, dir); len(got) != 1 || !strings.Contains(got[0], "no row") {
		t.Errorf("a wired id with no row: problems = %q", got)
	}
}

If the repo root has no writeFile(t, dir, name, body string) test helper yet (grep -n 'func writeFile' *_test.go), add this one at the end of the new file:

func writeFile(t *testing.T, dir, name, body string) {
	t.Helper()
	if err := os.WriteFile(filepath.Join(dir, name), []byte(body), 0o644); err != nil {
		t.Fatal(err)
	}
}

with "os" and "path/filepath" added to the imports. If a helper of that name already exists with another signature, name this one writeSeededFile and use that name in TestSeededWiredCheckBites.

  • [ ] Step 2: Run the tests to verify they fail

Run: go test . -run 'TestSeeded|TestParseSeeded' -count=1 Expected: FAIL to compile: undefined: parseSeededCopyRecord, undefined: parseCopyTable, undefined: checkSeededWired.

  • [ ] Step 3: Generalise the parser and add the seeded helpers

In approved_moves_copy_test.go, rename the body of parseMovesCopyTable to parseCopyTable with the id pattern as a parameter, and keep parseMovesCopyTable as a wrapper so every moves caller and every moves error message is unchanged:

func parseMovesCopyTable(src, name string) ([]copyRow, error) {
	return parseCopyTable(src, name, movesCopyIDPattern, "an M-C<n>, MV-N<n> or HC5-N<n>")
}

// parseCopyTable reads a copy record's tables: the moves record and the
// seeded deck share the format. ids is the record's id pattern and idKind
// names it in the refusal.
func parseCopyTable(src, name string, ids *regexp.Regexp, idKind string) ([]copyRow, error) {
	var rows []copyRow
	seen := map[string]int{}
	inTable := false
	for n, line := range strings.Split(src, "\n") {
		// ... the existing loop body, unchanged, except the id check:
		if !ids.MatchString(r.ID) {
			return nil, fmt.Errorf("%s: %q is not %s id", at, r.ID, idKind)
		}
		// ... rest unchanged
	}
	return rows, nil
}

The only edit inside the loop is that one if: the old line read if !movesCopyIDPattern.MatchString(r.ID) { return nil, fmt.Errorf("%s: %q is not an M-C<n>, MV-N<n> or HC5-N<n> id", at, r.ID) }, and the new one produces the same bytes for the moves record.

Append to approved_seeded_copy_test.go:

func parseSeededCopyRecord(path string) ([]copyRow, error) {
	raw, err := os.ReadFile(path)
	if err != nil {
		return nil, err
	}
	return parseCopyTable(string(raw), path, seededCopyIDPattern, "an SD-N<n>")
}

// checkSeededWired checks every wired id: it has a row, the row is
// approved, and its text is in the file the row names (the moves gate's own
// matcher, so a Go file must carry it as one literal, concatenation or
// format, and any other file raw).
func checkSeededWired(rows []copyRow, wired []string, root string) []string {
	byID := map[string]copyRow{}
	for _, r := range rows {
		byID[r.ID] = r
	}
	var out []string
	var applyRows []copyRow
	for _, id := range wired {
		r, ok := byID[id]
		switch {
		case !ok:
			out = append(out, id+": wired, but the deck has no row for it")
		case r.Status != "approved":
			out = append(out, id+": wired, but its row is "+r.Status+": only approved text may reach code")
		default:
			applyRows = append(applyRows, r)
		}
	}
	return append(out, checkMovesApplied(applyRows, root)...)
}

// checkSeededApproved fails on any row that is not approved. Plan 1 Task 2
// adds the test that calls it, once Paul's review has landed.
func checkSeededApproved(rows []copyRow) []string {
	var out []string
	for _, r := range rows {
		if r.Status != "approved" {
			out = append(out, r.ID+" is "+r.Status+": the deck is reviewed before any plan wires a string")
		}
	}
	return out
}

Add "os" to the new file's imports.

  • [ ] Step 4: Run the tests to verify they pass

Run: go test . -run 'TestSeeded|TestParseSeeded|TestMoves|TestParseMovesCopyTable|TestCopyRowPath' -count=1 Expected: PASS. TestSeededCopyRecordIsComplete passes against the deck written with this plan (116 rows, all pending).

  • [ ] Step 5: Commit
git add approved_moves_copy_test.go approved_seeded_copy_test.go
git commit -m "copy gate: the seeded-instances deck, pinned (Plan 1 Task 1)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>"

Task 2: Paul's copy review of the deck

Files:

  • Modify: docs/superpowers/plans/2026-10-08-seeded-instances-copy.md
  • Modify: approved_seeded_copy_test.go (add TestSeededCopyIsApproved; adjust seededCopyIDs only if Paul deletes or adds rows)

Interfaces:

  • Consumes: the deck; checkSeededApproved.
  • Produces: a deck whose every row is approved, which every later plan's wiring tasks read.
  • [ ] Step 1: Write the failing test

Append to approved_seeded_copy_test.go:

// Every row is reviewed before any plan of the series wires one (Plan 1
// Task 2). A row added later is reviewed before the plan that adds it
// merges, and this test holds that line.
func TestSeededCopyIsApproved(t *testing.T) {
	reportAll(t, checkSeededApproved(mustParseSeededRecord(t)))
}
  • [ ] Step 2: Run it to verify it fails

Run: go test . -run TestSeededCopyIsApproved -count=1 Expected: FAIL with 116 lines of the form SD-N1 is pending: the deck is reviewed before any plan wires a string.

  • [ ] Step 3: Run the copy review

Invoke the copy-review skill with the deck. Before relaunching its server for any reason, read the review's result.json first (a relaunch deletes a submitted review). Give it every row's id, the file it will live in, and the text, and the context line from the deck's heading (which door or command speaks it). Wait for Paul's submitted edits. Do not continue this task until they arrive.

  • [ ] Step 4: Apply the edits verbatim

For each row Paul changed, replace the text cell with his text exactly, keeping the id. For each row he deleted, delete the row and remove its id from seededCopyIDs (replace the generator with an explicit literal list if the ids are no longer contiguous). For each row he added, give it the next free SD-N<n> id, add it to the deck under the plan heading he named, and add it to seededCopyIDs. Then set every row's status to approved.

Check the result has no em dash: grep -n $'—' docs/superpowers/plans/2026-10-08-seeded-instances-copy.md prints nothing.

  • [ ] Step 5: Run the gate

Run: go test . -run 'TestSeeded|TestParseSeeded' -count=1 Expected: PASS, including TestSeededCopyIsApproved and TestSeededCopyRecordIsComplete.

  • [ ] Step 6: Commit
git add docs/superpowers/plans/2026-10-08-seeded-instances-copy.md approved_seeded_copy_test.go
git commit -m "copy: Paul's review of the seeded-instances deck, every row approved (Plan 1 Task 2)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>"

---

Self-review

  • Spec coverage: §7's copy rule is the whole of this plan; every string §7 lists (list states, audit rows, each error, the swap's refusal reasons) has a deck row: list states SD-N43, SD-N46, SD-N83 to SD-N88; audit SD-N12, SD-N41, SD-N62 to SD-N66, SD-N106; errors SD-N2 to SD-N10, SD-N20 to SD-N40, SD-N48 to SD-N61, SD-N100, SD-N101, SD-N103; swap reasons SD-N91 to SD-N98.
  • The moves gate's behaviour is unchanged: the only edit there is the parser's id check, which produces the same message for the moves record.
  • Review Focus: lines 1 to 3 are tests in Task 1; 4 is Task 2 Step 5; 5 is Task 2's test.