infinity-harness 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/CHANGELOG.md +114 -0
  2. package/LICENSE +21 -0
  3. package/README.md +266 -0
  4. package/extensions/infinity-harness/index.ts +870 -0
  5. package/harness/docs/ARCHITECTURE.md +159 -0
  6. package/harness/docs/CONSTRAINTS.md +19 -0
  7. package/harness/docs/DECISIONS.md +107 -0
  8. package/harness/docs/DOMAIN.md +13 -0
  9. package/harness/docs/agents/evaluator.md +14 -0
  10. package/harness/docs/agents/generator.md +13 -0
  11. package/harness/docs/agents/planner.md +13 -0
  12. package/harness/docs/agents/simplifier.md +13 -0
  13. package/harness/docs/api-patterns.md +23 -0
  14. package/harness/docs/phases/build.md +47 -0
  15. package/harness/docs/phases/define.md +58 -0
  16. package/harness/docs/phases/plan.md +50 -0
  17. package/harness/docs/phases/review.md +47 -0
  18. package/harness/docs/phases/ship.md +43 -0
  19. package/harness/docs/phases/simplify.md +45 -0
  20. package/harness/docs/phases/verify.md +46 -0
  21. package/harness/model-router.json +28 -0
  22. package/harness/skills/README.md +60 -0
  23. package/harness/skills/auth-security.md +56 -0
  24. package/harness/skills/building-mcp-servers.md +70 -0
  25. package/harness/skills/building-tools.md +60 -0
  26. package/harness/skills/capability-acquisition.md +72 -0
  27. package/harness/skills/cli-design.md +55 -0
  28. package/harness/skills/code-review.md +57 -0
  29. package/harness/skills/codebase-design.md +70 -0
  30. package/harness/skills/concurrency-async.md +61 -0
  31. package/harness/skills/config-and-secrets.md +52 -0
  32. package/harness/skills/context-hygiene.md +51 -0
  33. package/harness/skills/databases.md +63 -0
  34. package/harness/skills/diagnosing-bugs.md +84 -0
  35. package/harness/skills/domain-modeling.md +65 -0
  36. package/harness/skills/error-handling-logging.md +56 -0
  37. package/harness/skills/frontend-ui.md +56 -0
  38. package/harness/skills/grilling.md +48 -0
  39. package/harness/skills/http-apis.md +60 -0
  40. package/harness/skills/performance.md +53 -0
  41. package/harness/skills/pi-todo-adapted.md +41 -0
  42. package/harness/skills/planning-tasks.md +86 -0
  43. package/harness/skills/prototype.md +39 -0
  44. package/harness/skills/research.md +32 -0
  45. package/harness/skills/resolving-merge-conflicts.md +30 -0
  46. package/harness/skills/scope-discipline.md +49 -0
  47. package/harness/skills/self-review.md +45 -0
  48. package/harness/skills/stuck-protocol.md +51 -0
  49. package/harness/skills/tdd.md +80 -0
  50. package/harness/skills/testing-infra.md +57 -0
  51. package/harness/skills/writing-skills.md +60 -0
  52. package/package.json +61 -0
  53. package/src/core/brief.ts +242 -0
  54. package/src/core/config.ts +265 -0
  55. package/src/core/exec.ts +130 -0
  56. package/src/core/featureList.ts +286 -0
  57. package/src/core/fsx.ts +119 -0
  58. package/src/core/gates.ts +444 -0
  59. package/src/core/lock.ts +192 -0
  60. package/src/core/paths.ts +95 -0
  61. package/src/core/phases.ts +143 -0
  62. package/src/core/settings.ts +445 -0
  63. package/src/core/types.ts +245 -0
  64. package/src/goalLoop.ts +628 -0
  65. package/src/goalSpec.ts +679 -0
  66. package/src/goalState.ts +338 -0
  67. package/src/loop.ts +355 -0
  68. package/src/modelRouter.ts +184 -0
  69. package/src/remote.ts +244 -0
  70. package/src/replan.ts +300 -0
  71. package/src/review.ts +53 -0
  72. package/src/rework.ts +274 -0
  73. package/src/taskList.ts +355 -0
  74. package/src/ui/config.ts +286 -0
  75. package/src/ui/dashboard.ts +1066 -0
  76. package/src/ui/theme.ts +317 -0
  77. package/src/ui/widget.ts +370 -0
  78. package/src/unstuck.ts +214 -0
  79. package/src/worker.ts +351 -0
  80. package/types/proper-lockfile.d.ts +19 -0
@@ -0,0 +1,41 @@
1
+ ---
2
+ name: pi-todo-adapted
3
+ description: "Atomic infinity_plan pattern with omission deletion, baseRevision optimistic concurrency, cycle/missing-dep checks and compaction-safe replay (adapted from @99percentpeople/pi-todo)"
4
+ tags: [harness, pi-todo, atomic, baseRevision, omission, deletion, compaction, widget, task-list, dependency]
5
+ when: "implementing infinity_plan, atomic revisioning, Pi widget with session persistence or task dependencies"
6
+ phases: [build, verify]
7
+ provenance: { origin: "https://github.com/99percentpeople/pi-extensions", license: MIT, adapted: true, url: "https://github.com/99percentpeople/pi-extensions", contentHash: abf626df6a74a6fb }
8
+ ---
9
+
10
+ # pi-todo Adapted — Atomic Task List with Compaction-Safe Replay
11
+
12
+ > Adapted from [@99percentpeople/pi-todo](https://github.com/99percentpeople/pi-extensions) (MIT © 99percentpeople). Trimmed to harness core and re-pointed to `harness/features/feature-list.json` + Pi extension `harness:checkpoint`.
13
+
14
+ ## Rules
15
+
16
+ - **One-call authoritative list.** Client sends complete desired list; server treats omission as permanent deletion. Inherit omitted fields from previous snapshot; new keys require `subject` + `status`. Validation all-or-nothing.
17
+ - **Optimistic concurrency via baseRevision.** Compare `input.baseRevision` vs current `revision`; reject stale with expected/current numbers; increment revision only on real change; empty no-op preserves revision.
18
+ - **Dependency guarantees.** Every `dependsOn` must exist in resulting snapshot (prune completed→completed soft refs only); detect cycles via DFS; `in_progress`/`completed` requires deps completed. Reject atomically.
19
+ - **ToolResult.details is SSOT for branch/compaction.** Store `{rev, tasks}` in toolResult.details; reconstruct state by scanning session branch for latest valid entry on `session_start`, `session_tree`, `/reload`. Branch-aware replay — each branch has correct snapshot.
20
+ - **Hidden checkpoint for compaction.** On `session_before_compact`, persist `{rev, tasks}` as custom entry `harness:checkpoint` outside context; inject as hidden `[{role:"user", content:[{type:"text", text: hidden}]}]` via `context` event on next prompt (or immediately as steer if overflow/continuation). No extra model turn.
21
+ - **Periodic hidden reminder.** Every N LLM calls (default 3) inject compact reminder with `rev + key/status` list via `context` event; reset counter after successful write, restoration, checkpoint injection, or interval change; suppress for empty/completed plans; not written to session.
22
+ - **Widget renders collapsed window via getWidgetWindowBounds.** Use WIDGET_LIMIT=8, COMPLETED_CONTEXT=3, `+N more`, wrap not truncate, `← #1` deps. State comes from reconstructed session + file; survive /reload and /tree.
23
+
24
+ ## Anti-patterns
25
+
26
+ - **Per-field patch calls** — one call must carry full authoritative list; piecemeal updates lose atomicity.
27
+ - **File-only persistence** — file is SSOT but session replay is required for branches/compaction; without details replay, `/tree` drifts.
28
+ - **Eager completed cleanup without dependency check** — completed tasks that still block pending/in_progress must be retained.
29
+ - **Stale revision retry without re-read** — on stale error, re-read current rev/tasks and retry merge.
30
+
31
+ ## Checklist
32
+
33
+ - [ ] Stale baseRevision rejected with expected/current message, no mutation
34
+ - [ ] Omitted keys deleted, completed→completed deps pruned, new keys validated
35
+ - [ ] Cycle and missing dep rejected atomically
36
+ - [ ] in_progress requires deps completed (also completed requires deps completed if strict)
37
+ - [ ] Revision increments only on added/updated/removed/reordered; details contain {rev, tasks}
38
+ - [ ] State replays from toolResult.details + harness:checkpoint on session_start / session_tree
39
+ - [ ] Hidden checkpoint stored before compaction and injected via context event after compaction
40
+ - [ ] Periodic reminder every 3 calls injects rev + key/status via context, counter resets correctly
41
+ - [ ] Widget uses rolling window (WIDGET_LIMIT=8) with +N more, wraps long labels, shows ← #1
@@ -0,0 +1,86 @@
1
+ ---
2
+ name: planning-tasks
3
+ description: Break specs into tracer-bullet vertical slices with checkable acceptance criteria
4
+ tags: [plan, planning, slice, ticket, decompose, criteria, backlog, breakdown, refactor]
5
+ when: writing or restructuring the feature list from a spec
6
+ phases: [plan]
7
+ provenance: { origin: "mattpocock/skills", license: MIT, adapted: true }
8
+ ---
9
+
10
+ # Planning Tasks — Tracer-Bullet Vertical Slices
11
+
12
+ > Adapted from [mattpocock/skills](https://github.com/mattpocock/skills) (MIT © Matt Pocock, "to-tickets").
13
+ > Use during: PLAN (writing harness/features/feature-list.json).
14
+
15
+ Break the spec into **tracer-bullet vertical slices** and record them as
16
+ features + tasks in `harness/features/feature-list.json`.
17
+
18
+ ## Vertical slice rules
19
+
20
+ - Each slice cuts a narrow but COMPLETE path through every layer (schema,
21
+ logic, interface, tests) — vertical, NOT a horizontal slice of one layer.
22
+ - A completed slice is demoable or verifiable on its own.
23
+ - Each task is sized to fit one working session / fresh context window.
24
+ - Prefactoring goes first: "make the change easy, then make the easy change."
25
+ - Order features so each builds on completed ones — the harness works them
26
+ top to bottom.
27
+
28
+ ## Writing the feature list
29
+
30
+ ```json
31
+ {
32
+ "version": "0.1",
33
+ "features": [
34
+ {
35
+ "id": "feature-001",
36
+ "name": "User can create a note",
37
+ "description": "End-to-end: POST /notes → stored → retrievable",
38
+ "passes": false,
39
+ "definitionOfDone": [
40
+ "POST /notes with valid body returns 201 + id",
41
+ "GET /notes/:id returns the stored note",
42
+ "Invalid body returns 400 with error message"
43
+ ],
44
+ "tasks": [
45
+ {
46
+ "id": "task-001",
47
+ "description": "Note model + storage with create/get",
48
+ "status": "pending",
49
+ "acceptanceCriteria": [
50
+ "createNote(data) returns note with generated id",
51
+ "getNote(id) returns the created note",
52
+ "getNote(unknown) returns null"
53
+ ]
54
+ }
55
+ ]
56
+ }
57
+ ]
58
+ }
59
+ ```
60
+
61
+ Rules for criteria:
62
+
63
+ - **acceptanceCriteria** (task) — 1–3 concrete, checkable statements about
64
+ observable behavior. "Works correctly" is not a criterion; "getNote(unknown)
65
+ returns null" is. Gates reject empty or placeholder criteria.
66
+ - **definitionOfDone** (feature) — user-visible outcomes proving the whole
67
+ slice works end-to-end.
68
+ - Name features in **user terms** ("User can X"), not layer terms ("Add DB
69
+ table").
70
+ - Use the project's domain vocabulary (`harness/docs/DOMAIN.md`).
71
+
72
+ ## Wide refactors — the exception
73
+
74
+ A **wide refactor** (rename a shared symbol, retype a column) breaks
75
+ thousands of call sites at once; no vertical slice can land green. Sequence
76
+ it as **expand–contract**: one task to add the new form beside the old, then
77
+ migration tasks in batches (per package/directory) that stay green because
78
+ the old form still exists, then one task to delete the old form once no
79
+ caller remains.
80
+
81
+ ## Sanity check before finishing PLAN
82
+
83
+ - Does each feature deliver something demoable?
84
+ - Is anything blocked by a feature that comes AFTER it? (Reorder.)
85
+ - Too coarse (task needs multiple sessions) or too fine (trivial edits)?
86
+ - Zero placeholder text left anywhere in the file.
@@ -0,0 +1,39 @@
1
+ ---
2
+ name: prototype
3
+ description: Throwaway code that answers a design question fast, then gets deleted
4
+ tags: [prototype, spike, experiment, explore, poc, throwaway, design]
5
+ when: a state model, logic shape, or UI direction cannot be judged on paper
6
+ phases: [build, plan]
7
+ provenance: { origin: "mattpocock/skills", license: MIT, adapted: true }
8
+ ---
9
+
10
+ # Prototype
11
+
12
+ > Adapted from [mattpocock/skills](https://github.com/mattpocock/skills) (MIT © Matt Pocock).
13
+ > Use during: BUILD (or PLAN) when a design question can't be answered on paper.
14
+
15
+ A prototype is **throwaway code that answers a question**. The question
16
+ decides the shape:
17
+
18
+ - **"Does this logic / state model feel right?"** → build a tiny runnable
19
+ script that pushes the state machine through the cases that are hard to
20
+ reason about on paper.
21
+ - **"What should this look like?"** → build several radically different
22
+ variations of the interface, cheaply switchable.
23
+
24
+ ## Rules
25
+
26
+ 1. **Throwaway from day one, clearly marked.** Name it so a casual reader
27
+ sees it's a prototype (`proto-*`, a `prototypes/` dir) — never mixed
28
+ into production paths.
29
+ 2. **One command to run.** Whatever the project's runner supports.
30
+ 3. **No persistence by default.** State lives in memory; persistence is
31
+ usually the thing being *checked*, not a dependency.
32
+ 4. **Skip the polish.** No tests, no error handling beyond runnability, no
33
+ abstractions. The point is to learn fast.
34
+ 5. **Surface the state.** After every action, print the full relevant state
35
+ so the effect of each step is visible.
36
+ 6. **Capture it when done.** Fold the validated decision into the real code
37
+ and record it: `infinity-harness decision "state machine X chosen because Y
38
+ (validated by prototype)"`. Then DELETE the prototype — the
39
+ anti-placeholder gate will flag leftovers, and that's by design.
@@ -0,0 +1,32 @@
1
+ ---
2
+ name: research
3
+ description: Answer factual questions from primary sources; capture cited findings in the repo
4
+ tags: [research, docs, documentation, investigate, source, facts, api, spec, evidence]
5
+ when: a factual question about an API, library, protocol, or tool blocks progress
6
+ phases: [define, plan, build]
7
+ provenance: { origin: "mattpocock/skills", license: MIT, adapted: true }
8
+ ---
9
+
10
+ # Research
11
+
12
+ > Adapted from [mattpocock/skills](https://github.com/mattpocock/skills) (MIT © Matt Pocock).
13
+ > Use during: DEFINE, or any time a factual question blocks progress.
14
+
15
+ When a question needs investigating (an API's real behavior, a library's
16
+ limits, a protocol detail):
17
+
18
+ 1. Investigate against **primary sources** — official docs, source code,
19
+ specs, first-party APIs — never a secondary write-up of them. Follow
20
+ every claim back to the source that owns it.
21
+ 2. Write the findings to a single Markdown file under `docs/research/`
22
+ (create it if missing), citing each claim's source.
23
+ 3. Keep conclusions separate from evidence: a short "Answer" section up
24
+ top, the sourced detail below.
25
+ 4. Record the headline insight so future sessions inherit it:
26
+ `harness/lessons-decisions.md "research: X behaves like Y (see docs/research/...)"`.
27
+
28
+ Rules of evidence:
29
+
30
+ - A claim without a source is a guess. Label guesses as guesses.
31
+ - Version matters: note WHICH version of the tool/API the claim covers.
32
+ - If two sources disagree, the more primary one wins; note the conflict.
@@ -0,0 +1,30 @@
1
+ ---
2
+ name: resolving-merge-conflicts
3
+ description: Resolve git merge/rebase conflicts by original intent, verify, and finish
4
+ tags: [git, merge, rebase, conflict, branch, hunk]
5
+ when: an in-progress git merge or rebase has conflicting hunks
6
+ phases: []
7
+ provenance: { origin: "mattpocock/skills", license: MIT, adapted: true }
8
+ ---
9
+
10
+ # Resolving Merge Conflicts
11
+
12
+ > Adapted from [mattpocock/skills](https://github.com/mattpocock/skills) (MIT © Matt Pocock).
13
+ > Use during: any in-progress git merge/rebase conflict.
14
+
15
+ 1. **See the current state** of the merge/rebase. Check git history and the
16
+ conflicting files.
17
+ 2. **Find the primary sources** for each conflict. Understand deeply why
18
+ each change was made and what the original intent was: read commit
19
+ messages, PRs, the feature list entries, and progress log
20
+ (`harness/progress.md`).
21
+ 3. **Resolve each hunk.** Preserve both intents where possible. Where
22
+ incompatible, pick the one matching the merge's stated goal and note the
23
+ trade-off. Do **not** invent new behaviour. Always resolve; never
24
+ `--abort`.
25
+ 4. **Run the project's automated checks** — typecheck, then tests, then
26
+ lint (`the infinity_validate tool` runs the configured set). Fix anything the
27
+ merge broke.
28
+ 5. **Finish the merge/rebase.** Stage everything and commit. If rebasing,
29
+ continue until all commits are rebased. Record anything surprising:
30
+ `harness/lessons-decisions.md "..."`.
@@ -0,0 +1,49 @@
1
+ ---
2
+ name: scope-discipline
3
+ description: The contract is the boundary — park ideas, resist drive-by fixes, renegotiate explicitly
4
+ tags: [meta, scope, creep, contract, focus, discipline, playbook]
5
+ when: mid-task ideas, adjacent problems, or "while I'm here" temptations appear
6
+ phases: []
7
+ provenance: { origin: built-in, notes: frontier-playbook }
8
+ ---
9
+
10
+ # Scope Discipline
11
+
12
+ Scope creep is how good sessions produce unshippable diffs. The sprint
13
+ contract and the task's acceptance criteria define what you're building —
14
+ everything else, however good, is NOT NOW.
15
+
16
+ ## Rules
17
+
18
+ - **The criteria are the whole job.** Done = criteria met. Not "criteria
19
+ met plus improvements I noticed". Extra behavior nobody asked for is
20
+ scope creep even when it's good — it widens the diff, the review
21
+ surface, and the blast radius.
22
+ - **Park, don't pursue.** Mid-task ideas go to the parking lot in one
23
+ line — `harness/lessons-decisions.md "idea: ..."` or a backlog entry in the
24
+ feature list — and you return to the slice. Parking takes 10 seconds;
25
+ pursuing takes an hour and derails the task.
26
+ - **Drive-by fixes are parked too.** Broken thing found outside the task
27
+ (unless it BLOCKS the task): record it, leave it. One task = one
28
+ concern = one reviewable diff.
29
+ - **Blockers interrupt; everything else waits.** The only legitimate
30
+ mid-task detour is something the acceptance criteria cannot be met
31
+ without. Name it as a blocker (learn), fix the minimum, return.
32
+ - **Renegotiate explicitly, never silently.** If the task turns out
33
+ wrong-sized or the contract missed something real: stop, record it, and
34
+ change the plan through the front door (feature-list edit in PLAN
35
+ terms, or contract re-negotiation) — not by quietly building something
36
+ different from what the contract says.
37
+ - **Simplification has its phase.** Refactoring urges during BUILD get
38
+ parked for SIMPLIFY — the pipeline gives cleanup its own budgeted slot.
39
+
40
+ ## The test
41
+
42
+ Before touching a file, ask: *which acceptance criterion needs this
43
+ change?* No answer → you're creeping. Park it.
44
+
45
+ ## Checklist
46
+
47
+ - [ ] Diff touches only what the criteria require
48
+ - [ ] Every parked idea captured (learn/backlog), none pursued
49
+ - [ ] Any scope change went through plan/contract, with a decision recorded
@@ -0,0 +1,45 @@
1
+ ---
2
+ name: self-review
3
+ description: Structured pass before claiming done — criteria re-check, real run, leftover hunt
4
+ tags: [meta, review, verify, done, quality, check, playbook]
5
+ when: before running validate on any task — every time
6
+ phases: [build, verify, simplify]
7
+ provenance: { origin: built-in, notes: frontier-playbook }
8
+ ---
9
+
10
+ # Self-Review — Before You Claim Done
11
+
12
+ Frontier models don't submit their first draft; they run this pass. It
13
+ takes two minutes and catches the majority of validate failures before
14
+ they burn a retry.
15
+
16
+ ## The pass
17
+
18
+ 1. **Re-read the acceptance criteria, one by one, against your diff.**
19
+ For each: point at the line(s) that satisfy it. A criterion you can't
20
+ point at is not met — no matter how done it feels.
21
+ 2. **Run the thing once, for real.** Not just the tests — the actual entry
22
+ point (start the server, invoke the CLI, render the page). Tests
23
+ passing while the app crashes on boot is a classic.
24
+ 3. **Hunt leftovers:** debug prints, commented-out code, TODO/FIXME stubs,
25
+ hardcoded test values, files you created but abandoned. (The
26
+ anti-placeholder gate will catch some — beat it to the punch.)
27
+ 4. **Read the diff as a skeptic:** `git diff` top to bottom. Would a
28
+ reviewer who dislikes you find something? Fix it now.
29
+ 5. **Check the blast radius:** what ELSE uses what you touched? One
30
+ caller-grep per changed public symbol.
31
+
32
+ ## Rules
33
+
34
+ - Never validate as a formality — validate expecting to find problems.
35
+ - The criteria are the contract; "better than asked" and "close enough to
36
+ asked" are both failures (see scope-discipline.md).
37
+ - If step 2 is impossible (no runnable surface), say so in the commit
38
+ message rather than pretending you ran it.
39
+
40
+ ## Checklist
41
+
42
+ - [ ] Every acceptance criterion mapped to specific lines
43
+ - [ ] Entry point actually executed post-change
44
+ - [ ] Leftover grep clean (debug/TODO/dead files)
45
+ - [ ] Diff read end-to-end; caller-grep done per public change
@@ -0,0 +1,51 @@
1
+ ---
2
+ name: stuck-protocol
3
+ description: What to do after repeated failures — stop, write down, reframe once, escalate cleanly
4
+ tags: [meta, stuck, retry, failure, escalate, blocked, playbook]
5
+ when: the same step has failed 2-3 times, or you notice you are looping
6
+ phases: []
7
+ provenance: { origin: built-in, notes: frontier-playbook }
8
+ ---
9
+
10
+ # Stuck Protocol
11
+
12
+ Thrashing — trying variations of the same failing approach — burns retries
13
+ and context while teaching nothing. Frontier models notice the loop and
14
+ break it deliberately.
15
+
16
+ ## The trigger
17
+
18
+ **Three failed attempts at the same step = STOP.** Also stop when you
19
+ catch any of these tells:
20
+
21
+ - You're re-running the same command hoping for a different result
22
+ - Each "fix" is a small mutation of the last failed fix
23
+ - You can no longer say what NEW information the last attempt produced
24
+
25
+ ## The protocol
26
+
27
+ 1. **Write down what you know** (before trying anything else):
28
+ what you're trying to achieve, the exact error/failure, the attempts
29
+ made, and what each attempt ruled out. Writing this usually exposes the
30
+ gap — half the time you solve it here.
31
+ 2. **Re-read the brief and the skill** (`the infinity_brief tool`; the skill it
32
+ references). Stuck often means a constraint was missed, not that the
33
+ problem is hard.
34
+ 3. **Reframe ONCE** — one genuinely different angle, not a variation:
35
+ different seam, smaller slice, different tool, the inverse operation,
36
+ or build the feedback loop you skipped (`diagnosing-bugs.md` Phase 1).
37
+ 4. **Escalate cleanly** if the reframe fails:
38
+ - `harness/lessons-decisions.md "BLOCKED <step>: tried A, B, C; ruled out X, Y; suspect Z"`
39
+ - Leave the tree committed and green-adjacent (no half-applied change)
40
+ - Let validate fail honestly — the retry ladder and the human exist for
41
+ exactly this. A clean escalation with evidence is a GOOD outcome;
42
+ a fourth identical attempt is not.
43
+
44
+ ## Rules
45
+
46
+ - Never delete the failing evidence (test, error output) to make the step
47
+ "pass".
48
+ - Never widen scope to route around the blocker ("I'll just rewrite the
49
+ module") — that's thrash with more damage.
50
+ - The write-down in step 1 is mandatory, not optional — it's what makes
51
+ the human's (or next session's) job possible.
@@ -0,0 +1,80 @@
1
+ ---
2
+ name: tdd
3
+ description: Red → green test-driven loop — what a good test is, seams, anti-patterns
4
+ tags: [tdd, test, testing, unit, integration, red-green, seam, mock, coverage]
5
+ when: implementing any BUILD task, or fixing a bug that needs a regression test
6
+ phases: [build, verify]
7
+ provenance: { origin: "mattpocock/skills", license: MIT, adapted: true }
8
+ ---
9
+
10
+ # TDD — Test-Driven Development
11
+
12
+ > Adapted from [mattpocock/skills](https://github.com/mattpocock/skills) (MIT © Matt Pocock).
13
+ > Use during: BUILD (every task).
14
+
15
+ TDD is the red → green loop. This skill makes that loop produce tests worth
16
+ keeping. Every section applies on every cycle.
17
+
18
+ Before exploring the codebase, read `harness/docs/DOMAIN.md` (if present) so
19
+ test names and interface vocabulary match the project's domain language, and
20
+ respect decisions recorded in `harness/docs/DECISIONS.md`.
21
+
22
+ ## Rules of the loop
23
+
24
+ - **Red before green.** Write the failing test first, then only enough code
25
+ to pass it. Don't anticipate future tests or add speculative features.
26
+ - **One slice at a time.** One seam, one test, one minimal implementation
27
+ per cycle.
28
+ - **Refactoring is not part of the loop.** It belongs to SIMPLIFY/REVIEW,
29
+ not the red → green cycle.
30
+
31
+ ## What a good test is
32
+
33
+ Tests verify behavior through public interfaces, not implementation details.
34
+ Code can change entirely; tests shouldn't. A good test reads like a
35
+ specification — "user can checkout with valid cart" tells you exactly what
36
+ capability exists — and survives refactors because it doesn't care about
37
+ internal structure.
38
+
39
+ ```js
40
+ // GOOD: observable behavior through the public API
41
+ test('user can checkout with valid cart', async () => {
42
+ const cart = createCart();
43
+ cart.add(product);
44
+ const result = await checkout(cart, paymentMethod);
45
+ expect(result.status).toBe('confirmed');
46
+ });
47
+
48
+ // BAD: implementation detail — breaks on refactor, catches nothing real
49
+ test('checkout calls paymentService.process', async () => {
50
+ const mockPayment = mock(paymentService);
51
+ await checkout(cart, payment);
52
+ expect(mockPayment.process).toHaveBeenCalledWith(cart.total);
53
+ });
54
+ ```
55
+
56
+ ## Seams — where tests go
57
+
58
+ A **seam** is the public boundary you test at: the interface where you
59
+ observe behavior without reaching inside. Tests live at seams, never against
60
+ internals. Before writing any test, write down the seams under test — the
61
+ task's acceptance criteria usually name them. Testing effort goes to critical
62
+ paths and complex logic, not every edge case.
63
+
64
+ Mock **only at external seams** (network, clock, filesystem, third-party
65
+ APIs) — never mock your own internal collaborators.
66
+
67
+ ## Anti-patterns
68
+
69
+ - **Implementation-coupled** — mocks internal collaborators, tests private
70
+ methods, or verifies through a side channel (querying the database instead
71
+ of using the interface). Tell: the test breaks when you refactor but
72
+ behavior hasn't changed.
73
+ - **Tautological** — the assertion recomputes the expected value the way the
74
+ code does (`expect(add(a, b)).toBe(a + b)`), so it passes by construction.
75
+ Expected values must come from an independent source of truth — a
76
+ known-good literal, a worked example, the spec.
77
+ - **Horizontal slicing** — writing all tests first, then all implementation.
78
+ Bulk tests verify *imagined* behavior. Work in **vertical slices**: one
79
+ test → one implementation → repeat, each test a tracer bullet that responds
80
+ to what the last cycle taught you.
@@ -0,0 +1,57 @@
1
+ ---
2
+ name: testing-infra
3
+ description: Test infrastructure — fixtures, isolation, flaky-test policy, CI hygiene, coverage sanity
4
+ tags: [test, testing, fixture, ci, flaky, coverage, mock, isolation, pipeline, suite]
5
+ when: task sets up test tooling, fixtures, CI, or fights slow/flaky suites
6
+ phases: [build, verify]
7
+ provenance: { origin: built-in }
8
+ ---
9
+
10
+ # Testing Infrastructure
11
+
12
+ (`tdd.md` covers what a good test IS; this covers the machinery around them.)
13
+
14
+ ## Rules
15
+
16
+ - **Tests are order-independent and parallel-safe.** Each test creates its
17
+ own data and cleans up (or runs in a transaction rolled back / fresh
18
+ tmpdir). If running one test alone changes its result, the suite is
19
+ broken even while green.
20
+ - **Fixtures are builders, not blobs.** `makeUser({overrides})` beats a
21
+ 500-line fixtures.json — tests state only what matters, defaults carry
22
+ the rest, schema changes touch one builder.
23
+ - **Determinism is manufactured:** pin the clock (fake timers), seed RNG,
24
+ freeze network (record/replay or fakes at the seam), isolate filesystem
25
+ (tmpdir per test). Anything you didn't pin will flake at 2am.
26
+ - **Flaky-test policy — zero tolerance:** a flaky test is a P1 against the
27
+ suite. Quarantine it the day it flakes (skip with a linked issue),
28
+ diagnose with `diagnosing-bugs.md` (usually: unpinned time, shared
29
+ state, real network, race). A retried-until-green suite verifies nothing.
30
+ - **The pyramid is a budget:** many fast unit/integration tests at seams,
31
+ few end-to-end smoke tests. E2E for "the wiring works", not for every
32
+ edge case — edges belong at the seam where they live.
33
+ - **CI runs what developers run:** same command (`npm test`), same
34
+ versions, fresh checkout, no network by default. "Works locally" bugs
35
+ are environment drift — fix the environment, not the test.
36
+ - **Coverage is a smoke detector, not a target.** Use it to FIND untested
37
+ branches; never write assertion-free tests to move the number. The
38
+ gate's threshold is a floor, not a goal.
39
+
40
+ ## Anti-patterns
41
+
42
+ - **Shared mutable fixtures** ("the test database user") → per-test data
43
+ with unique keys; suites die by a thousand shared rows.
44
+ - **Sleeping to wait** (`sleep 2` then assert) → poll with timeout or
45
+ await the actual signal; sleeps are both slow AND flaky.
46
+ - **Mocking the world** — suite passes while the app is down → integration
47
+ tests at real seams (real DB, real HTTP server in-process).
48
+ - **Snapshot everything** — giant snapshots nobody reads, updated with
49
+ `--update` reflexively → snapshot small, stable, reviewed outputs only.
50
+
51
+ ## Checklist
52
+
53
+ - [ ] Any single test runs alone: same result
54
+ - [ ] Suite passes twice in a row AND with a different order/seed
55
+ - [ ] No real network/clock/RNG unpinned anywhere
56
+ - [ ] Flaky tests quarantined with linked issues (count: 0 is the goal)
57
+ - [ ] CI command identical to the local test command
@@ -0,0 +1,60 @@
1
+ ---
2
+ name: writing-skills
3
+ description: How to author a skill worth keeping — scope, structure, frontmatter, quality bar
4
+ tags: [meta, skill, writing, author, documentation, checklist]
5
+ when: creating a new skill or adapting an acquired one
6
+ phases: []
7
+ provenance: { origin: "mattpocock/skills writing-great-skills", license: MIT, adapted: true }
8
+ ---
9
+
10
+ # Writing Skills
11
+
12
+ A skill is a reusable instruction file that changes how the work gets done.
13
+ The test: **would a weak model do the right thing following ONLY this
14
+ file?** If not, it's not done.
15
+
16
+ ## Rules
17
+
18
+ - **One job per skill.** "databases" not "backend-development". If you're
19
+ writing "and also…", split it.
20
+ - **Rules + anti-patterns + checklist** — that structure, every time.
21
+ Rules are imperative and checkable. Anti-patterns show what going wrong
22
+ looks like AND the fix. The checklist is how the reader verifies they
23
+ applied it.
24
+ - **≤120 lines of body.** Longer means it's two skills or it's a tutorial.
25
+ - **Concrete over abstract.** One short example beats three paragraphs of
26
+ principle. Name real commands, real file paths, real error messages.
27
+ - **Frontmatter required** (the matcher is blind without it):
28
+
29
+ ```yaml
30
+ ---
31
+ name: kebab-case-name
32
+ description: One line, 10-300 chars — what this teaches
33
+ tags: [five, or, more, search, keywords] # ≥3 required; think "what words appear in tasks that need this?"
34
+ when: the task signal that should trigger this skill
35
+ phases: [build] # optional phase affinity
36
+ provenance: { origin: self-authored, authoredBy: agent }
37
+ ---
38
+ ```
39
+
40
+ Frontmatter uses a strict subset: `key: value`, `key: [a, b]`,
41
+ `key: { k: v }` — no multiline YAML.
42
+
43
+ - **Tag for the matcher.** Tags are matched against task text — use the
44
+ nouns a task description would actually contain ("postgres", "auth",
45
+ "migration"), not categories ("backend").
46
+
47
+ ## Anti-patterns
48
+
49
+ - **The essay** — background, history, philosophy. Delete everything that
50
+ doesn't change what the reader does next.
51
+ - **The mirror** — restating what the harness already enforces (gates,
52
+ phases). Skills teach craft, not process.
53
+ - **The wishlist** — "consider…", "you might…". Skills say DO and DON'T.
54
+
55
+ ## Checklist
56
+
57
+ - [ ] Frontmatter passes: `infinity-harness capability add skill <path>` (it validates)
58
+ - [ ] A cold reader knows exactly what to do and how to check they did it
59
+ - [ ] Every rule is checkable; every anti-pattern has a fix
60
+ - [ ] Attribution line present if adapted from elsewhere
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "infinity-harness",
3
+ "version": "2.0.0",
4
+ "description": "A pi agent extension that runs a gated build pipeline unattended \u2014 enforces phases, validates with deterministic gates, and keeps working for hours or days without losing the plan.",
5
+ "type": "module",
6
+ "keywords": [
7
+ "pi-package",
8
+ "pi-extension",
9
+ "harness",
10
+ "agent-pipeline",
11
+ "autonomous",
12
+ "continuous-development"
13
+ ],
14
+ "license": "MIT",
15
+ "author": "Bakr Bagaber",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/bakr-bagaber/infinity-harness.git"
19
+ },
20
+ "pi": {
21
+ "extensions": [
22
+ "./extensions"
23
+ ],
24
+ "skills": [
25
+ "./harness/skills"
26
+ ]
27
+ },
28
+ "files": [
29
+ "extensions/",
30
+ "src/",
31
+ "types/",
32
+ "harness/skills/",
33
+ "harness/docs/",
34
+ "harness/model-router.json",
35
+ "README.md",
36
+ "LICENSE",
37
+ "CHANGELOG.md"
38
+ ],
39
+ "engines": {
40
+ "node": ">=22.0.0"
41
+ },
42
+ "dependencies": {
43
+ "proper-lockfile": "^4.1.2",
44
+ "string-width": "^7.1.0"
45
+ },
46
+ "devDependencies": {
47
+ "@earendil-works/pi-coding-agent": "*",
48
+ "@types/node": "^22.0.0",
49
+ "typescript": "^5.5.0"
50
+ },
51
+ "scripts": {
52
+ "test": "node scripts/run-tests.mjs",
53
+ "check": "tsc --noEmit",
54
+ "build": "tsc --noEmit",
55
+ "e2e": "node scripts/e2e.mjs"
56
+ },
57
+ "homepage": "https://github.com/bakr-bagaber/infinity-harness#readme",
58
+ "bugs": {
59
+ "url": "https://github.com/bakr-bagaber/infinity-harness/issues"
60
+ }
61
+ }