dorfl 0.1.2 → 0.2.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/dist/advance-drivers.d.ts +1 -1
- package/dist/advance-drivers.d.ts.map +1 -1
- package/dist/advance-drivers.js +15 -3
- package/dist/advance-drivers.js.map +1 -1
- package/dist/advance-lifecycle-template.d.ts.map +1 -1
- package/dist/advance-lifecycle-template.js +76 -4
- package/dist/advance-lifecycle-template.js.map +1 -1
- package/dist/advance-treeless-publish.d.ts +23 -0
- package/dist/advance-treeless-publish.d.ts.map +1 -1
- package/dist/advance-treeless-publish.js +41 -0
- package/dist/advance-treeless-publish.js.map +1 -1
- package/dist/advance.d.ts +59 -7
- package/dist/advance.d.ts.map +1 -1
- package/dist/advance.js +389 -82
- package/dist/advance.js.map +1 -1
- package/dist/advancing-lock.d.ts +30 -2
- package/dist/advancing-lock.d.ts.map +1 -1
- package/dist/advancing-lock.js +50 -0
- package/dist/advancing-lock.js.map +1 -1
- package/dist/agent-launch.d.ts +12 -0
- package/dist/agent-launch.d.ts.map +1 -1
- package/dist/agent-launch.js +22 -12
- package/dist/agent-launch.js.map +1 -1
- package/dist/agent-stop.d.ts +40 -2
- package/dist/agent-stop.d.ts.map +1 -1
- package/dist/agent-stop.js +30 -2
- package/dist/agent-stop.js.map +1 -1
- package/dist/apply-decide.d.ts +17 -3
- package/dist/apply-decide.d.ts.map +1 -1
- package/dist/apply-decide.js +36 -7
- package/dist/apply-decide.js.map +1 -1
- package/dist/apply-merge-action.d.ts +17 -5
- package/dist/apply-merge-action.d.ts.map +1 -1
- package/dist/apply-merge-action.js +43 -8
- package/dist/apply-merge-action.js.map +1 -1
- package/dist/apply-persist.d.ts +57 -26
- package/dist/apply-persist.d.ts.map +1 -1
- package/dist/apply-persist.js +166 -37
- package/dist/apply-persist.js.map +1 -1
- package/dist/apply-stuck-action.d.ts +151 -0
- package/dist/apply-stuck-action.d.ts.map +1 -0
- package/dist/apply-stuck-action.js +125 -0
- package/dist/apply-stuck-action.js.map +1 -0
- package/dist/brand.d.ts +12 -1
- package/dist/brand.d.ts.map +1 -1
- package/dist/brand.js +2 -1
- package/dist/brand.js.map +1 -1
- package/dist/cli-spinner.d.ts +1 -1
- package/dist/cli-spinner.js +1 -1
- package/dist/cli.d.ts +10 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +320 -63
- package/dist/cli.js.map +1 -1
- package/dist/close-job-template.js +1 -1
- package/dist/complete.d.ts +6 -3
- package/dist/complete.d.ts.map +1 -1
- package/dist/complete.js +91 -37
- package/dist/complete.js.map +1 -1
- package/dist/config.d.ts +55 -9
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +56 -4
- package/dist/config.js.map +1 -1
- package/dist/continue-branch.d.ts.map +1 -1
- package/dist/continue-branch.js +22 -0
- package/dist/continue-branch.js.map +1 -1
- package/dist/decision-engine.d.ts +38 -10
- package/dist/decision-engine.d.ts.map +1 -1
- package/dist/decision-engine.js +10 -6
- package/dist/decision-engine.js.map +1 -1
- package/dist/do-autopick.d.ts.map +1 -1
- package/dist/do-autopick.js +11 -1
- package/dist/do-autopick.js.map +1 -1
- package/dist/do-config.d.ts +1 -1
- package/dist/do.d.ts +49 -56
- package/dist/do.d.ts.map +1 -1
- package/dist/do.js +419 -30
- package/dist/do.js.map +1 -1
- package/dist/env-config.d.ts.map +1 -1
- package/dist/env-config.js +12 -4
- package/dist/env-config.js.map +1 -1
- package/dist/failure-cause.d.ts +3 -2
- package/dist/failure-cause.d.ts.map +1 -1
- package/dist/failure-cause.js +28 -1
- package/dist/failure-cause.js.map +1 -1
- package/dist/format.d.ts +6 -6
- package/dist/format.d.ts.map +1 -1
- package/dist/format.js +10 -27
- package/dist/format.js.map +1 -1
- package/dist/frontmatter.d.ts +16 -4
- package/dist/frontmatter.d.ts.map +1 -1
- package/dist/frontmatter.js +27 -1
- package/dist/frontmatter.js.map +1 -1
- package/dist/gc.d.ts +65 -6
- package/dist/gc.d.ts.map +1 -1
- package/dist/gc.js +126 -9
- package/dist/gc.js.map +1 -1
- package/dist/github.d.ts +14 -0
- package/dist/github.d.ts.map +1 -1
- package/dist/github.js +73 -0
- package/dist/github.js.map +1 -1
- package/dist/harness.d.ts +25 -0
- package/dist/harness.d.ts.map +1 -1
- package/dist/harness.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/install-ci-branch-protection.d.ts +132 -38
- package/dist/install-ci-branch-protection.d.ts.map +1 -1
- package/dist/install-ci-branch-protection.js +190 -48
- package/dist/install-ci-branch-protection.js.map +1 -1
- package/dist/install-ci-core.d.ts +21 -1
- package/dist/install-ci-core.d.ts.map +1 -1
- package/dist/install-ci-core.js +10 -3
- package/dist/install-ci-core.js.map +1 -1
- package/dist/install-ci-github.d.ts +32 -0
- package/dist/install-ci-github.d.ts.map +1 -1
- package/dist/install-ci-github.js +73 -0
- package/dist/install-ci-github.js.map +1 -1
- package/dist/install-skills.d.ts +123 -0
- package/dist/install-skills.d.ts.map +1 -0
- package/dist/install-skills.js +100 -0
- package/dist/install-skills.js.map +1 -0
- package/dist/intake-trigger-template.js +1 -1
- package/dist/intake.d.ts +17 -4
- package/dist/intake.d.ts.map +1 -1
- package/dist/intake.js +23 -7
- package/dist/intake.js.map +1 -1
- package/dist/integration-core.d.ts +10 -7
- package/dist/integration-core.d.ts.map +1 -1
- package/dist/integration-core.js +174 -60
- package/dist/integration-core.js.map +1 -1
- package/dist/integrator.d.ts +1 -1
- package/dist/integrator.d.ts.map +1 -1
- package/dist/integrator.js +19 -3
- package/dist/integrator.js.map +1 -1
- package/dist/isolation.d.ts +2 -2
- package/dist/isolation.d.ts.map +1 -1
- package/dist/isolation.js +9 -0
- package/dist/isolation.js.map +1 -1
- package/dist/item-lock.d.ts +196 -129
- package/dist/item-lock.d.ts.map +1 -1
- package/dist/item-lock.js +329 -254
- package/dist/item-lock.js.map +1 -1
- package/dist/ledger-write.d.ts +31 -26
- package/dist/ledger-write.d.ts.map +1 -1
- package/dist/ledger-write.js +100 -122
- package/dist/ledger-write.js.map +1 -1
- package/dist/lifecycle-gather.d.ts +17 -0
- package/dist/lifecycle-gather.d.ts.map +1 -1
- package/dist/lifecycle-gather.js +7 -1
- package/dist/lifecycle-gather.js.map +1 -1
- package/dist/lifecycle-pools.d.ts +40 -3
- package/dist/lifecycle-pools.d.ts.map +1 -1
- package/dist/lifecycle-pools.js +26 -6
- package/dist/lifecycle-pools.js.map +1 -1
- package/dist/merge-question-surfacer.d.ts +15 -1
- package/dist/merge-question-surfacer.d.ts.map +1 -1
- package/dist/merge-question-surfacer.js +18 -3
- package/dist/merge-question-surfacer.js.map +1 -1
- package/dist/migrate-stuck-locks.d.ts +129 -0
- package/dist/migrate-stuck-locks.d.ts.map +1 -0
- package/dist/migrate-stuck-locks.js +355 -0
- package/dist/migrate-stuck-locks.js.map +1 -0
- package/dist/needs-attention.d.ts +293 -16
- package/dist/needs-attention.d.ts.map +1 -1
- package/dist/needs-attention.js +532 -57
- package/dist/needs-attention.js.map +1 -1
- package/dist/orphan-sidecar.d.ts +8 -4
- package/dist/orphan-sidecar.d.ts.map +1 -1
- package/dist/orphan-sidecar.js +35 -2
- package/dist/orphan-sidecar.js.map +1 -1
- package/dist/pi-harness.d.ts +16 -0
- package/dist/pi-harness.d.ts.map +1 -1
- package/dist/pi-harness.js +82 -2
- package/dist/pi-harness.js.map +1 -1
- package/dist/placement.d.ts +3 -3
- package/dist/placement.js +1 -1
- package/dist/prd-to-spec.d.ts.map +1 -1
- package/dist/prd-to-spec.js +9 -5
- package/dist/prd-to-spec.js.map +1 -1
- package/dist/protocol/CLAIM-PROTOCOL.md +17 -10
- package/dist/protocol/REVIEW-PROTOCOL.md +4 -1
- package/dist/protocol/SURFACE-PROTOCOL.md +16 -2
- package/dist/protocol/TASKING-PROTOCOL.md +3 -1
- package/dist/protocol/WORK-CONTRACT.md +22 -18
- package/dist/protocol/task-template.md +1 -1
- package/dist/reap-branches.d.ts +12 -9
- package/dist/reap-branches.d.ts.map +1 -1
- package/dist/reap-branches.js +25 -7
- package/dist/reap-branches.js.map +1 -1
- package/dist/recover-isolated.d.ts +12 -0
- package/dist/recover-isolated.d.ts.map +1 -1
- package/dist/recover-isolated.js +5 -0
- package/dist/recover-isolated.js.map +1 -1
- package/dist/repo-config.d.ts +23 -2
- package/dist/repo-config.d.ts.map +1 -1
- package/dist/repo-config.js +53 -7
- package/dist/repo-config.js.map +1 -1
- package/dist/repo-mirror.d.ts.map +1 -1
- package/dist/repo-mirror.js +18 -2
- package/dist/repo-mirror.js.map +1 -1
- package/dist/review-verdict.js +1 -1
- package/dist/review-verdict.js.map +1 -1
- package/dist/run.d.ts +1 -1
- package/dist/run.d.ts.map +1 -1
- package/dist/run.js +54 -19
- package/dist/run.js.map +1 -1
- package/dist/scan.d.ts +17 -8
- package/dist/scan.d.ts.map +1 -1
- package/dist/scan.js +22 -8
- package/dist/scan.js.map +1 -1
- package/dist/select-priority.d.ts +2 -2
- package/dist/select-priority.js +1 -1
- package/dist/select-priority.js.map +1 -1
- package/dist/sidecar-apply.js +1 -1
- package/dist/sidecar-apply.js.map +1 -1
- package/dist/sidecar.d.ts +45 -10
- package/dist/sidecar.d.ts.map +1 -1
- package/dist/sidecar.js +82 -2
- package/dist/sidecar.js.map +1 -1
- package/dist/skills/answer-questions/SKILL.md +89 -0
- package/dist/skills/capture-signal/SKILL.md +52 -0
- package/dist/skills/convert-from-prd-to-spec/SKILL.md +90 -0
- package/dist/skills/drive-tasks/SKILL.md +218 -0
- package/dist/skills/from-idea/SKILL.md +83 -0
- package/dist/skills/merge-prs/SKILL.md +70 -0
- package/dist/skills/orchestrate/SKILL.md +101 -0
- package/dist/skills/promote/SKILL.md +35 -0
- package/dist/skills/review/SKILL.md +16 -0
- package/dist/skills/setup/SKILL.md +258 -0
- package/dist/skills/setup/protocol/ADR-FORMAT.md +47 -0
- package/dist/skills/setup/protocol/CLAIM-PROTOCOL.md +224 -0
- package/dist/skills/setup/protocol/REVIEW-PROTOCOL.md +122 -0
- package/dist/skills/setup/protocol/SURFACE-PROTOCOL.md +135 -0
- package/dist/skills/setup/protocol/TASKING-PROTOCOL.md +124 -0
- package/dist/skills/setup/protocol/WORK-CONTRACT.md +280 -0
- package/dist/skills/setup/protocol/spec-template.md +71 -0
- package/dist/skills/setup/protocol/task-template.md +65 -0
- package/dist/skills/surface-questions/SKILL.md +16 -0
- package/dist/skills/to-spec/SKILL.md +34 -0
- package/dist/skills/to-task/SKILL.md +19 -0
- package/dist/skills/triage-observations/SKILL.md +78 -0
- package/dist/skills/work/SKILL.md +51 -0
- package/dist/slug-namespace.d.ts +5 -5
- package/dist/slug-namespace.js +5 -5
- package/dist/slug-namespace.js.map +1 -1
- package/dist/spec-complete.d.ts.map +1 -1
- package/dist/spec-complete.js +2 -3
- package/dist/spec-complete.js.map +1 -1
- package/dist/start.d.ts.map +1 -1
- package/dist/start.js +51 -57
- package/dist/start.js.map +1 -1
- package/dist/surface-gate.d.ts.map +1 -1
- package/dist/surface-gate.js +10 -3
- package/dist/surface-gate.js.map +1 -1
- package/dist/surface-persist.js +1 -1
- package/dist/surface-persist.js.map +1 -1
- package/dist/tasker-review-loop.d.ts +4 -3
- package/dist/tasker-review-loop.d.ts.map +1 -1
- package/dist/tasker-review-loop.js.map +1 -1
- package/dist/tasking-lock.d.ts +2 -2
- package/dist/tasking-lock.d.ts.map +1 -1
- package/dist/tasking-lock.js +53 -32
- package/dist/tasking-lock.js.map +1 -1
- package/dist/tasking.d.ts +40 -6
- package/dist/tasking.d.ts.map +1 -1
- package/dist/tasking.js +224 -12
- package/dist/tasking.js.map +1 -1
- package/dist/triage-persist.d.ts +10 -6
- package/dist/triage-persist.d.ts.map +1 -1
- package/dist/triage-persist.js +34 -4
- package/dist/triage-persist.js.map +1 -1
- package/dist/vendor/incur/agents.d.ts +58 -0
- package/dist/vendor/incur/agents.d.ts.map +1 -0
- package/dist/vendor/incur/agents.js +343 -0
- package/dist/vendor/incur/agents.js.map +1 -0
- package/dist/verify-workflow-template.js +1 -1
- package/dist/watch-session.d.ts +11 -3
- package/dist/watch-session.d.ts.map +1 -1
- package/dist/watch-session.js +94 -7
- package/dist/watch-session.js.map +1 -1
- package/dist/work-layout.d.ts +7 -3
- package/dist/work-layout.d.ts.map +1 -1
- package/dist/work-layout.js +6 -3
- package/dist/work-layout.js.map +1 -1
- package/dist/workspace.d.ts +1 -1
- package/package.json +2 -2
- package/src/advance-drivers.ts +16 -3
- package/src/advance-lifecycle-template.ts +89 -4
- package/src/advance-treeless-publish.ts +45 -0
- package/src/advance.ts +467 -87
- package/src/advancing-lock.ts +96 -1
- package/src/agent-launch.ts +37 -12
- package/src/agent-stop.ts +60 -2
- package/src/apply-decide.ts +36 -7
- package/src/apply-merge-action.ts +45 -7
- package/src/apply-persist.ts +228 -54
- package/src/apply-stuck-action.ts +260 -0
- package/src/brand.ts +14 -2
- package/src/cli-spinner.ts +1 -1
- package/src/cli.ts +401 -70
- package/src/close-job-template.ts +1 -1
- package/src/complete.ts +110 -52
- package/src/config.ts +107 -13
- package/src/continue-branch.ts +22 -0
- package/src/decision-engine.ts +55 -17
- package/src/do-autopick.ts +12 -1
- package/src/do-config.ts +1 -1
- package/src/do.ts +511 -38
- package/src/env-config.ts +12 -4
- package/src/failure-cause.ts +30 -1
- package/src/format.ts +10 -30
- package/src/frontmatter.ts +35 -5
- package/src/gc.ts +172 -9
- package/src/github.ts +78 -0
- package/src/harness.ts +25 -0
- package/src/index.ts +10 -0
- package/src/install-ci-branch-protection.ts +282 -57
- package/src/install-ci-core.ts +30 -3
- package/src/install-ci-github.ts +86 -0
- package/src/install-skills.ts +166 -0
- package/src/intake-trigger-template.ts +1 -1
- package/src/intake.ts +46 -11
- package/src/integration-core.ts +199 -65
- package/src/integrator.ts +22 -9
- package/src/isolation.ts +11 -2
- package/src/item-lock.ts +485 -341
- package/src/ledger-write.ts +135 -150
- package/src/lifecycle-gather.ts +29 -6
- package/src/lifecycle-pools.ts +67 -9
- package/src/merge-question-surfacer.ts +28 -4
- package/src/migrate-stuck-locks.ts +451 -0
- package/src/needs-attention.ts +857 -84
- package/src/orphan-sidecar.ts +47 -6
- package/src/pi-harness.ts +82 -2
- package/src/placement.ts +3 -3
- package/src/prd-to-spec.ts +13 -5
- package/src/reap-branches.ts +38 -14
- package/src/recover-isolated.ts +17 -0
- package/src/repo-config.ts +55 -6
- package/src/repo-mirror.ts +23 -1
- package/src/review-verdict.ts +1 -1
- package/src/run.ts +64 -18
- package/src/scan.ts +22 -8
- package/src/select-priority.ts +3 -3
- package/src/sidecar-apply.ts +1 -1
- package/src/sidecar.ts +138 -11
- package/src/slug-namespace.ts +7 -7
- package/src/spec-complete.ts +2 -3
- package/src/start.ts +53 -59
- package/src/surface-gate.ts +10 -3
- package/src/surface-persist.ts +1 -1
- package/src/tasker-review-loop.ts +4 -3
- package/src/tasking-lock.ts +52 -36
- package/src/tasking.ts +261 -17
- package/src/triage-persist.ts +56 -12
- package/src/vendor/incur/LICENSE +21 -0
- package/src/vendor/incur/README.md +19 -0
- package/src/vendor/incur/agents.ts +392 -0
- package/src/verify-workflow-template.ts +1 -1
- package/src/watch-session.ts +102 -7
- package/src/work-layout.ts +6 -3
- package/src/workspace.ts +1 -1
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: setup
|
|
3
|
+
disable-model-invocation: true
|
|
4
|
+
description: 'Onboard a repo onto the file-based work/ contract: scaffold an empty repo, or also migrate existing trackers/docs/code in a populated one. The single adoption entry point.'
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# setup
|
|
8
|
+
|
|
9
|
+
The **single skill to onboard a repo** onto the **`work/` contract** — the runner-agnostic, file-based protocol (defined in `protocol/WORK-CONTRACT.md`, which setup OWNS and copies into each target repo's `work/protocol/`) that `dorfl` consumes. This is the **adopt-the-contract** step (a SKILL, not a command — adoption must NOT require installing `dorfl`; the contract is the protocol, the runner is one consumer).
|
|
10
|
+
|
|
11
|
+
**One skill, two phases, auto-detected depth.** setup always does **Phase A — Scaffold** (deterministic; the `work/` skeleton, the protocol docs, `CONTEXT.md`, the `dorfl.json` gate). If it then detects **existing material to convert** (a task tracker, design docs, substantial source), it ALSO does **Phase B — Convert** (judgement-heavy; map that material onto the contract's buckets, hunt decisions, elicit ADRs). On an empty/near-empty repo, Phase B is simply empty and setup finishes after Phase A. The user never has to choose "scaffold vs migrate" — setup figures out the depth.
|
|
12
|
+
|
|
13
|
+
> **Where the contract docs live:** setup copies the protocol reference docs into the target repo's **`work/protocol/`** (`WORK-CONTRACT.md`, `ADR-FORMAT.md`, `task-template.md`, `spec-template.md`, `CLAIM-PROTOCOL.md`, `REVIEW-PROTOCOL.md`). Every bare "WORK-CONTRACT" / "ADR-FORMAT" mention below refers to `work/protocol/<doc>` in the repo you are setting up — read them there, not from a sibling skill folder.
|
|
14
|
+
|
|
15
|
+
## The unified shape: detect → PLAN → **STOP for confirmation** → write
|
|
16
|
+
|
|
17
|
+
Whatever the depth, setup follows the same arc, and the **plan-then-confirm step is a HARD STOP, not a narrated aside**:
|
|
18
|
+
|
|
19
|
+
1. **Inventory** the repo (read-only).
|
|
20
|
+
2. **Present a plan** — the proposed description + detected `verify` gate, AND (if material exists) the inventory→bucket mapping table.
|
|
21
|
+
3. **STOP and wait for the user to confirm/correct.** Do NOT proceed to write the judgement-heavy parts in the same turn you present the plan. Showing the plan and continuing is the single most common failure — the user must get a turn to ratify the mapping and the gate before anything is converted. (Phase-A scaffolding of the deterministic skeleton may proceed once the description+gate are confirmed; Phase-B conversion waits for the mapping to be confirmed.)
|
|
22
|
+
4. **Write** (create-only, never clobber), run the gate once, report, hand off.
|
|
23
|
+
|
|
24
|
+
## The one discipline that matters most (Phase B): an ADR is written IFF we have the WHY — else write nothing; the code is the pre-ADR
|
|
25
|
+
|
|
26
|
+
An **ADR** (`docs/adr/`) records a **DECISION + its why** — the rejected options, the constraints, the reasoning. The _why_ is **usually NOT recoverable from code or docs**; the _what_ (the decision the code embodies) often IS. Reverse-engineering a codebase yields **description / ground-truth**, not a rationale. So the rule is simple and absolute:
|
|
27
|
+
|
|
28
|
+
- **Write an ADR if and ONLY if you have a complete, clear decision — the _what_ AND a real _why_.** The only legitimate source of the why is the **human**.
|
|
29
|
+
- When you spot something that LOOKS like a decision (a design doc or the code reveals "we chose X over Y") AND it is **ADR-worthy** — all three of _hard to reverse_ AND _surprising without context_ (a future reader would wonder "why on earth this way?") AND _the result of a real trade-off_ (there were genuine alternatives) — **ASK the user, during the run: "Why was this done? What were the alternatives and constraints?"** If they answer, write a **complete ADR in `docs/adr/`** from THAT answer (`work/protocol/ADR-FORMAT.md` shape: `NNNN-slug.md`, one decision per file, 1–3 sentences of context/decision/why). The why came from the human, so the record is honest.
|
|
30
|
+
- **If the user is absent, declines, or does not know the why — write NOTHING.** No `proposed`/pre-ADR, no `docs/adr/candidates/` folder, no holding doc, no candidate-flag, no question-list file. The **code itself remains the pre-ADR**: it already embodies the decision, is always current, and re-surfaces the same "why is this so?" question to the next reader who cares — who can then ask a human and record the answer as an ADR. A stored "open question" artifact nobody maintains would just rot; the absence of one is the feature.
|
|
31
|
+
- **Non-negotiable.** NEVER write an ADR whose _why_ you inferred from code — a fabricated rationale corrupts the decision record silently. No why from a human → no ADR.
|
|
32
|
+
|
|
33
|
+
## Phase A — Scaffold (always)
|
|
34
|
+
|
|
35
|
+
### A1. Detect the repo state (works empty OR populated — NEVER clobber)
|
|
36
|
+
|
|
37
|
+
`ls`/glob the repo first. For EACH artifact setup would write, if it ALREADY exists, do NOT overwrite — report it and leave it (or, for `dorfl.json` / `CONTEXT.md`, offer to MERGE-in only the missing keys/sections). Only CREATE what is missing. This is what makes setup safe to run on a populated repo and idempotent to re-run.
|
|
38
|
+
|
|
39
|
+
- If `work/` already has the folders → skip them. **If `work/` is on the LEGACY FLAT layout** (top-level `backlog/` / `prd/` / `observations/` / … instead of the `notes/` / `tasks/` / `specs/` umbrellas) → do NOT silently scaffold a second, parallel tree beside it; surface it and offer the documented migration mapping (see A5b).
|
|
40
|
+
- **`work/protocol/` is the ONE exception to never-clobber — it is protocol-owned, not repo-owned.** The repo's `work/` _items_ (tasks/specs/notes) are sacred and never touched; but the `work/protocol/` reference docs are verbatim copies setup owns, so re-running setup **re-syncs** them (overwrite with the current canonical copies + bump `work/protocol/VERSION`). This is how a repo picks up protocol updates. Never hand-edit `work/protocol/<doc>` in a target repo — edits belong in setup's canonical `protocol/` source and propagate via re-sync.
|
|
41
|
+
- If `CONTEXT.md` exists → do not overwrite; offer to APPEND a "domain terms" section if absent, else leave it.
|
|
42
|
+
- If `dorfl.json` exists → do not overwrite; report its `verify`/`harness` and offer to fill only ABSENT keys.
|
|
43
|
+
- **Detect Phase B material — do NOT skip dotfolders that might hold meaningful content.** While inventorying, note any **convertible material**: a `tasks/` folder, a `TODO.md`, an issue-tracker export, `docs/` design notes / RFCs, plan files, and **substantial source code** (code that embodies decisions). A plain `ls` of the visible top level is NOT enough: a hidden (dot-prefixed) folder can hold real sources (design docs, plans, tasks, notes), and a missed source is silently under-routed (a rich design doc that should be a spec never gets seen). So look inside dotfolders too, and judge each by whether it _could plausibly contain documents/plans/notes_ worth converting. **Skip only the noise** — `.git/`, `node_modules/` and other dependency dirs, build output, and anything `.gitignore`d are never sources. This detection decides whether Phase B runs (see A4 / Phase B). A repo with only a README that says "clean slate" (or nothing) → Phase B is empty; finish after Phase A.
|
|
44
|
+
|
|
45
|
+
### A2. The adoption conversation (seed CONTEXT.md — keep it short)
|
|
46
|
+
|
|
47
|
+
Derive the **project name** from the repo (folder/remote name) for the CONTEXT title.
|
|
48
|
+
|
|
49
|
+
- **Empty/near-empty repo (no material to read):** you have nothing to propose, so ASK: **"What is this repo about? (one or two sentences — or skip and I'll scaffold a stub you fill in later.)"** If they describe it, put it in `CONTEXT.md`'s "What <repo> is" and ask 1–3 **refining** questions to seed the glossary (core domain nouns, actors, what it integrates with). If they say nothing/"skip", scaffold `CONTEXT.md` with a `<!-- TODO: describe the project -->` stub + placeholder glossary entries — don't block; the repo is still fully set up. (If the user is here to start a project FROM a raw idea, the `from-idea` on-ramp is the front door that runs setup AND lands the idea as a spec — you may MENTION it as the next step, but setup never invokes it; the orchestration lives there, setup stays the focused adoption primitive.)
|
|
50
|
+
- **Populated repo (you read it in A1):** do NOT cold-open. Form a **proposed one-to-two-sentence description** (+ core domain nouns) from the README/docs/code, and CONFIRM it: **"Here is what I think this repo is: <proposed description>. Correct/refine, or accept?"** The human still ratifies; you just spare them a blank prompt. (This confirmation is part of the PLAN you present at A4.)
|
|
51
|
+
|
|
52
|
+
**Nudge for a per-change convention (language-agnostic — never tool-specific).** Many repos require something extra on every change: a changeset, a `CHANGELOG` entry, a news fragment, etc. setup does NOT detect or assume any of these — there is no generic signal and guessing one (e.g. keying off `.changeset/`) would smuggle ecosystem favouritism into a deliberately language-agnostic skill (the A3 rule). Instead, ASK once, generically: **"Any standing per-change rule agents must follow in this repo — e.g. a changeset, a CHANGELOG entry, a news fragment? I'll note it under `## Conventions` in CONTEXT.md."** If they give one, record it in the CONTEXT.md `## Conventions` section (fold this into the A4 plan, do NOT make it a separate question round); if they skip, leave the commented stub. Mention the homes a convention can live in: **CONTEXT.md** (the in-band slot agents read), **their own agent config** (e.g. an `AGENTS.md` their harness reads), and — if they want it _enforced_ rather than merely stated — **their own check wired into the `verify` gate** (their command, e.g. `changeset status --since=main`; setup never injects one, per A3 — it only points out that `verify` is where enforcement would go).
|
|
53
|
+
|
|
54
|
+
**Nudge for `promptGuidance.testFirst` (a prompt nudge, NOT a gate — fold into the A4 plan, do NOT add a separate question round).** `promptGuidance` is a per-repo NUDGE namespace in `dorfl.json` that strengthens the worker's in-band prompt without changing what the `verify` gate accepts; its first member is `testFirst`. ASK once, phrased AS a nudge so it is obvious this is guidance and not an acceptance bar: **"Should autonomous builds in this repo default to writing the failing test BEFORE the production code? Your `verify` gate still decides pass/fail either way — this just strengthens the wording the worker is given (`promptGuidance.testFirst` in `dorfl.json`; a NUDGE, not a gate)."** On **yes** → MERGE-IN `promptGuidance: { testFirst: true }` into `dorfl.json` per A1's merge-don't-clobber rule (preserve every other existing key VERBATIM; if `promptGuidance` already exists, add/set only `testFirst` and leave any sibling members in place). On **no / skip / don't know / absent user** → write NOTHING for this key (the runtime default is `false`, so omitting it IS the negative answer; do not write `testFirst: false`, do not create the `promptGuidance` object just to leave it empty). AGENTS.md is **never** written or modified by this nudge (host-owned — see the Boundary section); the load-bearing channel is the in-band prompt, which the runner already strengthens from `CLAIM-PROTOCOL.md` when the resolved value is `true`.
|
|
55
|
+
|
|
56
|
+
### A3. Discover the real `verify` gate FROM THE REPO (detect, never assume)
|
|
57
|
+
|
|
58
|
+
The `dorfl.json` `verify` gate is the protocol's per-project, **language-agnostic** acceptance gate (build + test + format/lint, all green). The single rule: **discover the gate from THIS repo; never write a canned, stack-shaped guess.**
|
|
59
|
+
|
|
60
|
+
**Two shape rules for the gate you write (independent of which stack):**
|
|
61
|
+
|
|
62
|
+
- **Cheapest checks FIRST, for fail-fast.** Order the gate so the quick, deterministic checks run before the expensive ones: **format/lint → typecheck/build → test**. A formatting nit should fail in seconds, not after a full build+test. So prefer `pnpm format:check && pnpm build && pnpm test` over `… && pnpm test && pnpm format:check`.
|
|
63
|
+
- **The gate is ACCEPTANCE, not environment-prep.** Do NOT bake dependency install / submodule fetch / codegen into `verify` (e.g. do not write `pnpm install --ignore-scripts && …` even if CI does it as a separate step). `verify` answers "is the working tree green?", assuming deps are already present. **Install now has a sanctioned home: the sibling `prepare` field** (see A3b) — the runner runs `prepare` ONCE before the first `verify` on a fresh worktree. So **MOVE any install/bootstrap prefix CI wraps around its build/test steps into `prepare`, do not delete it**: keep `verify` to the build/test/lint commands themselves, and put the install/submodule/codegen step in `prepare`. The clean split is **`prepare` = env-ready, `verify` = tree-green.**
|
|
64
|
+
|
|
65
|
+
The built-in fallback happens to be Node-shaped, which is SILENTLY WRONG for any other stack — so do not rely on it, and do not swing the other way and template a language's "usual" command blind. The protocol names no toolchain; neither should the gate you write. Find what THIS repo actually uses, in this order of reliability:
|
|
66
|
+
|
|
67
|
+
1. **CI is the most reliable source — read it first.** `.github/workflows/*.yml` (or other CI config): the build/test/lint/fmt steps it runs ARE the project's real acceptance commands. (A workflow running `cargo build` then `cargo test` tells you the gate directly — even if the repo also has a `package.json`.)
|
|
68
|
+
2. **Then the project's own task runner / declared scripts.** Read the actual commands, do not assume their shape: a `package.json` `scripts` block (use the REAL `build`/`test`/`format:check` scripts as written — they may wrap a monorepo tool or an env loader, so a blind per-package flag would BYPASS them), a `Makefile`/`justfile`/`Taskfile`, `pyproject.toml` `[tool.*]` / tox, a `composer.json`, etc.
|
|
69
|
+
3. **Only then infer from the manifest — as a HINT to confirm, not an answer to write.** A manifest tells you the ecosystem, not the gate. `Cargo.toml` ⇒ likely `cargo build && cargo test && cargo fmt --check`; `go.mod` ⇒ `go build ./... && go test ./... && gofmt -l .`; a Node manifest ⇒ read its scripts (do NOT assume a workspace/recursive flag). Treat these as starting guesses to verify against (1)/(2), never as the final command.
|
|
70
|
+
|
|
71
|
+
**Multiple manifests is normal — pick the gate, not the first file that matched.** Many repos carry several (e.g. a Rust crate that also ships an npm install-wrapper `package.json` with no build/test scripts; a Solidity repo with `foundry.toml` + a Node manifest + nested crates). Do NOT key off "a manifest exists"; identify the PRIMARY build/test toolchain (CI usually settles it) and, for a genuinely mixed repo, compose the gate (e.g. `forge build && forge test && cargo test && …`). A wrapper manifest with no real scripts is a decoy — ignore it.
|
|
72
|
+
|
|
73
|
+
If you cannot determine it, **leave `verify` with a `TODO` comment and ASK the user** for the exact build/test/lint command — never invent one. The final gate is part of the PLAN you present at A4 and must be CONFIRMED (one line) before you write it: a wrong `verify` gate is the one scaffolding mistake that bites later.
|
|
74
|
+
|
|
75
|
+
### A3b. Detect (or ASK for) the `prepare` env-prep step — the sibling of `verify`
|
|
76
|
+
|
|
77
|
+
`prepare` is the protocol's per-repo **env-prep / install** step: the runner runs it ONCE before the FIRST `verify` on a freshly-materialised worktree (a fresh clone/worktree off the hub mirror has no `node_modules` / submodules / generated code, so `verify` would fail for lack of deps). It is **deliberately distinct from — and NOT baked into — `verify`** (`prepare` = env-ready; `verify` = tree-green), so the gate stays a pure, cheaply-re-runnable acceptance check. It is the home the "strip install from `verify`" rule (A3) points install AT.
|
|
78
|
+
|
|
79
|
+
**DETECT a likely `prepare` from THIS repo (same detect-never-assume discipline as A3), then SET it or ASK to confirm:**
|
|
80
|
+
|
|
81
|
+
- **A lockfile ⇒ the matching install.** `pnpm-lock.yaml` ⇒ `pnpm install`; `package-lock.json` ⇒ `npm ci`; `yarn.lock` ⇒ `yarn install --frozen-lockfile`; `Cargo.lock` is fetched by the build (often no separate step); `go.sum` ⇒ usually none (the build fetches). Read the REAL install CI uses (the install prefix you stripped from `verify` in A3 is exactly what belongs here).
|
|
82
|
+
- **Submodules ⇒ `git submodule update --init --recursive`** (if `.gitmodules` exists).
|
|
83
|
+
- **A codegen step ⇒ include it** (e.g. a `prebuild`/`codegen` script CI runs before build).
|
|
84
|
+
- **Compose** when more than one applies (e.g. `git submodule update --init && pnpm install`), as an ordered list or a `&&` chain (all must pass), exactly like `verify`.
|
|
85
|
+
|
|
86
|
+
**Unset is a valid answer — do NOT invent a default.** A repo with no deps to install needs NO `prepare`: leave the field absent (unset ⇒ a no-op; the runner installs nothing). Never write a default `pnpm install` into a repo that has no lockfile. If you detect a likely command, propose it in the A4 plan for one-line confirmation; if you are unsure whether the repo needs env-prep at all, ASK rather than guess. `prepare` is part of the PLAN you present at A4 alongside `verify`.
|
|
87
|
+
|
|
88
|
+
### A4. Present the PLAN and STOP for confirmation (the hard checkpoint)
|
|
89
|
+
|
|
90
|
+
Present, in one message:
|
|
91
|
+
|
|
92
|
+
- the **proposed description** (A2) — "correct/refine or accept?";
|
|
93
|
+
- the **detected `verify` gate** (A3) — "confirm?";
|
|
94
|
+
- the **detected `prepare` env-prep step** (A3b), if any — "confirm?" (or "none needed?" when the repo has no deps to install);
|
|
95
|
+
- the **`promptGuidance.testFirst` nudge** (A2) — "default to test-first autonomous builds in this repo? (NUDGE, not a gate — `verify` still decides pass/fail.)" Default = no (omit the key);
|
|
96
|
+
- **IF Phase-B material was detected (A1): the inventory → bucket mapping table** (see B1) — "is this routing right?".
|
|
97
|
+
|
|
98
|
+
Then **STOP and WAIT for the user's reply.** Do not write `CONTEXT.md`/`dorfl.json` with an unconfirmed description/gate, and do not start Phase-B conversion, until they answer. (You MAY create the deterministic skeleton — empty `work/` folders + `work/protocol/` copies — without waiting, since those are content-free; but anything carrying judgement waits.) Narrating "I'll show the plan first" and then barrelling ahead in the same turn defeats the checkpoint — the STOP is real.
|
|
99
|
+
|
|
100
|
+
### A5. Write the scaffold (create-only) + run the gate once
|
|
101
|
+
|
|
102
|
+
Once description + gate are confirmed: create the missing `work/` folders in the **regime-umbrella layout** — NOT a legacy flat tree. Create the folders setup actually populates (the capture buckets it writes notes into, the staging/pool folders, `questions/`, `protocol/`); the empty TERMINAL/status sub-folders (`tasks/done`, `tasks/cancelled`, `specs/tasked`, `specs/dropped`) do NOT need eager creation with a `.gitkeep` — they are created LAZILY on first use (whoever first `git mv`s an item into them makes the dir; `git mv` auto-creates the destination, and the runner `mkdir`s on demand where it writes). An absent empty status folder is NOT a broken contract. The full intended tree, for reference:
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
work/
|
|
106
|
+
notes/{observations,ideas,findings}/ # capture buckets (do not flow; leave by deletion)
|
|
107
|
+
tasks/{backlog,ready,done,cancelled}/ # the build board: backlog=staging, ready=pool, done, cancelled=terminal
|
|
108
|
+
specs/{proposed,ready,tasked,dropped}/ # spec lifecycle: proposed=staging, ready=pool, tasked, dropped=terminal
|
|
109
|
+
questions/ # the "what needs me?" queue (TOP-LEVEL, not under notes/)
|
|
110
|
+
protocol/ # the propagated protocol docs (copied below)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
The two won't-proceed terminals are deliberately named DIFFERENTLY per regime (`tasks/cancelled/` vs `specs/dropped/`) — a slug-collision correctness fix (a dropped task and a dropped spec sharing a slug must not collide on one bare-slug path); see `work/protocol/WORK-CONTRACT.md`. Then **copy the protocol docs verbatim into `work/protocol/`** from this skill's `protocol/` directory (`WORK-CONTRACT.md`, `CLAIM-PROTOCOL.md`, `REVIEW-PROTOCOL.md`, `task-template.md`, `spec-template.md`, `ADR-FORMAT.md`) and write `work/protocol/VERSION` — creating them if absent, RE-SYNCING (overwriting) them if present (protocol-owned, per A1); write `CONTEXT.md` and `dorfl.json` if absent (or merge-in missing keys per A1). NOTE: `dorfl.json` (plain, non-dotfile) is the name setup CREATES and dorfl PREFERS; a repo that already committed the legacy `.dorfl.json` (dotfile) is still honoured on READ (dorfl falls back to it), so on a re-run against a legacy repo, MERGE-into the existing `.dorfl.json` in place rather than creating a second `dorfl.json` (do not end up with both).
|
|
114
|
+
|
|
115
|
+
**Run the gate ONCE and report (catch a wrong `verify` immediately).** After writing `dorfl.json`, actually EXECUTE the `verify` command once and report green/red — the cheapest moment to discover the gate is wrong (a typo, a missing script, deps not installed), instead of at first build. If red, say WHY (e.g. "`format:check` failed — run `format` first" / "`build` needs deps installed") and offer to adjust the gate or note the prep step; do NOT silently leave a red gate. (Deps clearly not installed = the env-prep gap — that is what the `prepare` field is FOR: put the install in `prepare` (A3b), do NOT contort `verify` to hide it.)
|
|
116
|
+
|
|
117
|
+
If **no Phase-B material** was detected, skip to **Report + hand off**. Otherwise continue to Phase B.
|
|
118
|
+
|
|
119
|
+
### A5b. Migrating a repo already on the LEGACY FLAT layout (old → new `git mv` mapping)
|
|
120
|
+
|
|
121
|
+
A repo set up under an earlier protocol version has a FLAT `work/` (top-level `backlog/`, `prd/`, `observations/`, …). Do NOT scaffold a second tree beside it — MIGRATE it with `git mv` (renames preserve history). The mapping is documented and mechanical:
|
|
122
|
+
|
|
123
|
+
```
|
|
124
|
+
work/observations/ -> work/notes/observations/
|
|
125
|
+
work/ideas/ -> work/notes/ideas/
|
|
126
|
+
work/findings/ -> work/notes/findings/
|
|
127
|
+
work/pre-backlog/ -> work/tasks/backlog/ # staging takes the freed name
|
|
128
|
+
work/backlog/ -> work/tasks/ready/ # the pool keeps being the pool, new name
|
|
129
|
+
work/in-progress/ -> (GONE: transient status is now a per-item lock ref, not a folder)
|
|
130
|
+
work/done/ -> work/tasks/done/
|
|
131
|
+
work/pre-prd/ -> work/specs/proposed/
|
|
132
|
+
work/prd/ -> work/specs/ready/
|
|
133
|
+
work/prd-tasked/ -> work/specs/tasked/
|
|
134
|
+
work/questions/ -> work/questions/ # unchanged (stays top-level)
|
|
135
|
+
work/protocol/ -> work/protocol/ # re-synced (overwritten), not moved
|
|
136
|
+
# The old SHARED terminal work/dropped/ is SPLIT per regime (the slug-collision fix):
|
|
137
|
+
# a dropped TASK -> work/tasks/cancelled/<slug>.md
|
|
138
|
+
# a dropped SPEC -> work/specs/dropped/<slug>.md
|
|
139
|
+
# a dropped OBSERVATION needs no terminal (notes leave by deletion) -> git rm
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Do the moves with `git mv` so each rename is staged as a rename; sort the old `work/dropped/` contents by what each item IS (task vs spec vs observation) into the right destination. Present this mapping and STOP for confirmation (it touches the repo's live ledger); never auto-run it.
|
|
143
|
+
|
|
144
|
+
## Phase B — Convert existing material (only when material was detected)
|
|
145
|
+
|
|
146
|
+
Phase B fills the scaffolded buckets from what already exists. COMPOSE the producer skills — do NOT reimplement tasking/spec-writing (`to-spec`/`to-task` own those shapes). The mapping table was already presented and CONFIRMED at A4; now execute it.
|
|
147
|
+
|
|
148
|
+
### B1. Inventory → mapping (the table presented at A4)
|
|
149
|
+
|
|
150
|
+
Classify each source by what it should BECOME (this is the table you showed at A4):
|
|
151
|
+
|
|
152
|
+
- **A task/issue system** — `tasks/`, `TODO.md`, an issue export, GitHub issues. Route each by _how buildable it actually is_, NOT by default to a task (the common mistake — see the buildability gate in B2):
|
|
153
|
+
- a **scoped, buildable** ask → a **task** (one ask, one task);
|
|
154
|
+
- a **coherent ask needing >1 task** (a shared vision) → a **spec**;
|
|
155
|
+
- a **wish / "maybe" / blocked-on-external / one-line sketch** → an **idea**, NOT a task. A vague TODO line is an `idea`, not a `needsAnswers` task.
|
|
156
|
+
- **Design docs / RFCs / architecture notes** — is it a north-star plan (→ **spec** or **idea**), a decision-with-rationale (→ if it carries a real _why_, it may seed an **ADR**; if the _why_ is absent, ASK the user — answered → ADR, else write nothing), or a description of how something works (→ split by polarity: _external_ behaviour → **finding** with a `source:`; _our own_ code's shape → **`CONTEXT.md` / `docs/`**, never `findings/`)?
|
|
157
|
+
- **READMEs / wikis / inline docs** — domain vocabulary → seed/extend `CONTEXT.md`'s glossary; how-our-code-works → **`CONTEXT.md` / `docs/`**; how-an-external-thing-works → **finding** (with `source:`).
|
|
158
|
+
- **The source code itself** — B3 (generate understanding **and hunt decisions**): description of OUR code → **`CONTEXT.md` / `docs/`**; _external behaviour our code integrates with_ → a **finding** (with `source:`); AND — separately and always — the **deliberate decisions** the code embodies → B3b actively hunts these and asks the user (→ ADRs). Do not forget the decision hunt; it is the highest-value output and the easiest to skip.
|
|
159
|
+
- **Loose "we should…" / "known issue" notes** → **ideas** (proposed) or **observations** (spotted, unverified).
|
|
160
|
+
|
|
161
|
+
**CONVERGE sources that describe the SAME thing into ONE item — do not create one item per source.** Two different sources often point at the same feature (e.g. a one-line entry in a task list AND a detailed design doc elsewhere are the same ask at two fidelities). When that happens, produce a **single** item at the richest appropriate fidelity (here, the design doc → a spec) and route the thinner source INTO it — do NOT also emit a separate idea/task for the thin source that merely re-describes the same feature. One feature → one item. (A cross-reference stub that says "see the spec" is still a redundant second item — fold the thin source's content into the one item instead.) The same applies across buckets: do not let one decision become both an ADR and an observation, or one ask become both a spec and an idea.
|
|
162
|
+
|
|
163
|
+
### B2. Convert the task/work system → specs + tasks + ideas
|
|
164
|
+
|
|
165
|
+
- A single, clear, buildable ask → a **`work/tasks/backlog/<slug>.md`** task (`to-task`' task shape; `covers: []`, no `spec:` — its own source of truth).
|
|
166
|
+
- A coherent ask needing >1 task → a **`work/specs/ready/<slug>.md`** spec (`to-spec`'s framing). Task it only if asked; usually leave the spec for the human to task.
|
|
167
|
+
- **Buildability gate — decide task-vs-idea BEFORE writing anything to the task board.** A `tasks/backlog/` task is for a **scoped, buildable** ask. `needsAnswers: true` is for a **near-complete spec with a few SPECIFIC open questions listed in the body** — _almost a task_, not a wish. A vague one-liner is NEITHER a task nor a `needsAnswers` task — it is an **`idea`**. The contract is explicit: _under-specified items should not be written into the task board until they are ready_. **When in doubt → `notes/ideas/`, not `tasks/backlog/`.** Do NOT manufacture a `needsAnswers` task to "capture" a wish.
|
|
168
|
+
- **Set the two gate axes honestly** (WORK-CONTRACT §3b), once the item really is a task: `humanOnly: true` where building needs human judgement/security; `needsAnswers: true` where the spec is _near-complete but has specific listed open questions_ (NOT merely vague — that is an `idea`).
|
|
169
|
+
- **Task ↔ spec link:** a self-contained task (chore/refactor/build-fix, no spec stories) carries `covers: []` and **omits `spec:`**. A task pointing into a spec's stories MUST set `covers: [...]` AND name that spec in `spec:` (`spec` required iff `covers` non-empty). Do not invent a `spec:` for a task deriving from no spec.
|
|
170
|
+
- Content-derived slugs, never counters. Preserve any traceability (e.g. an `issue: N` link on a spec).
|
|
171
|
+
|
|
172
|
+
### B3. Understand the code — split by POLARITY, AND actively hunt decisions
|
|
173
|
+
|
|
174
|
+
TWO obligations, easy to conflate. Do NOT collapse this into "process the how-it-works material I happen to have" (e.g. turning a review doc into observations and stopping) — that silently skips the decision hunt, the single highest-value output. Do BOTH:
|
|
175
|
+
|
|
176
|
+
**(B3a) Route understanding by POLARITY** — because describing our OWN code in `findings/` corrupts the bucket (findings = _external_ ground-truth, and our code changes so it would rot with no status-flow to retire it):
|
|
177
|
+
|
|
178
|
+
- **Our own code's shape** (package layout, module seams, internal conventions, dependency flow) → **`CONTEXT.md`** (vocabulary) and/or a **`docs/architecture.md`** overview. NOT a finding. The code is its own current-truth source.
|
|
179
|
+
- **External/domain ground-truth our code integrates with** (a third-party API's real behaviour, a wire/artifact format, an EIP/spec, an external tool's contract) → a **`work/notes/findings/<slug>.md`**, which **MUST carry a `source:` (provenance)** (WORK-CONTRACT findings box + frontmatter): how, and how _currently_, you came to believe it. A finding _derived from reading our own code_ records `source:` = that file (+ commit) and is the **weakest** provenance — SAY so ("derived from <file> @ <commit>") so a later "our code was buggy" can revise it. Prefer upgrading to a dated external authority or a captured trace. (No separate `confidence:` field — a rich, dated `source:` carries the weight.)
|
|
180
|
+
- Keep verified ground-truth in `findings/` (with `source:`); keep speculation / spotted-but-unverified concerns in `observations/`.
|
|
181
|
+
|
|
182
|
+
**(B3b) HUNT for ADR-worthy decisions — a REQUIRED, distinct sub-pass, not a side effect of B3a.** Deliberately READ the primary source (and design docs / comments) _looking for choices_, not just structure. The fact that a piece of code IS a deliberate choice almost never announces itself; ask of the code, repeatedly: **"is this the way it is because someone DECIDED it, against a real alternative, for a reason a future reader couldn't reconstruct?"** Apply the bar (hard to reverse + surprising without context + a real trade-off). Candidates are wherever the code does something a competent reader would NOT assume by default and would later wonder "why this way?" — anything intentional-but-unexplained is a candidate; do not pre-filter to a known shape.
|
|
183
|
+
|
|
184
|
+
- For each candidate clearing the bar: **ASK the user the _why_**. If they answer → write a complete ADR in `docs/adr/`. If absent/unsure/decline → **write NOTHING** (the discipline above). Never author an ADR whose why you inferred.
|
|
185
|
+
- This sub-pass MUST run whenever Phase B touches source code. If you scanned and genuinely found no ADR-worthy decision, that is a fine outcome — but you must have _actively looked_ and be able to say what you scanned (B-report checkpoint). "I produced no ADRs" is only acceptable after a real hunt, never as a side effect of skipping it.
|
|
186
|
+
|
|
187
|
+
## Report + hand off (no auto-commit)
|
|
188
|
+
|
|
189
|
+
Phase B finishes with TWO mandatory checkpoints (a flat report bullet is too easy to skip — these are the two steps runs most often forget, so treat each as a gate you must consciously clear):
|
|
190
|
+
|
|
191
|
+
- **CHECKPOINT 1 — Decision-hunt (if Phase B ran on code): you cannot finish without accounting for B3b.** State explicitly: either (a) the ADR-worthy decisions you found and the _why_ you asked about (→ the ADRs written), or (b) that you ACTIVELY scanned and found none worth an ADR — and **name what you scanned** (which packages/files/areas) so "no ADRs" is visibly a real hunt's result. Reaching here having never asked the user a single _why_ about a code decision is a RED FLAG you skipped B3b — go do the hunt before reporting.
|
|
192
|
+
- **CHECKPOINT 2 — Source-cleanup: for EVERY source you fully converted, you MUST explicitly propose deleting it (with confirmation) before finishing.** Once a source (`TODO.md`, a tracker folder, a plan doc — including ones in dotfolders — a design note) is _fully_ captured into `work/` items, leaving the original creates **two sources of truth that drift**. So enumerate each fully-converted source and ASK to delete it ("<source> is now captured in <where> — delete it?"). This is not optional and not a side note: a run that converts sources but never proposes their cleanup has left the repo with dual truth. Only delete on explicit user confirmation; never `rm` on your own initiative; never propose deleting a source you only _partially_ converted (say what's left); if the user declines, leave them and note they are superseded. Reaching the end having converted ≥1 source but proposed deleting NONE is a RED FLAG you skipped this checkpoint.
|
|
193
|
+
- **Make the cleanup confirmation UNAMBIGUOUS — because deletion is destructive.** Present the cleanup prompt as its OWN clearly-labelled, explicitly-numbered list (e.g. "Delete? **1.** `<source-a>` **2.** `<source-b>`…"), and do NOT put any OTHER numbered list (e.g. a "Next steps 1/2/3") in the same message — a bare `1. yes / 2. no` reply must map to exactly ONE thing. If the user's reply is at all ambiguous about WHICH sources to delete (or you co-mingled it with another numbered list), do NOT guess and `rm` — re-state "to confirm: delete A, keep B?" and wait. A wrong guess here irreversibly deletes a file the user wanted kept. Prefer making cleanup the LAST interaction, after the final report, so nothing competes with it.
|
|
194
|
+
- **Resolve the ADR asks before finishing.** Batch any still-unasked _why_ questions into a single round rather than interrupting repeatedly. Write a complete ADR per _why_ the user supplies; for each they cannot/will not answer, write **NOTHING**. (Mention the un-answered ones in the report; persist no file for them.)
|
|
195
|
+
- **REPORT** every path written/created, re-synced, and every repo-owned file left untouched — grouped by bucket — plus anything left as `needsAnswers`/`observations`/`ideas`, and (ephemerally, report-only) any ADR-worthy decisions whose _why_ went un-answered. Note any `findings/` whose `source:` is code-derived (weakest provenance) so the human knows to verify them. Report the gate-run result (green/red).
|
|
196
|
+
- **Update `CONTEXT.md` to reflect what was populated:** fold domain vocabulary into the glossary; note which buckets this repo now uses, precise about polarity (`notes/findings/` = **external** ground-truth with sources; our own architecture in `CONTEXT.md`/`docs/`; open questions in `notes/ideas/` (vague) or `needsAnswers` tasks (near-spec)). Do NOT write "this repo carries reverse-engineered `findings/` about our code" — the polarity mistake B3 exists to prevent. Append/merge only; never clobber.
|
|
197
|
+
- **NEVER enumerate individual items in `CONTEXT.md` (no index files — the FOLDER is the index).** Describe _that_ the repo has ADRs / specs / observations and what they are FOR; do NOT list them one by one (e.g. not "`0001` (…), `0002` (…)"). A hand-maintained list goes stale the moment one is added/removed/superseded — and `docs/adr/` (the folder) already IS the canonical, always-current index (WORK-CONTRACT rule 2: no shared index/manifest; derive lists with `ls`). Applies to every bucket. (FINE and distinct: cross-referencing ONE specific ADR as a glossary term's authority — "the X seam (`docs/adr/x.md`)" — that points a term at its source of truth; the ban is on the _list_, not a pointed cross-reference.)
|
|
198
|
+
- **Hand off:** tell the user the repo is contract-ready and what's next — write a spec (`to-spec`), task it into tasks (`to-task`), or build with `dorfl do` (if the runner is installed — note the `harness`/`verify` configured).
|
|
199
|
+
- **Git etiquette:** do NOT stage/commit/push — leave everything in the working tree for the user to inspect and commit (the `to-spec`/`to-task` producer convention). For a big repo, Phase B is iterative: bound each run to a subset (one source area at a time), report, let the human review, run again.
|
|
200
|
+
|
|
201
|
+
## Boundary (what setup does NOT do)
|
|
202
|
+
|
|
203
|
+
- It does NOT install or require `dorfl` (the contract is runner-agnostic).
|
|
204
|
+
- It does NOT register an arbiter / configure CI (those are runner/CI concerns).
|
|
205
|
+
- It does NOT BUILD or claim work (that is the runner). A converted task is just another `tasks/ready/` (or staged `tasks/backlog/`) item the engine then advances.
|
|
206
|
+
- It NEVER writes an ADR whose _why_ it inferred from code (the discipline above); NEVER puts a description of our own code in `findings/` (that is `CONTEXT.md`/`docs/`); NEVER writes a vague wish into the task board `tasks/backlog/` (that is an `idea`); NEVER enumerates items into `CONTEXT.md` (the folder is the index); NEVER auto-commits or silently deletes.
|
|
207
|
+
|
|
208
|
+
## Templates
|
|
209
|
+
|
|
210
|
+
### `CONTEXT.md`
|
|
211
|
+
|
|
212
|
+
```md
|
|
213
|
+
# CONTEXT — <project> domain language
|
|
214
|
+
|
|
215
|
+
The domain glossary for `<project>`. Agents and skills use THIS vocabulary when naming modules, tests, and discussing the system. Architectural rationale lives in `docs/adr/` (decisions); product framing lives in `work/specs/`.
|
|
216
|
+
|
|
217
|
+
## What <project> is
|
|
218
|
+
|
|
219
|
+
<the user's one-to-two-sentence description, or: <!-- TODO: describe the project --> >
|
|
220
|
+
|
|
221
|
+
## Core domain terms
|
|
222
|
+
|
|
223
|
+
- **<term>** — <meaning> (seeded from the adoption conversation; refine as you go).
|
|
224
|
+
- **promptGuidance** — the per-repo NUDGE namespace in `dorfl.json` whose members (currently just `testFirst`) strengthen the wording in the worker's in-band prompt. NOT a gate: the `verify` step is still the only acceptance bar. Omitted ⇒ off; absence is the default.
|
|
225
|
+
- **work/ contract** — the on-disk system this repo uses, defined by the reference docs in **`work/protocol/`** (copied here by `setup`): `WORK-CONTRACT.md` (the contract), `CLAIM-PROTOCOL.md`, `REVIEW-PROTOCOL.md`, `task-template.md`, `spec-template.md`, `ADR-FORMAT.md`. Three REGIME umbrellas — `notes/` (capture buckets), `tasks/` (the build board), `specs/` (the spec lifecycle) — plus top-level `questions/` and `protocol/`. One markdown file per item, status = the folder it lives in (never a field). Capture buckets: `notes/ideas/` (proposed), `notes/observations/` (spotted, unverified, append-only), `notes/findings/` (verified external/domain ground truth, each with a `source:`). ADRs (`docs/adr/`, format in `work/protocol/ADR-FORMAT.md`) record what WE decided and why.
|
|
226
|
+
|
|
227
|
+
## Conventions
|
|
228
|
+
|
|
229
|
+
Standing per-change rules agents must follow in this repo.
|
|
230
|
+
|
|
231
|
+
<!-- e.g. "Every change requires a changeset (`pnpm changeset`)" / a CHANGELOG fragment / a news entry. Add yours here, or delete this section. For enforcement, wire your own check into the `dorfl.json` `verify` gate. -->
|
|
232
|
+
|
|
233
|
+
## Skills this repo uses
|
|
234
|
+
|
|
235
|
+
- Required: `setup` (onboarding/migration), `to-spec`, `to-task`.
|
|
236
|
+
- Recommended: `review`, `grill-me`.
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### `dorfl.json`
|
|
240
|
+
|
|
241
|
+
```json
|
|
242
|
+
{
|
|
243
|
+
"prepare": "<install/env-prep from A3b, or OMIT if the repo has no deps>",
|
|
244
|
+
"verify": "<stack-appropriate command from A3>",
|
|
245
|
+
"harness": "pi",
|
|
246
|
+
"autoBuild": false,
|
|
247
|
+
"autoTask": false,
|
|
248
|
+
"promptGuidance": {"testFirst": true}
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
> Only include `promptGuidance` if the maintainer answered YES to the A2 nudge question. If they said no / skip / don't know, OMIT the whole `promptGuidance` object — the runtime default (`false`) takes over.
|
|
253
|
+
|
|
254
|
+
> `prepare` — the env-prep / install step the runner runs ONCE before the first `verify` on a fresh worktree (install deps / submodules / codegen, from A3b). The sibling of `verify`, NOT baked into it (`prepare` = env-ready, `verify` = tree-green). **OMIT it entirely if the repo has no deps to install** (unset ⇒ a no-op; never write a default `pnpm install` into a repo with no lockfile). `verify` — the acceptance gate (set it correctly for the stack; cheap-first; no install/env-prep — that lives in `prepare`). `harness` — the agent adapter (`pi`, or `null` + `agentCmd` for a shell agent). `autoBuild` / `autoTask` — strict-by-default (off; `autoBuild` is the build-gate). `noPR` — the PR-INTENT axis (default off = "open a PR"): set `true` (or pass `--no-pr`) to push the branch but deliberately SKIP the review request even on an authed GitHub arbiter (the explicit suppress-PR intent). The review-request PROVIDER itself is NOT a config key — it is purely arbiter-derived (a GitHub remote ⇒ the GitHub provider, else push-only), with the identity's `providers.github` / ambient `gh` auth deciding whether `gh` can open the PR; there is no `provider`/`--provider` override. Add `defaultArbiter`, `integration`, `noPR`, `model` only as the repo needs them.
|
|
255
|
+
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
```
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# ADR Format
|
|
2
|
+
|
|
3
|
+
ADRs live in `docs/adr/` and use sequential numbering: `0001-slug.md`, `0002-slug.md`, etc.
|
|
4
|
+
|
|
5
|
+
Create the `docs/adr/` directory lazily — only when the first ADR is needed.
|
|
6
|
+
|
|
7
|
+
## Template
|
|
8
|
+
|
|
9
|
+
```md
|
|
10
|
+
# {Short title of the decision}
|
|
11
|
+
|
|
12
|
+
{1-3 sentences: what's the context, what did we decide, and why.}
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
That's it. An ADR can be a single paragraph. The value is in recording _that_ a decision was made and _why_ — not in filling out sections.
|
|
16
|
+
|
|
17
|
+
## Optional sections
|
|
18
|
+
|
|
19
|
+
Only include these when they add genuine value. Most ADRs won't need them.
|
|
20
|
+
|
|
21
|
+
- **Status** frontmatter (`proposed | accepted | deprecated | superseded by ADR-NNNN`) — useful when decisions are revisited
|
|
22
|
+
- **Considered Options** — only when the rejected alternatives are worth remembering
|
|
23
|
+
- **Consequences** — only when non-obvious downstream effects need to be called out
|
|
24
|
+
|
|
25
|
+
## Numbering
|
|
26
|
+
|
|
27
|
+
Scan `docs/adr/` for the highest existing number and increment by one.
|
|
28
|
+
|
|
29
|
+
## When to offer an ADR
|
|
30
|
+
|
|
31
|
+
All three of these must be true:
|
|
32
|
+
|
|
33
|
+
1. **Hard to reverse** — the cost of changing your mind later is meaningful
|
|
34
|
+
2. **Surprising without context** — a future reader will look at the code and wonder "why on earth did they do it this way?"
|
|
35
|
+
3. **The result of a real trade-off** — there were genuine alternatives and you picked one for specific reasons
|
|
36
|
+
|
|
37
|
+
If a decision is easy to reverse, skip it — you'll just reverse it. If it's not surprising, nobody will wonder why. If there was no real alternative, there's nothing to record beyond "we did the obvious thing."
|
|
38
|
+
|
|
39
|
+
### What qualifies
|
|
40
|
+
|
|
41
|
+
- **Architectural shape.** "We're using a monorepo." "The write model is event-sourced, the read model is projected into Postgres."
|
|
42
|
+
- **Integration patterns between contexts.** "Ordering and Billing communicate via domain events, not synchronous HTTP."
|
|
43
|
+
- **Technology choices that carry lock-in.** Database, message bus, auth provider, deployment target. Not every library — just the ones that would take a quarter to swap out.
|
|
44
|
+
- **Boundary and scope decisions.** "Customer data is owned by the Customer context; other contexts reference it by ID only." The explicit no-s are as valuable as the yes-s.
|
|
45
|
+
- **Deliberate deviations from the obvious path.** "We're using manual SQL instead of an ORM because X." Anything where a reasonable reader would assume the opposite. These stop the next engineer from "fixing" something that was deliberate.
|
|
46
|
+
- **Constraints not visible in the code.** "We can't use AWS because of compliance requirements." "Response times must be under 200ms because of the partner API contract."
|
|
47
|
+
- **Rejected alternatives when the rejection is non-obvious.** If you considered GraphQL and picked REST for subtle reasons, record it — otherwise someone will suggest GraphQL again in six months.
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# Claim protocol (consumed by the runner — `dorfl claim`/`do`/`complete`)
|
|
2
|
+
|
|
3
|
+
This documents how a `work/tasks/ready/<slug>.md` item is **atomically claimed** by one agent (human or autonomous) when several may try at once. The tasking discipline does not perform claims — it only emits files in a shape this protocol can consume. The runner/lifecycle implements the steps here.
|
|
4
|
+
|
|
5
|
+
## The core idea: claim = acquiring the item's per-item LOCK (an atomic create-only ref push)
|
|
6
|
+
|
|
7
|
+
A claim **acquires the item's per-item lock** — a hidden `refs/dorfl/lock/<type>-<slug>` ref (`<type>` is `task`/`spec`) created by an ATOMIC create-only push (`--force-with-lease=<ref>:`, i.e. "succeed only if the ref is still absent"). Git's ref-update-on-push IS the compare-and-swap: the winner creates the ref; a concurrent acquirer for the SAME item finds it present and is rejected = **definitively lost, with NO retry budget** (a per-item ref only ever contends with another writer for that same item — a genuine conflict the loser should lose). The item's body STAYS in `work/tasks/ready/<slug>.md`; **claim writes NOTHING to `main`** (so an agent can claim even on a protected `main`).
|
|
8
|
+
|
|
9
|
+
The claimable predicate is **"the body is in the pool `tasks/ready/` on `main` AND no lock is held on its ref."**
|
|
10
|
+
|
|
11
|
+
**Separate the claim from the work.** Acquire the lock first (cheap, collision-detecting); do the work only after the lock is provably held.
|
|
12
|
+
|
|
13
|
+
## The arbiter: one serialization point for updating `main`
|
|
14
|
+
|
|
15
|
+
The atomicity comes from a **single repo that everyone treats as the integration point** (`origin`), whose ref update on push linearizes claims. It can be EITHER:
|
|
16
|
+
|
|
17
|
+
- **A remote remote** — e.g. GitHub. Bare by construction; works across machines; everyone (including the human) participates by pushing to it.
|
|
18
|
+
- **A local bare remote** — a `--bare` repo in a folder (e.g. `work.git`), reached via `file://`. Works fully offline. **Must be `--bare`** (you cannot work _in_ the arbiter: a non-bare repo with `main` checked out rejects pushes to `main`, and force-enabling that moves `main` under your working tree).
|
|
19
|
+
|
|
20
|
+
The protocol is **identical** for both — it targets a remote _by name_ (`<arbiter>`), not a hardcoded URL. Switching offline↔online is `git remote set-url <arbiter> <url>` (or adding a second remote); the claim steps do not change.
|
|
21
|
+
|
|
22
|
+
> **Consequence the human must accept:** you participate like an agent — you reach `main` via push (ff / `pull --rebase` then push), NOT via unsynchronized local commits onto a checked-out `main` that is also the arbiter. The arbiter ref and a working `main` you hand-commit to cannot be the same ref. This is mild, good hygiene, and is what keeps the claim guarantee intact for everyone.
|
|
23
|
+
>
|
|
24
|
+
> **WARNING — reconcile by REBASE, never a plain `git pull` merge.** A merge does NOT re-run `verify` on the reconciled tree, so a clean merge can hide a semantically-broken result. If your push is rejected non-fast-forward: `git pull --rebase`, then re-run `verify` on the rebased tree BEFORE pushing. (The runner path enforces this automatically as the land invariant below; on the human path it is on you — the human path is deliberately lighter, but the invariant is the same.)
|
|
25
|
+
|
|
26
|
+
### Offline setup (local bare arbiter), once
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
# create the bare arbiter next to (not inside) your working clone
|
|
30
|
+
git clone --bare /path/to/project /path/to/project-work.git # or: git init --bare
|
|
31
|
+
# in each working clone, point an `arbiter` remote at it
|
|
32
|
+
git remote add arbiter file:///path/to/project-work.git
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
When back online, repoint: `git remote set-url arbiter <github-url>` (or push the bare repo's `main` up). Same protocol throughout.
|
|
36
|
+
|
|
37
|
+
## The command: `dorfl claim` / `do`
|
|
38
|
+
|
|
39
|
+
These steps are implemented (and verified against real git, including a truly simultaneous two-agent race) by the runner — so a human or agent does not hand-run the dance:
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
dorfl claim <slug> [--arbiter <remote>] [--by <who>] [--dry-run]
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Exit codes: `0` claimed · `2` not claimable (not in the pool, or the lock is already held = lost) · `1` usage/env error. The acquire is self-arbitrating (no contended-retry class — a per-item lock never falsely contends). The steps it performs:
|
|
46
|
+
|
|
47
|
+
## Claim steps
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
CLAIM (acquire the per-item lock; collision-detecting, no body move):
|
|
51
|
+
1. fetch the lock refs from <arbiter> (refs/dorfl/lock/*)
|
|
52
|
+
2. confirm the body is still in the pool: work/tasks/ready/<slug>.md on <arbiter>/main
|
|
53
|
+
3. build a PARENTLESS lock-entry commit (action: implement, state: active
|
|
54
|
+
— the SOLE `LockState`, post `retire-stuck-lock-state`; holder/since)
|
|
55
|
+
with plumbing — never touches the working tree/HEAD
|
|
56
|
+
4. push it create-only to refs/dorfl/lock/<type>-<slug> (<type> = task/spec)
|
|
57
|
+
with --force-with-lease=<ref>: (the EMPTY expected value = "ref must be absent")
|
|
58
|
+
├─ ACCEPTED -> the lock is atomically yours (the body stays in tasks/ready/;
|
|
59
|
+
| NOTHING was written to main).
|
|
60
|
+
└─ REJECTED -> the ref already exists: another writer holds this SAME item's
|
|
61
|
+
lock. You LOST, definitively (exit 2). No retry budget — pick a
|
|
62
|
+
DIFFERENT pool item. (holder/since are readable on the lock entry
|
|
63
|
+
via `dorfl status`.)
|
|
64
|
+
# who/when rides the lock entry, not a frontmatter field (no claimed_by/claimed_at).
|
|
65
|
+
|
|
66
|
+
WORK (only after the lock is held):
|
|
67
|
+
5. git switch -c work/<type>-<slug> <arbiter>/main # the body is still in tasks/ready/ on main
|
|
68
|
+
(use a dedicated worktree/clone for isolation when running in parallel)
|
|
69
|
+
6. do the work; tests green.
|
|
70
|
+
7a. SUCCESS path — the runner, at integration, lands the DURABLE move on main:
|
|
71
|
+
git mv work/tasks/ready/<slug>.md work/tasks/done/<slug>.md
|
|
72
|
+
committed together with the work (completed-task message, see below), then
|
|
73
|
+
RELEASES the lock (delete the ref). Order: durable main-move FIRST, lock
|
|
74
|
+
release SECOND — a crash between leaves a done-on-main item with a stale lock,
|
|
75
|
+
and recovery treats the main record as authoritative and clears it.
|
|
76
|
+
7b. BOUNCE path — if it could NOT complete (red gate, rebase/merge conflict, task
|
|
77
|
+
too ambiguous to build, timeout, rejected review): the runner performs ONE
|
|
78
|
+
crash-safe transition — write / update work/questions/<type>-<slug>.md (a
|
|
79
|
+
SidecarKind: 'stuck' sidecar) with the reason (+ any agent-surfaced questions),
|
|
80
|
+
set needsAnswers: true on the item body on main, RELEASE the lock — and SAVES
|
|
81
|
+
the recoverable work as a wip commit on the kept work/<type>-<slug> branch
|
|
82
|
+
(pushed to the arbiter). The body already rests in tasks/ready/. A human
|
|
83
|
+
ANSWERS the sidecar; the apply rung drains it (resolve = continue,
|
|
84
|
+
resolve+resolveReset = discard the work/<type>-<slug> branch and continue,
|
|
85
|
+
dispose = terminal git mv per regime). Ordering: surface-on-main FIRST,
|
|
86
|
+
release SECOND (main-authoritative on recovery). The `stuck` LOCK STATE
|
|
87
|
+
is RETIRED; `LockState` is 'active' only. (The build agent never touches the
|
|
88
|
+
lock or main — the runner owns both.)
|
|
89
|
+
8. integrate to <arbiter>/main as normal (PR on GitHub, or ff/rebase push offline).
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
> The durable `tasks/ready → tasks/done` / `specs/ready → specs/tasked` / `tasks/ready → tasks/cancelled` moves (and the bounce surface: write `work/questions/<type>-<slug>.md` + flip `needsAnswers`) are the writes to the shared `main` ref, so THEY keep a small retrying CAS; the per-item LOCK acquire/release never does (it is self-arbitrating). The two substrates are independent; the only lock that can outlive its leg is a genuine crash-orphan `active`, which `main`-authoritative recovery clears (post `retire-stuck-lock-state`, the pre-retirement `done + stuck` co-existence case no longer arises — a bounce releases the lock cleanly).
|
|
93
|
+
|
|
94
|
+
## The land invariant — rebase + re-verify + advance
|
|
95
|
+
|
|
96
|
+
Step 7a's durable `main` move (the LAND) is the mode-agnostic primitive: **fetch current `main` → rebase the work branch onto it → re-run `verify` (and review) on the rebased tree → advance.** A lost CAS or a moved-`main` between gate and push INVALIDATES any prior green and re-arms the gate (re-rebase, re-`verify`, retry — never a `--force`, never an auto-resolved conflict). Merge mode runs it inline at the serialised land; propose mode runs it at the human checkpoint (the propose PR is merged only after the rebased tip re-verifies green). Human review is ADDITIVE (intent/design/security), NEVER a substitute for the re-verify on the rebased tree. The durable _why_ — and the floor/ceiling gradient from bare git to a capable host — lives in ADR `land-primitive-rebase-reverify-advance`.
|
|
97
|
+
|
|
98
|
+
## The prompt handed to the work agent (the `## Prompt` wrapper)
|
|
99
|
+
|
|
100
|
+
When a human or an autonomous runner dispatches an agent to do the WORK phase, the agent is given a small, constant **wrapper** around the task's own `## Prompt` section. The wrapper is the same every time except the slug; an autonomous runner emits it deterministically. The task file is the spec; the wrapper just frames it and draws the line around git.
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
You are completing one work task in this repo. It has already been claimed for
|
|
104
|
+
you (its per-item lock is held) and lives at work/tasks/ready/<slug>.md — read that
|
|
105
|
+
file fully; it is your complete spec (What to build, Acceptance criteria, Prompt).
|
|
106
|
+
Also read its source spec (the task's `spec:` field, at work/specs/ready/<spec>.md)
|
|
107
|
+
for context.
|
|
108
|
+
|
|
109
|
+
<!-- if promptGuidance.testFirst -->
|
|
110
|
+
Implement it to satisfy every Acceptance criterion. At the agreed seam, write
|
|
111
|
+
the failing test BEFORE the production code, matching the repo's house style;
|
|
112
|
+
this is guidance, not a gate — the `verify` step still decides pass/fail.
|
|
113
|
+
<!-- else -->
|
|
114
|
+
Implement it to satisfy every Acceptance criterion. TDD where the task asks for
|
|
115
|
+
it; match the repo's house style.
|
|
116
|
+
<!-- /if -->
|
|
117
|
+
|
|
118
|
+
If you NOTICE a problem OUTSIDE this task's scope (a flaky test, a latent bug, a
|
|
119
|
+
suspicious behaviour), do NOT fix it and do NOT expand your scope. Instead drop a
|
|
120
|
+
short, dated note in work/notes/observations/<short-slug>.md (one or two sentences
|
|
121
|
+
is enough — what you saw and where) so the signal is captured, then carry on with
|
|
122
|
+
your task. (work/notes/observations/ is an append-only capture bucket; anyone, you
|
|
123
|
+
included, may add to it. Writing such a NOTE is the one exception to the "no file
|
|
124
|
+
changes outside your task" rule below — it is a note, not work.)
|
|
125
|
+
|
|
126
|
+
If the TASK ITSELF is the problem — it is ambiguous, under-specified, rests on a
|
|
127
|
+
premise that no longer matches the code/ADRs (it has DRIFTED), or hides an
|
|
128
|
+
unresolved design decision — do NOT guess and build on it. STOP and report
|
|
129
|
+
specifically what is unclear or contradicted (and where), so a human can resolve it
|
|
130
|
+
(the runner routes the item to needs-attention). Do not be shy about this: a
|
|
131
|
+
confident build on a wrong/ambiguous premise produces wrong-but-compiling work that
|
|
132
|
+
is far more expensive than a question. Building exactly what a flawed task says is
|
|
133
|
+
NOT success.
|
|
134
|
+
|
|
135
|
+
To STOP, make NO source change and end your final report with this EXACT
|
|
136
|
+
machine-readable block (the runner detects it, routes the item to
|
|
137
|
+
needs-attention with your reason VERBATIM, and SKIPS the gate + review — so put
|
|
138
|
+
the specific drift report INSIDE it):
|
|
139
|
+
|
|
140
|
+
=== TASK-STOP ===
|
|
141
|
+
<the specific reason: which premises are false, where, and a suggested re-scope>
|
|
142
|
+
=== END TASK-STOP ===
|
|
143
|
+
|
|
144
|
+
The decision bar between "resolve and proceed" and "STOP" / "record a decision":
|
|
145
|
+
A genuinely small, certain, SELF-CONTAINED factual gap you can resolve from the
|
|
146
|
+
code itself (it affects nothing outside this task), resolve and proceed silently.
|
|
147
|
+
But a choice that touches ANOTHER command/flag/task, introduces a new
|
|
148
|
+
ERROR/REFUSAL, or sets a USER-VISIBLE DEFAULT is a DESIGN decision, NOT a small
|
|
149
|
+
factual gap — do NOT bury it in code. If it is load-bearing AND hard to reverse,
|
|
150
|
+
STOP (above). Otherwise PROCEED but RECORD it DURABLY and LINK it from the done
|
|
151
|
+
record, one entry per decision — what you chose + why + the alternative(s) you
|
|
152
|
+
considered + what it touches (which other flag/command/task). Any durable home
|
|
153
|
+
is acceptable: a module JSDoc at the choice site (best when there is an obvious
|
|
154
|
+
code site the decision governs), a "## Decisions" block in the done record / PR
|
|
155
|
+
body (the recommended fallback when there is no natural code site), or a dated
|
|
156
|
+
observation note under work/notes/observations/. Whichever home you pick, LINK
|
|
157
|
+
it from the done record so it is discoverable. This does NOT stop the build; it
|
|
158
|
+
makes the choice visible so the reviewer + the human can ratify or reverse it.
|
|
159
|
+
The bar is "would another task / a user / a reviewer be surprised this was
|
|
160
|
+
decided here?" — if yes, record it. A real ambiguity or stale premise, STOP.
|
|
161
|
+
|
|
162
|
+
COHERENCE CHECK (before you introduce a new concept). Consistency and coherence
|
|
163
|
+
with the system's existing LANGUAGE is a first-class quality. Before you add a new
|
|
164
|
+
flag / config key / status / verb / named concept, check it against the project's
|
|
165
|
+
`CONTEXT.md` glossary + the ADRs + the existing code: (1) does the name already
|
|
166
|
+
MEAN something — are you silently re-meaning it or making it mean two things? (2)
|
|
167
|
+
is the concept at the RIGHT LAYER (e.g. a policy gate on the autonomous-selection
|
|
168
|
+
step vs the explicit verb a human typed)? (3) does it DUPLICATE/overlap an existing
|
|
169
|
+
concept you should reuse or rename instead of forking? If a new concept conflicts
|
|
170
|
+
with, re-means, or duplicates an existing one — or sits at the wrong layer — that is
|
|
171
|
+
NOT a "small factual gap": STOP if it is load-bearing/hard-to-reverse, else RECORD
|
|
172
|
+
it durably per the rule above (JSDoc at the choice site, a `## Decisions` entry
|
|
173
|
+
in the done record, or an observation note — linked from the done record), noting
|
|
174
|
+
what concept, what it overlaps, why your placement. This is
|
|
175
|
+
the prevention half of the review's conceptual-coherence lens — a muddled concept
|
|
176
|
+
that compiles is far more expensive than the question, because every later artifact
|
|
177
|
+
that reuses the muddled term inherits the debt.
|
|
178
|
+
|
|
179
|
+
Do NOT perform any git operations on THIS repo — do not stage, commit, push, or
|
|
180
|
+
move any files between work/ folders, and do not touch the item's lock ref or its
|
|
181
|
+
body at work/tasks/ready/<slug>.md. The runner (or human) owns every git-state
|
|
182
|
+
transition (the durable main-moves AND the per-item lock acquire/release/amend).
|
|
183
|
+
(Your TESTS may freely create and operate on their OWN throwaway git repos — that
|
|
184
|
+
is expected.)
|
|
185
|
+
|
|
186
|
+
Leave a CLEAN working tree — only the changes this task intends. The runner
|
|
187
|
+
commits everything untracked (`git add -A`), so any scratch, debug, or
|
|
188
|
+
runtime-artifact file you or your tools created would otherwise be swept into the
|
|
189
|
+
commit. Before you stop, delete such stray untracked files, or add them to
|
|
190
|
+
.gitignore if they legitimately belong ignored. This is NOT git work: deleting an
|
|
191
|
+
untracked file or editing .gitignore is producing clean WORK, like writing source
|
|
192
|
+
— the "no git" rule above (no stage/commit/push/move) still holds.
|
|
193
|
+
|
|
194
|
+
When the acceptance criteria are met and the repo's build/test/format checks are
|
|
195
|
+
green, STOP and report what you did. The runner handles the durable `git mv` of the
|
|
196
|
+
body tasks/ready/ -> work/tasks/done/, the completion commit, the lock release, and
|
|
197
|
+
integration.
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
The "no git" line is **in-band** in the prompt (not delegated to a host config like a global `AGENTS.md`): a portable runner cannot assume the target machine has any such rule, so the boundary travels with the prompt. This keeps the acceptance-test gate authoritative (the agent can't commit/merge around it) and the runner the single owner of git state.
|
|
201
|
+
|
|
202
|
+
## Completed-task commit message
|
|
203
|
+
|
|
204
|
+
The commit that completes a task (the work + the `git mv` to `work/tasks/done/`) uses a consistent, greppable format so the lifecycle is visible in `git log` and an autonomous runner can author it deterministically:
|
|
205
|
+
|
|
206
|
+
```
|
|
207
|
+
<type>(<slug>): <task title or short summary>; done
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
- `<type>` follows conventional-commits (`feat`, `fix`, `docs`, `chore`, …); use `feat` for a task that adds behaviour.
|
|
211
|
+
- `<slug>` is the task slug (its `work/tasks/done/<slug>.md` basename).
|
|
212
|
+
- the trailing **`; done`** marks the durable `tasks/ready→tasks/done` transition landing in this commit (the claim itself has no `main` commit to mirror — it is a lock-ref acquire, not a folder move).
|
|
213
|
+
|
|
214
|
+
Example: `feat(scan): cross-repo eligible-work queue (read-only); done`
|
|
215
|
+
|
|
216
|
+
Keep it ONE commit (work + the `git mv`) so a task's completion is a single, atomic, revertable unit — just as the claim is a single commit.
|
|
217
|
+
|
|
218
|
+
## Why this prevents (not merely detects) double-claims
|
|
219
|
+
|
|
220
|
+
The rejected push is the rejection of the claim. Because the arbiter serializes ref updates, only one create-only push to `refs/dorfl/lock/<type>-<slug>` can win; all others are rejected atomically by `git receive-pack`'s ref lock. No lock server, no integrator process. `--force-with-lease` is a CAS against the expected old value (safe); `--force` would clobber and MUST NOT be used.
|
|
221
|
+
|
|
222
|
+
## Isolation for parallel agents
|
|
223
|
+
|
|
224
|
+
Run each agent's work in its **own clone or worktree** so on-disk code changes can't collide; conflicts then only surface at integration time (normal PR-style resolution), never as corrupted shared state. Clones-of-an-arbiter give fully independent object stores (best isolation); worktrees share one object store (save disk) — either is fine, but prefer separate clones when many agents run at once.
|