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.
- package/CHANGELOG.md +114 -0
- package/LICENSE +21 -0
- package/README.md +266 -0
- package/extensions/infinity-harness/index.ts +870 -0
- package/harness/docs/ARCHITECTURE.md +159 -0
- package/harness/docs/CONSTRAINTS.md +19 -0
- package/harness/docs/DECISIONS.md +107 -0
- package/harness/docs/DOMAIN.md +13 -0
- package/harness/docs/agents/evaluator.md +14 -0
- package/harness/docs/agents/generator.md +13 -0
- package/harness/docs/agents/planner.md +13 -0
- package/harness/docs/agents/simplifier.md +13 -0
- package/harness/docs/api-patterns.md +23 -0
- package/harness/docs/phases/build.md +47 -0
- package/harness/docs/phases/define.md +58 -0
- package/harness/docs/phases/plan.md +50 -0
- package/harness/docs/phases/review.md +47 -0
- package/harness/docs/phases/ship.md +43 -0
- package/harness/docs/phases/simplify.md +45 -0
- package/harness/docs/phases/verify.md +46 -0
- package/harness/model-router.json +28 -0
- package/harness/skills/README.md +60 -0
- package/harness/skills/auth-security.md +56 -0
- package/harness/skills/building-mcp-servers.md +70 -0
- package/harness/skills/building-tools.md +60 -0
- package/harness/skills/capability-acquisition.md +72 -0
- package/harness/skills/cli-design.md +55 -0
- package/harness/skills/code-review.md +57 -0
- package/harness/skills/codebase-design.md +70 -0
- package/harness/skills/concurrency-async.md +61 -0
- package/harness/skills/config-and-secrets.md +52 -0
- package/harness/skills/context-hygiene.md +51 -0
- package/harness/skills/databases.md +63 -0
- package/harness/skills/diagnosing-bugs.md +84 -0
- package/harness/skills/domain-modeling.md +65 -0
- package/harness/skills/error-handling-logging.md +56 -0
- package/harness/skills/frontend-ui.md +56 -0
- package/harness/skills/grilling.md +48 -0
- package/harness/skills/http-apis.md +60 -0
- package/harness/skills/performance.md +53 -0
- package/harness/skills/pi-todo-adapted.md +41 -0
- package/harness/skills/planning-tasks.md +86 -0
- package/harness/skills/prototype.md +39 -0
- package/harness/skills/research.md +32 -0
- package/harness/skills/resolving-merge-conflicts.md +30 -0
- package/harness/skills/scope-discipline.md +49 -0
- package/harness/skills/self-review.md +45 -0
- package/harness/skills/stuck-protocol.md +51 -0
- package/harness/skills/tdd.md +80 -0
- package/harness/skills/testing-infra.md +57 -0
- package/harness/skills/writing-skills.md +60 -0
- package/package.json +61 -0
- package/src/core/brief.ts +242 -0
- package/src/core/config.ts +265 -0
- package/src/core/exec.ts +130 -0
- package/src/core/featureList.ts +286 -0
- package/src/core/fsx.ts +119 -0
- package/src/core/gates.ts +444 -0
- package/src/core/lock.ts +192 -0
- package/src/core/paths.ts +95 -0
- package/src/core/phases.ts +143 -0
- package/src/core/settings.ts +445 -0
- package/src/core/types.ts +245 -0
- package/src/goalLoop.ts +628 -0
- package/src/goalSpec.ts +679 -0
- package/src/goalState.ts +338 -0
- package/src/loop.ts +355 -0
- package/src/modelRouter.ts +184 -0
- package/src/remote.ts +244 -0
- package/src/replan.ts +300 -0
- package/src/review.ts +53 -0
- package/src/rework.ts +274 -0
- package/src/taskList.ts +355 -0
- package/src/ui/config.ts +286 -0
- package/src/ui/dashboard.ts +1066 -0
- package/src/ui/theme.ts +317 -0
- package/src/ui/widget.ts +370 -0
- package/src/unstuck.ts +214 -0
- package/src/worker.ts +351 -0
- package/types/proper-lockfile.d.ts +19 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: concurrency-async
|
|
3
|
+
description: Concurrency and async correctness — races, idempotency, queues, cancellation, locking
|
|
4
|
+
tags: [concurrency, async, race, parallel, queue, lock, mutex, retry, idempotent, worker, thread, promise, deadlock, atomic]
|
|
5
|
+
when: task involves parallel work, background jobs, shared state, or retried operations
|
|
6
|
+
phases: [plan, build, verify]
|
|
7
|
+
provenance: { origin: built-in }
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Concurrency & Async
|
|
11
|
+
|
|
12
|
+
## Rules
|
|
13
|
+
|
|
14
|
+
- **Name the shared state.** Before writing concurrent code, list what is
|
|
15
|
+
read/written by more than one flow. No list = you haven't designed yet.
|
|
16
|
+
Prefer eliminating sharing (message passing, ownership) over locking it.
|
|
17
|
+
- **Check-then-act is a race.** `if (!exists) create()` fails under
|
|
18
|
+
concurrency every time. Push atomicity down: unique constraints +
|
|
19
|
+
upsert, atomic compare-and-swap, `INSERT ... ON CONFLICT` — let the
|
|
20
|
+
storage layer arbitrate.
|
|
21
|
+
- **Everything retried must be idempotent.** Queues deliver at-least-once;
|
|
22
|
+
networks retry; users double-click. Design handlers so processing twice
|
|
23
|
+
= processing once (idempotency keys, natural dedup, upserts).
|
|
24
|
+
- **Every await is a suspension point.** State can change across it —
|
|
25
|
+
re-validate assumptions after resuming; don't cache a check made before
|
|
26
|
+
an await and act on it after.
|
|
27
|
+
- **Bound everything:** every queue has a max depth + backpressure
|
|
28
|
+
behavior; every parallel map has a concurrency limit; every wait has a
|
|
29
|
+
timeout. Unbounded = OOM or thundering herd, only later.
|
|
30
|
+
- **Cancellation propagates.** Long operations accept a signal
|
|
31
|
+
(AbortSignal / context) and pass it to their children; on cancel, clean
|
|
32
|
+
up partial work.
|
|
33
|
+
- **One lock order.** If you must hold two locks, EVERY path acquires them
|
|
34
|
+
in the same documented order — that single rule prevents most deadlocks.
|
|
35
|
+
|
|
36
|
+
## Anti-patterns
|
|
37
|
+
|
|
38
|
+
- **Fire-and-forget promises** — unawaited async work whose failures
|
|
39
|
+
vanish → await it, or hand it to a supervised job runner that logs +
|
|
40
|
+
retries.
|
|
41
|
+
- **Sleep-based coordination** — "wait 500ms so X finishes first" →
|
|
42
|
+
synchronize on the event itself; timing assumptions break under load.
|
|
43
|
+
- **Global mutable singletons as coordination** → explicit passing or a
|
|
44
|
+
real store with atomic ops.
|
|
45
|
+
- **Distributed transactions by hope** — two systems updated without an
|
|
46
|
+
outbox/saga → write locally + outbox table, deliver async, reconcile.
|
|
47
|
+
|
|
48
|
+
## Testing concurrency
|
|
49
|
+
|
|
50
|
+
Force interleavings — don't hope: run the racy pair with a barrier so both
|
|
51
|
+
start together; loop 100×; inject delays at suspension points. A race you
|
|
52
|
+
can't provoke deliberately is a race you'll meet in production
|
|
53
|
+
(`diagnosing-bugs.md` § non-deterministic bugs).
|
|
54
|
+
|
|
55
|
+
## Checklist
|
|
56
|
+
|
|
57
|
+
- [ ] Shared state enumerated; each entry owned, locked, or made atomic
|
|
58
|
+
- [ ] All retried paths provably idempotent (test: run handler twice)
|
|
59
|
+
- [ ] Queues/parallelism/waits all bounded with explicit numbers
|
|
60
|
+
- [ ] Cancellation reaches children; partial work cleaned up
|
|
61
|
+
- [ ] At least one test forces the race window
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: config-and-secrets
|
|
3
|
+
description: Configuration discipline — env-driven config, validation at boot, secret hygiene, environments
|
|
4
|
+
tags: [config, configuration, environment, env, secret, settings, deploy, dotenv, variable]
|
|
5
|
+
when: task adds configuration, environment handling, or deployment settings
|
|
6
|
+
phases: [build, ship]
|
|
7
|
+
provenance: { origin: built-in }
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Configuration & Secrets
|
|
11
|
+
|
|
12
|
+
## Rules
|
|
13
|
+
|
|
14
|
+
- **Config comes from the environment; code ships identical everywhere.**
|
|
15
|
+
Anything that differs between dev/staging/prod (URLs, credentials,
|
|
16
|
+
flags, limits) is env config — never an `if (env === "prod")` branch
|
|
17
|
+
buried in logic.
|
|
18
|
+
- **One config module, validated at boot.** Read `process.env`/equivalent
|
|
19
|
+
in exactly one place; parse, type-check, and apply defaults there; crash
|
|
20
|
+
at startup with a message naming every missing/invalid variable. A
|
|
21
|
+
missing variable discovered at 3am mid-request is a design failure.
|
|
22
|
+
- **Everything has a sane dev default EXCEPT secrets.** A fresh clone runs
|
|
23
|
+
with `.env.example` copied to `.env`. Secrets have NO defaults —
|
|
24
|
+
absence must fail loudly, never fall back to a shared "dev secret".
|
|
25
|
+
- **Secrets:** environment or secrets manager only. `.env` gitignored;
|
|
26
|
+
`.env.example` committed with dummy values documenting every variable.
|
|
27
|
+
Rotate on any suspicion of exposure; a secret that hit git history IS
|
|
28
|
+
exposed (rewriting history doesn't un-leak it — rotate).
|
|
29
|
+
- **Name for grep:** consistent prefix (`APP_DB_URL`, `APP_REDIS_URL`).
|
|
30
|
+
Booleans parse explicitly ("true"/"1"); everything else arrives as a
|
|
31
|
+
string — convert deliberately.
|
|
32
|
+
- **Feature flags are config too:** defined in the config module, defaulted
|
|
33
|
+
off, deleted after full rollout — a flag older than a quarter is debt.
|
|
34
|
+
|
|
35
|
+
## Anti-patterns
|
|
36
|
+
|
|
37
|
+
- **`process.env.X` scattered through the codebase** → impossible to know
|
|
38
|
+
what the app needs; centralize.
|
|
39
|
+
- **Config objects passed 8 layers deep** → modules accept the 2 values
|
|
40
|
+
they need (see `codebase-design.md`), not the world.
|
|
41
|
+
- **"Just for testing" hardcoded credentials** → tests read the same
|
|
42
|
+
config module, pointed at test resources.
|
|
43
|
+
- **Committing the real .env "temporarily"** → it's in history forever;
|
|
44
|
+
rotate everything it contained.
|
|
45
|
+
|
|
46
|
+
## Checklist
|
|
47
|
+
|
|
48
|
+
- [ ] Fresh clone + `.env.example` → app boots (or fails naming exactly what's missing)
|
|
49
|
+
- [ ] Single config module; grep finds no stray env reads
|
|
50
|
+
- [ ] No secret has a default; all documented in .env.example
|
|
51
|
+
- [ ] git history greps clean of credentials
|
|
52
|
+
- [ ] Env-specific behavior expressed as config values, not env-name branches
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: context-hygiene
|
|
3
|
+
description: Externalize state before it decays — write discoveries down, re-read instead of remember
|
|
4
|
+
tags: [meta, context, memory, handoff, session, notes, playbook]
|
|
5
|
+
when: any long session, and always before ending one
|
|
6
|
+
phases: []
|
|
7
|
+
provenance: { origin: built-in, notes: frontier-playbook }
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Context Hygiene
|
|
11
|
+
|
|
12
|
+
Your working memory decays and your session will end — possibly mid-task.
|
|
13
|
+
Everything that matters must live in files, not in your head. The next
|
|
14
|
+
session (you, another agent, a human) starts from what was WRITTEN.
|
|
15
|
+
|
|
16
|
+
## Rules
|
|
17
|
+
|
|
18
|
+
- **Externalize at the moment of discovery**, not "later":
|
|
19
|
+
- Surprise, gotcha, non-obvious behavior → `harness/lessons-decisions.md "..."`
|
|
20
|
+
- Design choice with a why → `infinity-harness decision "..."`
|
|
21
|
+
- Resolved terminology → `harness/docs/DOMAIN.md`
|
|
22
|
+
- Verified fact about an API/tool → `docs/research/` (with frontmatter)
|
|
23
|
+
- **Re-read instead of remember.** Before acting on something you learned
|
|
24
|
+
a while ago (a file's shape, a config value), read it again — it may
|
|
25
|
+
have changed, and your memory of it degrades silently.
|
|
26
|
+
- **Carry one slice.** Work on the current task only; when adjacent
|
|
27
|
+
problems surface, write them down (learn / feature-list backlog) and
|
|
28
|
+
return to the slice. Holding five threads drops four.
|
|
29
|
+
- **Commit = checkpoint your understanding.** Commit after every validated
|
|
30
|
+
step with a message that says WHY, not just what. Uncommitted work +
|
|
31
|
+
ended session = archaeology for the next one.
|
|
32
|
+
- **Before exiting — the clock-out ritual:**
|
|
33
|
+
1. Record any un-captured discoveries (`learn`) and choices (`decision`)
|
|
34
|
+
2. Commit (`git commit -am "session: <state + what's next>"`)
|
|
35
|
+
3. The harness writes `session-handoff.md` at boundaries — make sure
|
|
36
|
+
what YOU know that it doesn't is in progress notes or lessons
|
|
37
|
+
|
|
38
|
+
## Anti-patterns
|
|
39
|
+
|
|
40
|
+
- **"I'll remember"** — across an iteration boundary, you won't exist.
|
|
41
|
+
Write it down.
|
|
42
|
+
- **Giant uncommitted diffs** spanning multiple concerns — impossible to
|
|
43
|
+
hand off, painful to bisect. Commit per validated slice.
|
|
44
|
+
- **Notes in chat/scratch only** — anything not in the repo doesn't exist
|
|
45
|
+
for the next session.
|
|
46
|
+
|
|
47
|
+
## Checklist (end of any session)
|
|
48
|
+
|
|
49
|
+
- [ ] Lessons/decisions recorded for everything non-obvious found
|
|
50
|
+
- [ ] Working tree committed with a why-message
|
|
51
|
+
- [ ] A stranger could resume from handoff + progress + lessons alone
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: databases
|
|
3
|
+
description: Relational database craft — schema design, migrations, transactions, indexing, query safety
|
|
4
|
+
tags: [database, db, sql, postgres, postgresql, mysql, sqlite, schema, migration, transaction, index, query, orm, persistence, storage]
|
|
5
|
+
when: task touches persistent data, schemas, queries, or migrations
|
|
6
|
+
phases: [plan, build, verify]
|
|
7
|
+
provenance: { origin: built-in }
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Databases
|
|
11
|
+
|
|
12
|
+
## Rules
|
|
13
|
+
|
|
14
|
+
- **Schema first, code second.** Write the DDL (tables, types, constraints)
|
|
15
|
+
before the access code. Constraints in the database (`NOT NULL`, `UNIQUE`,
|
|
16
|
+
`FOREIGN KEY`, `CHECK`) beat validation in code — code has bugs; the
|
|
17
|
+
constraint never sleeps.
|
|
18
|
+
- **Every schema change is a migration file** — forward-only, numbered,
|
|
19
|
+
committed, and runnable from scratch (`migrate` on an empty DB must
|
|
20
|
+
produce the current schema). Never edit an applied migration; add a new one.
|
|
21
|
+
- **Transactions around invariants.** Any multi-statement change that must
|
|
22
|
+
hold together goes in one transaction. Name the invariant in a comment.
|
|
23
|
+
- **Parameterized queries only.** String-built SQL is an injection —
|
|
24
|
+
no exceptions, including "internal" tooling.
|
|
25
|
+
- **Index what you filter/join/sort on** — but only with evidence: add the
|
|
26
|
+
index when a real query needs it (EXPLAIN shows a scan), not
|
|
27
|
+
speculatively. Every index taxes writes.
|
|
28
|
+
- **IDs:** prefer surrogate keys (bigint identity or UUIDv7); natural keys
|
|
29
|
+
get UNIQUE constraints instead.
|
|
30
|
+
- **Timestamps:** store UTC (`timestamptz` in Postgres), convert at the edge.
|
|
31
|
+
- **Money/decimals:** exact types (`NUMERIC`), never floats.
|
|
32
|
+
|
|
33
|
+
## The N+1 rule
|
|
34
|
+
|
|
35
|
+
If you query in a loop, you have an N+1. Fix with a join, an `IN` batch,
|
|
36
|
+
or a dataloader — before it ships, not after the incident. Tell: page
|
|
37
|
+
loads trigger a query count proportional to row count.
|
|
38
|
+
|
|
39
|
+
## Migration safety (live systems)
|
|
40
|
+
|
|
41
|
+
Expand → migrate → contract: add the new column/table (nullable or
|
|
42
|
+
defaulted) → backfill + dual-write → switch reads → drop the old thing in
|
|
43
|
+
a LATER migration once nothing references it. Never rename or drop in the
|
|
44
|
+
same deploy that changes code.
|
|
45
|
+
|
|
46
|
+
## Anti-patterns
|
|
47
|
+
|
|
48
|
+
- **SELECT \*** in application code → name the columns; schema drift breaks
|
|
49
|
+
you silently otherwise.
|
|
50
|
+
- **Soft-delete everywhere by default** → only where restore/audit is a real
|
|
51
|
+
requirement; otherwise it poisons every query with `WHERE deleted_at IS NULL`.
|
|
52
|
+
- **Business logic in triggers** → invisible control flow; keep triggers to
|
|
53
|
+
bookkeeping (updated_at) if used at all.
|
|
54
|
+
- **Testing against mocks of the DB** → test against a real (local/ephemeral)
|
|
55
|
+
database; SQLite-in-memory only when production is SQLite.
|
|
56
|
+
|
|
57
|
+
## Checklist
|
|
58
|
+
|
|
59
|
+
- [ ] Schema has constraints for every invariant you rely on
|
|
60
|
+
- [ ] Migrations replay clean on an empty database
|
|
61
|
+
- [ ] No string-concatenated SQL anywhere
|
|
62
|
+
- [ ] Hot queries EXPLAINed; indexes justified by a real plan
|
|
63
|
+
- [ ] Tests hit a real database engine matching production
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: diagnosing-bugs
|
|
3
|
+
description: Feedback-loop-first debugging discipline for hard bugs and regressions
|
|
4
|
+
tags: [debug, debugging, bug, error, failure, crash, flaky, slow, performance, regression, bisect]
|
|
5
|
+
when: something is broken, throwing, failing intermittently, or slow
|
|
6
|
+
phases: [verify, build]
|
|
7
|
+
provenance: { origin: "mattpocock/skills", license: MIT, adapted: true }
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Diagnosing Bugs
|
|
11
|
+
|
|
12
|
+
> Adapted from [mattpocock/skills](https://github.com/mattpocock/skills) (MIT © Matt Pocock).
|
|
13
|
+
> Use during: VERIFY, or any time something is broken, throwing, failing, or slow.
|
|
14
|
+
|
|
15
|
+
A discipline for hard bugs. Follow the phases in order; skip one only when
|
|
16
|
+
you can explicitly justify it.
|
|
17
|
+
|
|
18
|
+
## Phase 1 — Build a feedback loop
|
|
19
|
+
|
|
20
|
+
**This is the skill.** Everything else is mechanical. You need a **tight**
|
|
21
|
+
pass/fail signal that goes red on *this* bug. Without one, no amount of
|
|
22
|
+
staring at code will save you. Spend disproportionate effort here.
|
|
23
|
+
|
|
24
|
+
Ways to construct one, roughly in order:
|
|
25
|
+
|
|
26
|
+
1. **Failing test** at whatever seam reaches the bug — unit, integration, e2e.
|
|
27
|
+
2. **CLI/HTTP invocation** with a fixture input, diffing output against a known-good result.
|
|
28
|
+
3. **Replay a captured trace** — save a real payload/event log, replay it through the code path in isolation.
|
|
29
|
+
4. **Throwaway harness** — a minimal subset of the system (one module, mocked externals) that exercises the bug path with a single call.
|
|
30
|
+
5. **Bisection harness** — if the bug appeared between two known states, automate "boot at state X, check" so `git bisect run` can do the work.
|
|
31
|
+
6. **Differential loop** — run the same input through old vs new version and diff outputs.
|
|
32
|
+
|
|
33
|
+
Once you have *a* loop, **tighten** it: faster (seconds, not minutes),
|
|
34
|
+
sharper (assert the exact symptom, not "didn't crash"), deterministic (pin
|
|
35
|
+
time, seed RNG, isolate filesystem). For flaky bugs, raise the reproduction
|
|
36
|
+
rate — loop the trigger 100×, add stress, narrow timing — until it's
|
|
37
|
+
debuggable.
|
|
38
|
+
|
|
39
|
+
**Completion criterion:** you can name ONE command you have already run that
|
|
40
|
+
is red-capable (asserts the user's exact symptom), deterministic, fast, and
|
|
41
|
+
runnable unattended. If you catch yourself reading code to build a theory
|
|
42
|
+
before this command exists — stop. That's the exact failure this skill
|
|
43
|
+
prevents.
|
|
44
|
+
|
|
45
|
+
## Phase 2 — Reproduce + minimise
|
|
46
|
+
|
|
47
|
+
Run the loop. Watch it go red. Confirm it produces the failure mode that was
|
|
48
|
+
*reported* — not a different failure nearby. Then shrink the repro to the
|
|
49
|
+
smallest scenario that still goes red: cut inputs, callers, config, one at a
|
|
50
|
+
time, re-running after each cut. Done when every remaining element is
|
|
51
|
+
load-bearing.
|
|
52
|
+
|
|
53
|
+
## Phase 3 — Hypothesise
|
|
54
|
+
|
|
55
|
+
Generate **3–5 ranked hypotheses** before testing any. Each must be
|
|
56
|
+
falsifiable: "If X is the cause, then changing Y will make the bug
|
|
57
|
+
disappear." If you can't state the prediction, it's a vibe — discard it.
|
|
58
|
+
Record the ranked list (in the task notes or `harness/lessons-decisions.md`).
|
|
59
|
+
|
|
60
|
+
## Phase 4 — Instrument
|
|
61
|
+
|
|
62
|
+
Each probe maps to one prediction. Change one variable at a time. Prefer a
|
|
63
|
+
debugger/REPL breakpoint over logs; targeted logs over log-everything. Tag
|
|
64
|
+
every debug log with a unique prefix (e.g. `[DEBUG-a4f2]`) so cleanup is a
|
|
65
|
+
single grep. For performance bugs: measure a baseline first, then bisect —
|
|
66
|
+
logs are usually the wrong tool.
|
|
67
|
+
|
|
68
|
+
## Phase 5 — Fix + regression test
|
|
69
|
+
|
|
70
|
+
Write the regression test **before the fix** — at a seam that exercises the
|
|
71
|
+
real bug pattern. If no correct seam exists, that is itself a finding:
|
|
72
|
+
record it as a lesson. Then: watch the test fail → apply the fix → watch it
|
|
73
|
+
pass → re-run the Phase 1 loop against the original scenario.
|
|
74
|
+
|
|
75
|
+
## Phase 6 — Cleanup + post-mortem
|
|
76
|
+
|
|
77
|
+
- [ ] Original repro no longer reproduces
|
|
78
|
+
- [ ] Regression test passes (or absence of seam documented)
|
|
79
|
+
- [ ] All `[DEBUG-...]` instrumentation removed (grep the prefix)
|
|
80
|
+
- [ ] Throwaway harnesses deleted
|
|
81
|
+
- [ ] Record the confirmed hypothesis: `harness/lessons-decisions.md "bug X was caused by Y"`
|
|
82
|
+
|
|
83
|
+
Then ask: what would have prevented this bug? If the answer is architectural
|
|
84
|
+
(no test seam, tangled callers), record it: `infinity-harness decision "..."`.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: domain-modeling
|
|
3
|
+
description: Pin down domain terminology — glossary discipline and decision records
|
|
4
|
+
tags: [domain, glossary, terminology, naming, model, ubiquitous, language, adr, decision]
|
|
5
|
+
when: defining specs, resolving fuzzy or conflicting terms, recording decisions
|
|
6
|
+
phases: [define, plan]
|
|
7
|
+
provenance: { origin: "mattpocock/skills", license: MIT, adapted: true }
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Domain Modeling
|
|
11
|
+
|
|
12
|
+
> Adapted from [mattpocock/skills](https://github.com/mattpocock/skills) (MIT © Matt Pocock).
|
|
13
|
+
> Use during: DEFINE (before writing the spec), and whenever terminology gets fuzzy.
|
|
14
|
+
|
|
15
|
+
Actively build and sharpen the project's domain model as you design.
|
|
16
|
+
The glossary lives in `harness/docs/DOMAIN.md`; decisions live in
|
|
17
|
+
`harness/docs/DECISIONS.md`. Create `DOMAIN.md` the moment the first term
|
|
18
|
+
is resolved — not before, and not "later".
|
|
19
|
+
|
|
20
|
+
## The habits
|
|
21
|
+
|
|
22
|
+
### Sharpen fuzzy language
|
|
23
|
+
When a vague or overloaded term appears ("account", "job", "sync"), propose
|
|
24
|
+
a precise canonical term. "You're saying 'account' — do you mean the
|
|
25
|
+
Customer or the User? Those are different things." One concept, one name,
|
|
26
|
+
everywhere: spec, code, tests, docs.
|
|
27
|
+
|
|
28
|
+
### Challenge against the glossary
|
|
29
|
+
When new text conflicts with an existing definition in `DOMAIN.md`, call it
|
|
30
|
+
out immediately and resolve which meaning wins. Never let two meanings
|
|
31
|
+
coexist silently.
|
|
32
|
+
|
|
33
|
+
### Stress-test with concrete scenarios
|
|
34
|
+
When domain relationships are being defined, invent specific scenarios that
|
|
35
|
+
probe the edges. "A customer cancels half an order that already partially
|
|
36
|
+
shipped — what happens to the invoice?" Force the boundaries between
|
|
37
|
+
concepts to be precise.
|
|
38
|
+
|
|
39
|
+
### Cross-reference with code
|
|
40
|
+
When someone states how something works, check whether the code agrees. If
|
|
41
|
+
the code cancels entire Orders but the spec says partial cancellation is
|
|
42
|
+
possible — surface the contradiction now, not in BUILD.
|
|
43
|
+
|
|
44
|
+
### Update DOMAIN.md inline
|
|
45
|
+
When a term is resolved, write it down right there. Don't batch. Format:
|
|
46
|
+
|
|
47
|
+
```markdown
|
|
48
|
+
## Terms
|
|
49
|
+
|
|
50
|
+
### Order
|
|
51
|
+
A customer's confirmed request to purchase. Created at checkout; immutable
|
|
52
|
+
once `shipped`. NOT the same as a Cart (pre-checkout, mutable).
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`DOMAIN.md` is a glossary and nothing else — no implementation details, no
|
|
56
|
+
scratch notes, no specs.
|
|
57
|
+
|
|
58
|
+
### Record decisions sparingly
|
|
59
|
+
Record a decision (`infinity-harness decision "..."`) only when all three hold:
|
|
60
|
+
|
|
61
|
+
1. **Hard to reverse** — changing your mind later costs something real
|
|
62
|
+
2. **Surprising without context** — a future reader would ask "why?"
|
|
63
|
+
3. **A real trade-off** — genuine alternatives existed and you picked one
|
|
64
|
+
|
|
65
|
+
If any is missing, skip it. Noise buries signal.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: error-handling-logging
|
|
3
|
+
description: Error taxonomy, fail-loud handling, structured logging that debugs itself
|
|
4
|
+
tags: [error, exception, logging, log, observability, retry, crash, handling, monitoring, trace]
|
|
5
|
+
when: task defines error paths, adds logging, or hardens failure behavior
|
|
6
|
+
phases: [build, verify]
|
|
7
|
+
provenance: { origin: built-in }
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Error Handling & Logging
|
|
11
|
+
|
|
12
|
+
## Rules
|
|
13
|
+
|
|
14
|
+
- **Two kinds of errors, two behaviors.** *Expected/operational* (bad
|
|
15
|
+
input, not-found, downstream timeout): handle at the boundary, map to
|
|
16
|
+
the API error shape, keep serving. *Unexpected/programmer* (undefined is
|
|
17
|
+
not a function, invariant broken): crash loud — log, alert, restart.
|
|
18
|
+
Catching-and-continuing a programmer error corrupts state downstream.
|
|
19
|
+
- **Catch where you can act.** A catch block must do one of: recover
|
|
20
|
+
meaningfully, translate to the caller's vocabulary (wrapping the cause),
|
|
21
|
+
or add context and rethrow. `catch (e) {}` and log-and-swallow are how
|
|
22
|
+
systems lie about being healthy.
|
|
23
|
+
- **Preserve the chain.** When wrapping, keep the original (`cause`), the
|
|
24
|
+
stack, and add what you know: which operation, which IDs, which inputs.
|
|
25
|
+
"Database error" tells nothing; "saving order 123: unique violation on
|
|
26
|
+
idempotency_key" tells everything.
|
|
27
|
+
- **Structured logs (JSON), one event per line:** timestamp, level, event
|
|
28
|
+
name, and the IDs someone will grep for at 3am (request_id, user_id,
|
|
29
|
+
order_id). A log line you can't query is decoration.
|
|
30
|
+
- **Correlate:** generate/propagate a request_id at the edge; include it
|
|
31
|
+
in every log line and error response. One incident = one grep.
|
|
32
|
+
- **Levels mean things:** ERROR = a human should look (alertable);
|
|
33
|
+
WARN = degraded but coping; INFO = state changes worth an audit trail;
|
|
34
|
+
DEBUG = off in production. If ERROR fires routinely, it's WARN or a bug.
|
|
35
|
+
- **Never log secrets** — tokens, passwords, full card/PII. Scrub
|
|
36
|
+
centrally at the logger, not at each call site.
|
|
37
|
+
|
|
38
|
+
## Anti-patterns
|
|
39
|
+
|
|
40
|
+
- **Stringly errors** (`throw "failed"`) → typed/coded errors the caller
|
|
41
|
+
can switch on; strings can't be handled, only displayed.
|
|
42
|
+
- **Retry without classification** — retrying a 400 forever, or not
|
|
43
|
+
retrying a timeout → retry only transient errors, bounded + backoff
|
|
44
|
+
(see `concurrency-async.md` for idempotency).
|
|
45
|
+
- **The 500-catch-all that hides the cause** → boundary handler logs the
|
|
46
|
+
full chained error with request_id, returns the safe shape.
|
|
47
|
+
- **printf debugging left behind** → tagged temp logs (`[DEBUG-x]`) and a
|
|
48
|
+
cleanup grep before validate (the anti-placeholder gate will catch you).
|
|
49
|
+
|
|
50
|
+
## Checklist
|
|
51
|
+
|
|
52
|
+
- [ ] Every catch: recovers, translates, or rethrows with context — no swallows
|
|
53
|
+
- [ ] Error responses use the API error shape; internals never leak
|
|
54
|
+
- [ ] Logs structured; request_id flows edge→depths
|
|
55
|
+
- [ ] ERROR level = actionable only; alert noise is a bug
|
|
56
|
+
- [ ] Grep confirms: no secrets in logs, no leftover debug prints
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: frontend-ui
|
|
3
|
+
description: Frontend craft — state discipline, componentization, accessibility, loading/error states
|
|
4
|
+
tags: [frontend, ui, component, react, vue, svelte, state, form, accessibility, a11y, css, browser, render]
|
|
5
|
+
when: task builds or changes user interface
|
|
6
|
+
phases: [plan, build]
|
|
7
|
+
provenance: { origin: built-in }
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Frontend / UI
|
|
11
|
+
|
|
12
|
+
## Rules
|
|
13
|
+
|
|
14
|
+
- **State has one owner.** Every piece of state lives in exactly one place;
|
|
15
|
+
everything else derives from it. Duplicated state = guaranteed
|
|
16
|
+
desync bug. Derive, don't copy.
|
|
17
|
+
- **Server state ≠ UI state.** Data fetched from an API (cacheable,
|
|
18
|
+
refetchable, shared) is a different animal from local UI state (open
|
|
19
|
+
modal, input draft). Use the ecosystem's query layer for server state;
|
|
20
|
+
keep UI state local to the component that owns it.
|
|
21
|
+
- **Every async view renders three states:** loading, error (with a retry
|
|
22
|
+
affordance), and empty ("no results" is not a blank screen). Design them
|
|
23
|
+
first — they're most of what users see on bad networks.
|
|
24
|
+
- **Forms:** controlled state or a form library — not DOM scraping.
|
|
25
|
+
Validate on submit + inline after first blur; disable the submit button
|
|
26
|
+
while in flight (double-submit is a data bug, not a UX nit).
|
|
27
|
+
- **Accessibility is baseline, not polish:** semantic elements first
|
|
28
|
+
(`button`, `label`+input, `nav`); every interactive element keyboard
|
|
29
|
+
reachable with visible focus; images get alt; color is never the only
|
|
30
|
+
signal. If a div has onClick, it's a button — make it one.
|
|
31
|
+
- **Componentize by responsibility, not by size.** A component that takes
|
|
32
|
+
12 props wants to be two. Container (data) vs presentational (markup)
|
|
33
|
+
split keeps both testable.
|
|
34
|
+
- **Test behavior through the user's eyes:** render, interact (click/type),
|
|
35
|
+
assert visible outcome. Don't assert internal state or mock child
|
|
36
|
+
components — that's the implementation-coupled trap (`tdd.md`).
|
|
37
|
+
|
|
38
|
+
## Anti-patterns
|
|
39
|
+
|
|
40
|
+
- **useEffect as event handler** — effects synchronize with external
|
|
41
|
+
systems; user actions belong in handlers. Effect-chains that set state
|
|
42
|
+
which triggers effects = rewrite the data flow.
|
|
43
|
+
- **Prop drilling 4+ levels** → lift to context/store — but only genuinely
|
|
44
|
+
shared state; context is not a junk drawer.
|
|
45
|
+
- **Pixel-perfect absolute positioning** → flexbox/grid; the content WILL
|
|
46
|
+
change length and language.
|
|
47
|
+
- **Swallowed promise rejections in handlers** → every await in a handler
|
|
48
|
+
has a catch that surfaces to the user.
|
|
49
|
+
|
|
50
|
+
## Checklist
|
|
51
|
+
|
|
52
|
+
- [ ] Loading / error / empty rendered for every async view
|
|
53
|
+
- [ ] Keyboard-only walkthrough works; focus visible
|
|
54
|
+
- [ ] No state duplicated across components (grep for copied fetch results)
|
|
55
|
+
- [ ] Forms: double-submit blocked, validation inline, errors named
|
|
56
|
+
- [ ] Interaction tests assert what the USER sees
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: grilling
|
|
3
|
+
description: Stress-test a spec or plan with relentless one-at-a-time questions before committing
|
|
4
|
+
tags: [grill, spec, requirements, questions, stress, interview, scope, clarify]
|
|
5
|
+
when: before proposing the sprint contract, or when a plan feels underspecified
|
|
6
|
+
phases: [define]
|
|
7
|
+
provenance: { origin: "mattpocock/skills", license: MIT, adapted: true }
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Grilling — Stress-Test the Spec
|
|
11
|
+
|
|
12
|
+
> Adapted from [mattpocock/skills](https://github.com/mattpocock/skills) (MIT © Matt Pocock).
|
|
13
|
+
> Use during: DEFINE (before the sprint contract is agreed).
|
|
14
|
+
|
|
15
|
+
A spec that hasn't been grilled is a guess. Before proposing the sprint
|
|
16
|
+
contract, interrogate the plan until it stops changing.
|
|
17
|
+
|
|
18
|
+
## With a human available (copilot mode)
|
|
19
|
+
|
|
20
|
+
Interview the human relentlessly about every aspect of the spec until you
|
|
21
|
+
reach shared understanding. Walk down each branch of the decision tree,
|
|
22
|
+
resolving dependencies between decisions one by one.
|
|
23
|
+
|
|
24
|
+
- Ask questions **one at a time** — wait for each answer before continuing.
|
|
25
|
+
Multiple questions at once are bewildering.
|
|
26
|
+
- For each question, provide your **recommended answer**.
|
|
27
|
+
- If a *fact* can be found by exploring the environment (filesystem, code,
|
|
28
|
+
docs), look it up rather than asking. The *decisions* are the human's —
|
|
29
|
+
put each one to them.
|
|
30
|
+
- Do not propose the contract until the human confirms shared understanding.
|
|
31
|
+
|
|
32
|
+
## Without a human (autopilot mode) — self-grill
|
|
33
|
+
|
|
34
|
+
Ask the questions anyway, and answer each with your best recommendation.
|
|
35
|
+
Write the question + answer pairs into `specs/prd.md` under
|
|
36
|
+
"## Resolved Questions" so a human can audit them later. Standard probes:
|
|
37
|
+
|
|
38
|
+
- Who uses this, and what do they do the moment it works?
|
|
39
|
+
- What is explicitly OUT of scope this sprint?
|
|
40
|
+
- What's the smallest end-to-end slice that proves the concept?
|
|
41
|
+
- What breaks first under bad input / no network / concurrent use?
|
|
42
|
+
- How will we KNOW it works — what command or check proves it?
|
|
43
|
+
- What existing code/library already does part of this?
|
|
44
|
+
- What's the most likely way this plan fails, and what's the fallback?
|
|
45
|
+
|
|
46
|
+
Any question you cannot answer confidently becomes an exclusion in the
|
|
47
|
+
sprint contract or a research task (`harness/skills/research.md`) — never
|
|
48
|
+
a silent assumption.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: http-apis
|
|
3
|
+
description: HTTP/REST API design — resources, status codes, errors, versioning, pagination, idempotency
|
|
4
|
+
tags: [api, http, rest, endpoint, route, json, status, error, versioning, pagination, webhook, request, response]
|
|
5
|
+
when: task designs or implements HTTP endpoints or consumes external APIs
|
|
6
|
+
phases: [plan, build]
|
|
7
|
+
provenance: { origin: built-in }
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# HTTP APIs
|
|
11
|
+
|
|
12
|
+
## Rules
|
|
13
|
+
|
|
14
|
+
- **Resources, not verbs.** `POST /orders`, `GET /orders/:id` — the verb is
|
|
15
|
+
the method. RPC-ish actions get a sub-resource: `POST /orders/:id/cancel`.
|
|
16
|
+
- **Status codes carry meaning:** 200 ok · 201 created (+ `Location`) ·
|
|
17
|
+
204 no body · 400 client sent garbage · 401 unauthenticated ·
|
|
18
|
+
403 unauthorized · 404 absent (also for "exists but not yours") ·
|
|
19
|
+
409 conflict · 422 validation · 429 throttled · 5xx = OUR bug, never the
|
|
20
|
+
client's. Don't return 200 with `{"error": ...}`.
|
|
21
|
+
- **One error shape everywhere:**
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{ "error": { "code": "ORDER_NOT_CANCELLABLE", "message": "Order already shipped", "details": [] } }
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`code` is machine-stable (clients switch on it); `message` is for humans
|
|
28
|
+
and may change.
|
|
29
|
+
- **Validate at the edge.** Reject unknown fields, wrong types, and
|
|
30
|
+
out-of-range values with 400/422 naming the field. Never let bad input
|
|
31
|
+
deep into the system.
|
|
32
|
+
- **Mutations are idempotent or idempotency-keyed.** PUT/DELETE naturally;
|
|
33
|
+
POST that charges/creates accepts an `Idempotency-Key` header and
|
|
34
|
+
dedupes retries — networks WILL retry.
|
|
35
|
+
- **Paginate every list** from day one (cursor > offset for anything that
|
|
36
|
+
grows). Return `next_cursor`; cap page size.
|
|
37
|
+
- **Version in the path** (`/v1/`) and only break within a version never.
|
|
38
|
+
Additive changes (new optional fields) don't need a bump.
|
|
39
|
+
- **Timeouts + retries on everything you CALL:** explicit connect/read
|
|
40
|
+
timeouts, retry only idempotent calls, exponential backoff + jitter,
|
|
41
|
+
honor `Retry-After`.
|
|
42
|
+
|
|
43
|
+
## Anti-patterns
|
|
44
|
+
|
|
45
|
+
- **Chatty endpoints** — client needs 5 calls to render one screen → add a
|
|
46
|
+
composed read endpoint; don't make the client an orchestrator.
|
|
47
|
+
- **Tunneling through POST /doThing** with a type field → separate routes;
|
|
48
|
+
observability and auth depend on it.
|
|
49
|
+
- **Leaking internals** — DB column names, stack traces, ORM errors in
|
|
50
|
+
responses → map to the error shape at the boundary.
|
|
51
|
+
- **Webhooks without verification** — sign payloads (HMAC), verify
|
|
52
|
+
signature + timestamp, respond 2xx fast and process async.
|
|
53
|
+
|
|
54
|
+
## Checklist
|
|
55
|
+
|
|
56
|
+
- [ ] Every endpoint: documented method+path, request/response example, error codes
|
|
57
|
+
- [ ] Error shape uniform; codes machine-stable
|
|
58
|
+
- [ ] Lists paginated; mutations idempotent or keyed
|
|
59
|
+
- [ ] Outbound calls have timeouts, bounded retries, backoff
|
|
60
|
+
- [ ] Contract exercised by an integration test hitting real routes
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: performance
|
|
3
|
+
description: Performance work — measure first, fix the biggest cost, cache with invalidation, verify
|
|
4
|
+
tags: [performance, slow, latency, profiling, optimize, cache, memory, benchmark, throughput, speed]
|
|
5
|
+
when: something is slow, memory-hungry, or a task sets performance targets
|
|
6
|
+
phases: [verify, build]
|
|
7
|
+
provenance: { origin: built-in }
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Performance
|
|
11
|
+
|
|
12
|
+
## Rules
|
|
13
|
+
|
|
14
|
+
- **Measure before touching anything.** Reproduce the slowness with a
|
|
15
|
+
number (timing harness, profiler, EXPLAIN, flamegraph) and save the
|
|
16
|
+
baseline. Optimizing without a measurement is guessing — most guesses
|
|
17
|
+
about "the slow part" are wrong.
|
|
18
|
+
- **Fix costs in order of size.** The profile ranks them. The top item is
|
|
19
|
+
usually I/O shaped: N+1 queries, missing index, sync call in a loop,
|
|
20
|
+
chatty API — algorithmic elegance rarely matters before those are gone.
|
|
21
|
+
- **Set a target before optimizing** ("p95 < 300ms", "fits in 512MB").
|
|
22
|
+
Without a target, optimization never ends; with one, you stop on hit.
|
|
23
|
+
- **Do less work first:** don't compute what nobody reads, paginate,
|
|
24
|
+
filter at the source (DB, not app), batch round-trips, stream instead of
|
|
25
|
+
buffering whole payloads.
|
|
26
|
+
- **Cache only with an invalidation story.** Every cache states: key,
|
|
27
|
+
TTL/eviction, and what makes it stale. A cache without invalidation is a
|
|
28
|
+
correctness bug on a delay. Prefer short TTLs you can reason about.
|
|
29
|
+
- **Verify with the same harness** that produced the baseline; keep the
|
|
30
|
+
before/after numbers in the commit message. No number = no optimization
|
|
31
|
+
happened.
|
|
32
|
+
- **Concurrency ≠ speed.** Parallelizing CPU-bound work on one core, or
|
|
33
|
+
hammering a saturated DB harder, makes it slower. Know which resource is
|
|
34
|
+
the bottleneck first.
|
|
35
|
+
|
|
36
|
+
## Anti-patterns
|
|
37
|
+
|
|
38
|
+
- **Micro-optimizing the cold path** — shaving the loop while every call
|
|
39
|
+
does a network round-trip → profile decides, not intuition.
|
|
40
|
+
- **Speculative caching layers** — Redis before there's a measured need →
|
|
41
|
+
measure, then cache the specific hot read.
|
|
42
|
+
- **Load testing at 1×** — perf verified only at dev-scale → test at
|
|
43
|
+
expected load ± spike; N+1s hide at N=3.
|
|
44
|
+
- **"It's the language/framework"** — usually it's your query pattern.
|
|
45
|
+
Prove the platform is the ceiling before replatforming.
|
|
46
|
+
|
|
47
|
+
## Checklist
|
|
48
|
+
|
|
49
|
+
- [ ] Baseline measured and saved before any change
|
|
50
|
+
- [ ] Target stated; work stops when hit
|
|
51
|
+
- [ ] Top profile item addressed first (linked evidence)
|
|
52
|
+
- [ ] Every cache has key/TTL/invalidation written down
|
|
53
|
+
- [ ] After-numbers from the same harness, in the commit
|