entropy-machines 0.1.1
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/LICENSE +93 -0
- package/README.md +68 -0
- package/agents/isolated-worker.md +128 -0
- package/agents/verifier.md +158 -0
- package/bin/dispatch +700 -0
- package/bin/doclint +460 -0
- package/bin/drain +507 -0
- package/bin/drain-pick.py +168 -0
- package/bin/drain-prompt.md +67 -0
- package/bin/drain-run.sh +342 -0
- package/bin/entropy-machines-init +285 -0
- package/bin/handoff +1151 -0
- package/bin/init +232 -0
- package/bin/post-fold-audit +377 -0
- package/bin/serve +724 -0
- package/bin/status +208 -0
- package/bin/tracker +153 -0
- package/docs/AGENT-QUICKSTART.md +86 -0
- package/docs/CONFIG.md +68 -0
- package/docs/NPM.md +91 -0
- package/docs/SERVE.md +74 -0
- package/docs/TRACKER-ADAPTER.md +66 -0
- package/doctrine/HANDOFF-PROMPT.md +63 -0
- package/doctrine/README.md +62 -0
- package/doctrine/ROLES.md +27 -0
- package/doctrine/WORKFLOW.md +87 -0
- package/hooks/commit-msg +24 -0
- package/hooks/post-checkout +354 -0
- package/hooks/pre-commit +33 -0
- package/lib/PRD-001-orientation.html +1180 -0
- package/lib/REPORT-TEMPLATE.html +413 -0
- package/lib/changelog-collate.mjs +328 -0
- package/lib/changelog-guard.sh +157 -0
- package/lib/changelog-new.mjs +70 -0
- package/lib/config.mjs +283 -0
- package/lib/config.py +317 -0
- package/lib/doc-template.html +807 -0
- package/lib/entropy-drain.plist.in +59 -0
- package/lib/entropy-drain.service.in +53 -0
- package/lib/entropy-drain.timer.in +36 -0
- package/lib/fail-first.mjs +901 -0
- package/lib/handoff-guard.sh +623 -0
- package/lib/install-hooks.sh +169 -0
- package/lib/notes.py +675 -0
- package/lib/preflight-tree.mjs +82 -0
- package/lib/roots.sh +212 -0
- package/lib/themes/daylight.css +84 -0
- package/lib/themes/high-contrast.css +36 -0
- package/lib/tracker-file +333 -0
- package/lib/tracker-view.py +784 -0
- package/package.json +38 -0
package/docs/SERVE.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# bin/serve — the factory lights
|
|
2
|
+
|
|
3
|
+
bin/serve [port] # default 8787, 127.0.0.1 only
|
|
4
|
+
|
|
5
|
+
A local web server, not a CLI. The owner's surface is a browser tab: dialogue
|
|
6
|
+
docs with response boxes answered in place, plus a dashboard.
|
|
7
|
+
|
|
8
|
+
| Route | What |
|
|
9
|
+
|---|---|
|
|
10
|
+
| `GET /` | Dashboard — ready issues, in-flight claims, per-doc answered counts, recent events. |
|
|
11
|
+
| `GET /<doc>.html` | A doc from `docs.dir`, save patch and theme injected at serve time. |
|
|
12
|
+
| `GET /__docversion?file=…` | `{"reviews": "<hash>"}`, polled by the live-reload watcher. |
|
|
13
|
+
| `POST /__save?file=…` | Body `{"<data-resp>": "<answer>"}`, merged atomically. |
|
|
14
|
+
|
|
15
|
+
Everything else 404s — no listing, nothing outside `docs.dir`.
|
|
16
|
+
|
|
17
|
+
## The rule
|
|
18
|
+
|
|
19
|
+
**No report, plan or PRD is ever published to an external site, and none may
|
|
20
|
+
depend on one** — a doc pulling a font or stylesheet from someone else's server
|
|
21
|
+
breaks offline and tells that server it was opened. And every section the owner
|
|
22
|
+
is asked about has a box to answer in.
|
|
23
|
+
|
|
24
|
+
**`bin/doclint` enforces exactly those two things.** Run it before handing over
|
|
25
|
+
a URL. Exit 1 names file, line and problem for an off-machine origin in an
|
|
26
|
+
`href`, `src`, CSS `url()` or script literal (prose mentioning a URL passes, as
|
|
27
|
+
do `data:` URIs and relative paths), or for an `<h2>` with no answer box, two
|
|
28
|
+
boxes sharing a key, or a missing `#saveBtn` / `#responses-data`.
|
|
29
|
+
`data-informational` on an `<h2>` skips that section — no whole-file exemption.
|
|
30
|
+
Exit 2 is a refusal, not a pass: it could not read its input. **The look is not
|
|
31
|
+
checked** — `lib/REPORT-TEMPLATE.html` is a default, not a conformance target.
|
|
32
|
+
|
|
33
|
+
## Docs and themes
|
|
34
|
+
|
|
35
|
+
`docs.dir` (default `entropy-machines-docs/`) is created if absent; docs sit directly in
|
|
36
|
+
it, since the save-path check refuses any `file` containing `/`. Reports start
|
|
37
|
+
from `lib/REPORT-TEMPLATE.html`, PRDs from `lib/doc-template.html`, whose header
|
|
38
|
+
comment authoritatively describes the response-box machinery. `docs.theme` picks
|
|
39
|
+
a stylesheet from `lib/themes/` (`high-contrast`, `daylight`), inlined on each
|
|
40
|
+
`GET` between the `entropy-machines-theme` markers; an unknown name is refused by name.
|
|
41
|
+
|
|
42
|
+
## Saving
|
|
43
|
+
|
|
44
|
+
A save endpoint taking a path is a whole-disk write primitive, so `file` is
|
|
45
|
+
validated on both routes: no `/`, no `..`, ends `.html`, already in `docs.dir`.
|
|
46
|
+
Refusals return plain 4xx JSON. A valid save merges into `#responses-data` and
|
|
47
|
+
the matching `<textarea>`s via temp-file-plus-`os.replace`.
|
|
48
|
+
|
|
49
|
+
**The save patch is injected on every `GET`, never written into the file** —
|
|
50
|
+
otherwise an older doc falls back to `showSaveFilePicker`, writing to
|
|
51
|
+
`~/Downloads` and leaving the reviewed file untouched. It runs in the click
|
|
52
|
+
capture phase with `stopImmediatePropagation`, so it wins whatever else is bound.
|
|
53
|
+
|
|
54
|
+
## Hot reload
|
|
55
|
+
|
|
56
|
+
The doc polls `__docversion` every 5s: a clean tab reloads itself, a dirty one
|
|
57
|
+
shows a bar instead. Typed text is never clobbered.
|
|
58
|
+
|
|
59
|
+
`reviews` is **not** a whole-file hash — your own 💾 rewrites `#responses-data`
|
|
60
|
+
and the answered `<textarea>`s, which would read back as an incoming reply. It
|
|
61
|
+
is a SHA-256 with exactly those two regions blanked. **Consequence:**
|
|
62
|
+
overwriting an existing answer is indistinguishable from the reader typing and
|
|
63
|
+
triggers no reload; land a reply as new content outside the answer regions.
|
|
64
|
+
|
|
65
|
+
## Dashboard, port, root
|
|
66
|
+
|
|
67
|
+
`GET /` uses only state already tracked: `tracker ready`; `tracker notes` through
|
|
68
|
+
`lib/notes.py claims --hours 24`, the same derivation `bin/dispatch` uses for its
|
|
69
|
+
denylist; each doc's own counts; the notes tail. An unreachable tracker says so
|
|
70
|
+
rather than rendering empty — unreadable and empty are different facts.
|
|
71
|
+
|
|
72
|
+
A busy port refuses with the next port to try, not a traceback. One root,
|
|
73
|
+
resolved via `--git-common-dir` so it is the MAIN checkout even from a linked
|
|
74
|
+
worktree; no environment override, so `cd` to serve another project.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# The tracker adapter
|
|
2
|
+
|
|
3
|
+
The harness needs **six** operations from an issue tracker. That is the whole
|
|
4
|
+
dependency. Anything richer belongs to your tracker, not to us.
|
|
5
|
+
|
|
6
|
+
A backend is any executable that implements this CLI. `tracker.backend` in
|
|
7
|
+
`config.json` selects one.
|
|
8
|
+
|
|
9
|
+
| Command | Contract |
|
|
10
|
+
|---|---|
|
|
11
|
+
| `show <id>` | Print one issue as JSON on stdout. Exit 3 if no such issue. |
|
|
12
|
+
| `notes [--issue <id>]` | Print the note log as JSONL, one record per line, oldest first. With `--issue`, only that issue's records. |
|
|
13
|
+
| `remember --issue <id> <text>` | Append one note record. Must be atomic under concurrent writers. |
|
|
14
|
+
| `claim <id>` | Mark in-progress and record the claimant. Exit 4 if already claimed by someone else. |
|
|
15
|
+
| `ready` | Print claimable issues as JSONL — no open blockers, not held, not gated. |
|
|
16
|
+
| `set <id> <key>=<value>` | Set a field. Exit 2 on an unknown key. |
|
|
17
|
+
|
|
18
|
+
## The note record
|
|
19
|
+
|
|
20
|
+
The original harness wrote notes as free text with em-dash delimiters and
|
|
21
|
+
re-parsed that format in four independent places. Any format change needed
|
|
22
|
+
four synchronised edits, so the format could never change.
|
|
23
|
+
|
|
24
|
+
Notes are JSONL here. One record per line, one parser, `lib/notes`:
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{"ts":"2026-08-27T19:04:11Z","verb":"DISPATCH","issue":"i-foo","actor":"orchestrator",
|
|
28
|
+
"fields":{"scope":["src/a.ts","tests/a.test.ts"],"brief":"one-line task"}}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
- `verb` is the record type: `DISPATCH`, `HANDOFF`, `INTERROGATION`, or a
|
|
32
|
+
backend-defined string. Consumers MUST ignore verbs they do not know.
|
|
33
|
+
- `fields` is free-form per verb. `scope` is an ARRAY — the original parsed
|
|
34
|
+
comma-vs-space-delimited strings differently in different call sites.
|
|
35
|
+
- Records are append-only. Nothing rewrites or deletes one.
|
|
36
|
+
|
|
37
|
+
## What we deliberately do not require
|
|
38
|
+
|
|
39
|
+
Milestones, priorities, assignees, sprints, epics, time tracking. If your
|
|
40
|
+
tracker has them, good; the harness will not read them.
|
|
41
|
+
|
|
42
|
+
## Two states a general tracker usually lacks
|
|
43
|
+
|
|
44
|
+
A `command` backend that cannot express these will degrade: `ready` will
|
|
45
|
+
over-report, and the orchestrator has to catch it by eye.
|
|
46
|
+
|
|
47
|
+
- **held** — decided not to do now, WITH a reason. Distinct from closed: a
|
|
48
|
+
held issue is a decision, not a completion, and `ready` must not return it.
|
|
49
|
+
- **gated** — blocked on an open question outside the issue graph (a design
|
|
50
|
+
decision, an owner ruling). Distinct from blocked-by, which points at
|
|
51
|
+
another issue. `ready` must not return a gated issue, and must fail closed
|
|
52
|
+
when the gate handle is unknown.
|
|
53
|
+
|
|
54
|
+
**Neither is a `status` value.** `status` holds only `notstarted`, `progress`
|
|
55
|
+
or `done`. Held-ness is the presence of a `heldWhy` field; gated-ness is the
|
|
56
|
+
presence of a `gate` field. This is deliberate. Modelling them as statuses
|
|
57
|
+
makes the reason optional — a bare `status: held` is expressible, and a held
|
|
58
|
+
issue whose reason nobody recorded is indistinguishable from one somebody
|
|
59
|
+
forgot. Carrying the reason in the field that defines the state makes the
|
|
60
|
+
reasonless version unrepresentable.
|
|
61
|
+
|
|
62
|
+
It also keeps the two orthogonal to progress: an issue can be half-implemented
|
|
63
|
+
AND held, which a single status enum forces you to lose.
|
|
64
|
+
|
|
65
|
+
The built-in `file` backend enforces this — `set <id> status=held` is refused
|
|
66
|
+
with a message naming the right field.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Reshare prompt
|
|
2
|
+
|
|
3
|
+
Paste the block below into a fresh session to hand this work off. It is
|
|
4
|
+
self-contained — it assumes the reader knows nothing about the repo or this
|
|
5
|
+
conversation.
|
|
6
|
+
|
|
7
|
+
Use it if you want the doctrine extended or revised by an agent that is not
|
|
8
|
+
carrying a sprint's worth of context.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
Read `doctrine/README.md`, `doctrine/ROLES.md` and `doctrine/WORKFLOW.md` in
|
|
14
|
+
this repo, plus `agents/verifier.md` and `agents/isolated-worker.md`. They
|
|
15
|
+
document how work moves through this project: issues → worked → verified →
|
|
16
|
+
folded → sprint report → issues.
|
|
17
|
+
|
|
18
|
+
Four roles: the owner (decides, ticks, says when held work may merge), workers
|
|
19
|
+
(one scoped issue each, in isolated git worktrees, commit nothing), a verifier
|
|
20
|
+
(ONE sweep across all finished work for a sprint, on a clean tree, before
|
|
21
|
+
anything is folded), and orchestrators (fold, land, report, file issues, talk to
|
|
22
|
+
the owner — the only committers).
|
|
23
|
+
|
|
24
|
+
The design intent, in the owner's words: verification is a quick check across
|
|
25
|
+
the workstreams for a sprint — not one verifier per worker — to confirm nothing
|
|
26
|
+
is glaringly wrong and the work is in an acceptable, testable, healthy state.
|
|
27
|
+
The orchestrator's job is the higher order: rather than polluting its context
|
|
28
|
+
with nitty-gritty verification, it inherits healthier work to fold in, which
|
|
29
|
+
frees orchestrators to work more fully with the owner instead of getting buried.
|
|
30
|
+
|
|
31
|
+
YOUR TASK: <state it here>
|
|
32
|
+
|
|
33
|
+
Things to know before you change any of it:
|
|
34
|
+
|
|
35
|
+
- Every rule in those files exists because something went wrong once, and most
|
|
36
|
+
say so. Do not shorten a rule into a slogan — the incident IS the argument.
|
|
37
|
+
If you remove one, say what now prevents the incident.
|
|
38
|
+
- The repo convention is that a new working rule names what enforces it (a
|
|
39
|
+
wrapper, a gate, a refusal, a CI job) or states plainly that nothing does.
|
|
40
|
+
- Depth belongs to the orchestrator, breadth to the verifier. The verifier
|
|
41
|
+
checks a test EXISTS and plausibly bites; proving a test is pinned to
|
|
42
|
+
behaviour — by mutating one half of a fix and watching the right subset go
|
|
43
|
+
red — stays with the orchestrator, on risky changes only. Do not collapse
|
|
44
|
+
those two into one role.
|
|
45
|
+
- A project can keep its own local, gitignored docs directory outside this
|
|
46
|
+
one; whether it is reachable from inside an agent's worktree depends on
|
|
47
|
+
whether it is listed in `config.json`'s `worktree.linkPaths` AND whether
|
|
48
|
+
this clone's git hooks were installed (`hooks/post-checkout` is what
|
|
49
|
+
symlinks it in). Don't assume either way — paste what an agent needs into
|
|
50
|
+
its brief regardless: proximity, not reachability, is what gets a rule
|
|
51
|
+
followed (96% vs 7%, measured on an earlier version of this workflow).
|
|
52
|
+
- Agent definitions are picked up at SESSION START only in Claude Code, which
|
|
53
|
+
is what this doctrine's reference implementation targets — `subagent_type`
|
|
54
|
+
needs a restart to see a new one. A different agent runner may behave
|
|
55
|
+
differently; check it before assuming this restart requirement carries
|
|
56
|
+
over.
|
|
57
|
+
|
|
58
|
+
Open question the owner raised and I could not resolve: they described the
|
|
59
|
+
roles as "4 — workers, verifiers, orchestrators", which names three. I
|
|
60
|
+
documented the fourth as the OWNER, since the owner holds decisions no agent may
|
|
61
|
+
take (the sprint-close tick has no code path, and held work merges only on their
|
|
62
|
+
say-so). If a different fourth role was meant, ask.
|
|
63
|
+
```
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# The factory
|
|
2
|
+
|
|
3
|
+
How work moves through this repo: the cycle, the roles, and the rules that
|
|
4
|
+
exist because something went wrong once.
|
|
5
|
+
|
|
6
|
+
This directory is the home for workflow documentation. A project can keep its
|
|
7
|
+
own local, gitignored docs directory for product and design material
|
|
8
|
+
alongside it; this directory is committed, because it governs how anyone —
|
|
9
|
+
human or agent — is supposed to operate here.
|
|
10
|
+
|
|
11
|
+
- **[WORKFLOW.md](WORKFLOW.md)** — the cycle, stage by stage, with what must be
|
|
12
|
+
true to leave each stage.
|
|
13
|
+
- **[ROLES.md](ROLES.md)** — who does what, and what each role must never do.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## The cycle
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
┌──────────────────────────────────────────────────────────┐
|
|
21
|
+
│ │
|
|
22
|
+
▼ │
|
|
23
|
+
ISSUES ──▶ WORKED ──▶ VERIFIED ──▶ FOLDED ──▶ SPRINT REPORT ──┘
|
|
24
|
+
(tracker) (workers) (verifier) (orchestr.) (orchestrator +
|
|
25
|
+
owner ticks)
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Issues get worked. Finished work is verified as a sprint, on a clean tree,
|
|
29
|
+
before anything is folded. The orchestrator folds it in and lands it. The sprint
|
|
30
|
+
ends in a report the owner ticks. Rulings from that report — plus whatever
|
|
31
|
+
arrives from other workstreams — become the next issues, and round it goes.
|
|
32
|
+
|
|
33
|
+
## The one-paragraph version
|
|
34
|
+
|
|
35
|
+
**Workers** implement one scoped issue each, in isolated git worktrees, and
|
|
36
|
+
commit nothing. **A verifier** sweeps all of their finished output together,
|
|
37
|
+
once, on a clean tree, and reports whether it is healthy. **An orchestrator**
|
|
38
|
+
folds the verified work into `main`, re-proving anything risky by its own
|
|
39
|
+
mutation, and is the only committer. The orchestrator then writes the sprint
|
|
40
|
+
report, the owner ticks it, and the rulings in it become issues.
|
|
41
|
+
|
|
42
|
+
## Why it is shaped like this
|
|
43
|
+
|
|
44
|
+
**Verification is a sprint-level sweep, not a per-agent gate.** One verifier
|
|
45
|
+
across the whole sprint is cheaper than one per worker, and it is the only
|
|
46
|
+
vantage point that can see two changes interacting. It also keeps the
|
|
47
|
+
orchestrator's attention on the owner rather than on nitty-gritty re-running.
|
|
48
|
+
|
|
49
|
+
**The orchestrator still owns depth.** The verifier checks that a test exists
|
|
50
|
+
and plausibly bites. Proving a test is pinned to *behaviour* — by mutating one
|
|
51
|
+
half of a fix at a time and watching the right subset go red — stays with the
|
|
52
|
+
orchestrator, on the risky changes only. A subagent's proof can be vacuous in a
|
|
53
|
+
way that re-running it cannot reveal; only an independent mutation shows what a
|
|
54
|
+
test is actually pinned to.
|
|
55
|
+
|
|
56
|
+
**Work is landed at the behest of the owner** If they're working wtith you directly likely they'll indicate what to do, to be mindful of other agents, when pulling work in. You should also be intentional about surfacing risks of collission and surfacing what you can in advance of merging work.
|
|
57
|
+
|
|
58
|
+
**The clean tree is the source of truth.** The orchestrator's checkout is
|
|
59
|
+
usually dirty with other sessions' work. A worker's worktree branches from
|
|
60
|
+
committed HEAD, so it is clean. When a worker reports a failure the
|
|
61
|
+
orchestrator cannot reproduce, **the worker is right** — see the incident in
|
|
62
|
+
[WORKFLOW.md](WORKFLOW.md#the-clean-tree-is-the-truth).
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Roles
|
|
2
|
+
|
|
3
|
+
Four roles: three agents and the owner. Every prohibition is here because
|
|
4
|
+
something went wrong once.
|
|
5
|
+
|
|
6
|
+
| Role | Does | Never |
|
|
7
|
+
|---|---|---|
|
|
8
|
+
| **Owner** | Rules on open questions in served docs, ticks the sprint closed, says when held work merges. | Delegates the ready tick — there is no code path to it. |
|
|
9
|
+
| **Worker**<br>`agents/isolated-worker.md` | One scoped issue in its own worktree: the change, its tests, its `changelog.d/` fragment, and a `HANDOFF.md` — `changed` / `found` / `assumed` / `next`. | Commits, pushes, merges, runs the full build, edits outside its scope, or touches another worktree. |
|
|
10
|
+
| **Verifier**<br>`agents/verifier.md` | One sweep per sprint (not per worker) on a clean tree: applies every finished patch together, builds, regenerates, runs the suites, checks each change has a test that bites. Verdict with real numbers. | Fixes, lands, commits, reviews design, edits a worker's worktree, or repeats a number it did not re-run. |
|
|
11
|
+
| **Orchestrator**<br>the session talking to the owner | Dispatches, folds, lands, reports, files rulings as issues. The **only committer**. | Lands held work without the owner, relays a subagent's verification as its own, or lets its cwd leave the repo root. |
|
|
12
|
+
|
|
13
|
+
**Worker, hard-won:** write `HANDOFF.md` early — a dead worker otherwise
|
|
14
|
+
leaves only a diff. Check `git rev-parse --git-common-dir` first — not
|
|
15
|
+
`--show-toplevel`, which prints the worktree's own path and cannot confirm
|
|
16
|
+
which repo you branched from. Symlink
|
|
17
|
+
`worktree.linkPaths` before running anything. Report what you did **not** run.
|
|
18
|
+
|
|
19
|
+
| Job | Agent |
|
|
20
|
+
|---|---|
|
|
21
|
+
| Implement a scoped issue | `isolated-worker` |
|
|
22
|
+
| Sprint verification sweep | `verifier` |
|
|
23
|
+
| Read-only investigation | `Explore` — no worktree cost |
|
|
24
|
+
| Fold, land, report, file | the orchestrator itself |
|
|
25
|
+
|
|
26
|
+
Written against Claude Code: `agents/*.md` are picked up **at session start
|
|
27
|
+
only**. Check your runner's equivalent.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# The workflow
|
|
2
|
+
|
|
3
|
+
```
|
|
4
|
+
ISSUES ──▶ WORKED ──▶ VERIFIED ──▶ FOLDED ──▶ SPRINT REPORT ──▶ ISSUES
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
## 1. Issues
|
|
8
|
+
The tracker is the state of the world, not the docs (`../docs/TRACKER-ADAPTER.md`).
|
|
9
|
+
**Leave when:** issues are chosen, non-overlapping in file scope, none gated on an
|
|
10
|
+
open decision. Held means decided, not forgotten. Two issues on one file serialize.
|
|
11
|
+
|
|
12
|
+
## 2. Worked
|
|
13
|
+
One worker per issue, each in its own worktree. `dispatch <id> --files "…" --brief
|
|
14
|
+
"…"` **before** spawning anything — it refuses a non-root cwd, refuses uncommitted
|
|
15
|
+
edits inside the worker's scope, claims the issue, records the brief.
|
|
16
|
+
**Everything the worker needs goes in the brief:** over 225 subagent runs a rule
|
|
17
|
+
pasted into the brief was followed 96% of the time, the same rule left in the
|
|
18
|
+
project instructions file 7%. Prompt position, not access.
|
|
19
|
+
**Leave when:** every worker has finished or stopped *with* a `HANDOFF.md`. A
|
|
20
|
+
stopped worker is fine; one with no handoff is a loss.
|
|
21
|
+
|
|
22
|
+
## 3. Verified
|
|
23
|
+
One verifier for the sprint. It gets every finished patch as a file and sweeps them
|
|
24
|
+
together. **Leave when:** `healthy` or `healthy with caveats` and the caveats are
|
|
25
|
+
understood. `not ready` goes back to stage 2.
|
|
26
|
+
|
|
27
|
+
## 4. Folded
|
|
28
|
+
The orchestrator, and only the orchestrator, merges. Per change: apply to `main`;
|
|
29
|
+
run `generate` and confirm **nothing moves**; for the risky ones **mutate one half
|
|
30
|
+
of the fix at a time** and check the failure set is the one that half owns —
|
|
31
|
+
replaying a worker's own revert only proves the test noticed *that* revert. Then
|
|
32
|
+
`handoff <id> --from <worktree> --verified "<what YOU ran>"` and commit.
|
|
33
|
+
**Leave when:** committed on `main` with a fragment, the issue `done`, suites green
|
|
34
|
+
**on the final tree** — not on each patch. Nothing is folded while work is HELD.
|
|
35
|
+
Because folding is serial, the verifier blessed a combination, not each merge: **a
|
|
36
|
+
conflict resolution is the orchestrator's own code and needs its own proof.**
|
|
37
|
+
|
|
38
|
+
## 5. Sprint report
|
|
39
|
+
Names the issues delivered and the issues opened, and gives the owner somewhere to
|
|
40
|
+
respond section by section. **A local HTML file in the project's docs directory** —
|
|
41
|
+
served by `bin/serve`, answered in the browser, saved back to disk; never published
|
|
42
|
+
to an external site, never loading from one. `bin/doclint` gates that plus
|
|
43
|
+
answerability. **Leave when:** the owner ticks the sprint closed — no code path.
|
|
44
|
+
|
|
45
|
+
## 6. Back to issues
|
|
46
|
+
Rulings become tracker issues. `remember --issue <id> "…"` puts a finding where the
|
|
47
|
+
next session reads it. A ruling living only in chat is lost; only in a doc, nearly.
|
|
48
|
+
|
|
49
|
+
## Standing rules
|
|
50
|
+
|
|
51
|
+
### The clean tree is the truth
|
|
52
|
+
A worker's worktree branches from committed HEAD; the orchestrator's checkout is
|
|
53
|
+
dirty. **When a worker reports a failure the orchestrator cannot reproduce, the
|
|
54
|
+
worker is right.** Dismissing that once hid a fixture set broken at HEAD.
|
|
55
|
+
|
|
56
|
+
- **A new rule names what enforces it** — wrapper, gate, refusal or CI job, or say
|
|
57
|
+
plainly that nothing does.
|
|
58
|
+
- **Work is held until the owner says merge.**
|
|
59
|
+
- **Never run the full build in a worker or verifier** — release commands rewrite
|
|
60
|
+
committed bookkeeping. Use `config.json`'s `suites`.
|
|
61
|
+
- **Symlink `worktree.linkPaths` in every worktree.** Unlinked, a runtime walks up
|
|
62
|
+
and resolves a *sibling worktree* — you test someone else's code and report it as
|
|
63
|
+
your own. Green, confident, wrong.
|
|
64
|
+
|
|
65
|
+
### Isolation is the runner's, and it can silently not happen
|
|
66
|
+
`isolation: worktree` branches the repo the **calling session's cwd** is in, not the
|
|
67
|
+
one the harness is vendored in. Orchestrate project A from a shell sitting in
|
|
68
|
+
project B and every worker gets a worktree of B, without the files it was sent for.
|
|
69
|
+
On 2026-08-30 the fallback from that put every agent in one shared checkout: they
|
|
70
|
+
could see each other's uncommitted edits, and one `git add -A` swept a live lane's
|
|
71
|
+
work into an unrelated commit.
|
|
72
|
+
|
|
73
|
+
**The worker's check, before it writes anything:** `git rev-parse --git-common-dir`
|
|
74
|
+
must contain the project's directory name; a bare relative `.git` means no worktree
|
|
75
|
+
at all. Not `--show-toplevel` — that prints the *worktree's* path, which never
|
|
76
|
+
equals the main repo's. `dispatch` pastes this into every brief.
|
|
77
|
+
|
|
78
|
+
**Orchestrating without isolation:** confirm cwd is the project root before every
|
|
79
|
+
dispatch, give concurrent agents strictly disjoint file scopes, and **never `git add
|
|
80
|
+
-A` while a lane is live** — stage explicit paths, every time.
|
|
81
|
+
|
|
82
|
+
**What enforces it: nothing.** `bin/dispatch` refuses a cwd that is not
|
|
83
|
+
`ENTROPY_MACHINES_ROOT`, which catches a session already drifted *when the brief is written*.
|
|
84
|
+
The runner creates the worktree later, from whatever the cwd is then, so a `cd` in
|
|
85
|
+
between is invisible to every gate — and no shell script can reach inside the
|
|
86
|
+
runner's isolation to check what it actually did. The worker's own check is all
|
|
87
|
+
that is downstream of it.
|
package/hooks/commit-msg
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Repo commit-msg hook — VERSION-CONTROLLED, like its pre-commit sibling.
|
|
3
|
+
#
|
|
4
|
+
# It exists separately from pre-commit because handoff-guard keys on the issue
|
|
5
|
+
# id in the commit message, and pre-commit runs before a message exists.
|
|
6
|
+
#
|
|
7
|
+
# ONE ROOT. lib/handoff-guard.sh is a harness file living one directory up from
|
|
8
|
+
# this one, inside the same repository as the commit being made. The shim
|
|
9
|
+
# exports ENTROPY_MACHINES_HOME (it lives in .git/hooks and cannot find lib/
|
|
10
|
+
# relatively); the fallback below only matters when this file is run directly.
|
|
11
|
+
# $1, the message file path git passes in, is already project-relative or
|
|
12
|
+
# absolute as git gives it — nothing here needs to touch it.
|
|
13
|
+
#
|
|
14
|
+
# Keep it fast. $1 is the path to the message file.
|
|
15
|
+
|
|
16
|
+
set -euo pipefail
|
|
17
|
+
|
|
18
|
+
. "$(dirname "$0")/../lib/roots.sh"
|
|
19
|
+
|
|
20
|
+
: "${ENTROPY_MACHINES_HOME:=$(entropy_machines_home "$0")}"
|
|
21
|
+
export ENTROPY_MACHINES_HOME
|
|
22
|
+
entropy_machines_require_root commit-msg
|
|
23
|
+
|
|
24
|
+
exec "$ENTROPY_MACHINES_HOME/lib/handoff-guard.sh" --message "$1"
|