devlyn-cli 4.3.28 → 4.3.30
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/AGENTS.md +1 -1
- package/CLAUDE.md +1 -1
- package/README.md +10 -10
- package/bin/devlyn.js +92 -58
- package/bin/engines.js +10 -10
- package/bin/harness-activation.js +369 -23
- package/bin/harness-context.js +7 -7
- package/bin/instruction-templates.json +2 -0
- package/bin/skill-history.json +12 -0
- package/config/skills/_shared/engine-doctor.sh +2 -2
- package/config/skills/_shared/engine-preflight.md +2 -4
- package/config/skills/_shared/managed-storage.md +80 -0
- package/config/skills/_shared/platform-support.py +67 -28
- package/config/skills/_shared/role-config.py +16 -7
- package/config/skills/_shared/runtime-principles.md +1 -1
- package/config/skills/_shared/storage-client.py +31 -7
- package/config/skills/_shared/task-complete.py +1 -1
- package/config/skills/_shared/task-completion.md +5 -91
- package/config/skills/devlyn-engines/SKILL.md +1 -1
- package/config/skills/devlyn-ideate/SKILL.md +2 -2
- package/config/skills/devlyn-ideate/references/executor.md +62 -0
- package/config/skills/devlyn-ideate/references/loop.md +4 -63
- package/config/skills/devlyn-ideate/references/package-format.md +1 -2
- package/config/skills/devlyn-ideate/scripts/acceptance.py +442 -21
- package/config/skills/devlyn-ideate/scripts/queue.py +58 -10
- package/harness/AGENTS.md +2 -2
- package/harness/CLAUDE.md +2 -2
- package/native/claude/.claude-plugin/plugin.json +1 -1
- package/native/claude/skills/_shared/engine-doctor.sh +2 -2
- package/native/claude/skills/_shared/engine-preflight.md +2 -4
- package/native/claude/skills/_shared/managed-storage.md +80 -0
- package/native/claude/skills/_shared/platform-support.py +67 -28
- package/native/claude/skills/_shared/role-config.py +16 -7
- package/native/claude/skills/_shared/runtime-principles.md +1 -1
- package/native/claude/skills/_shared/storage-client.py +31 -7
- package/native/claude/skills/_shared/task-complete.py +1 -1
- package/native/claude/skills/_shared/task-completion.md +5 -91
- package/native/claude/skills/devlyn-engines/SKILL.md +1 -1
- package/native/claude/skills/devlyn-ideate/SKILL.md +2 -2
- package/native/claude/skills/devlyn-ideate/references/executor.md +62 -0
- package/native/claude/skills/devlyn-ideate/references/loop.md +4 -63
- package/native/claude/skills/devlyn-ideate/references/package-format.md +1 -2
- package/native/claude/skills/devlyn-ideate/scripts/acceptance.py +442 -21
- package/native/claude/skills/devlyn-ideate/scripts/queue.py +58 -10
- package/native/codex/.agents/plugins/marketplace.json +1 -1
- package/native/codex/plugins/devlyn/.codex-plugin/plugin.json +1 -1
- package/native/codex/plugins/devlyn/skills/_shared/engine-doctor.sh +2 -2
- package/native/codex/plugins/devlyn/skills/_shared/engine-preflight.md +2 -4
- package/native/codex/plugins/devlyn/skills/_shared/managed-storage.md +80 -0
- package/native/codex/plugins/devlyn/skills/_shared/platform-support.py +67 -28
- package/native/codex/plugins/devlyn/skills/_shared/role-config.py +16 -7
- package/native/codex/plugins/devlyn/skills/_shared/runtime-principles.md +1 -1
- package/native/codex/plugins/devlyn/skills/_shared/storage-client.py +31 -7
- package/native/codex/plugins/devlyn/skills/_shared/task-complete.py +1 -1
- package/native/codex/plugins/devlyn/skills/_shared/task-completion.md +5 -91
- package/native/codex/plugins/devlyn/skills/devlyn-engines/SKILL.md +1 -1
- package/native/codex/plugins/devlyn/skills/devlyn-ideate/SKILL.md +2 -2
- package/native/codex/plugins/devlyn/skills/devlyn-ideate/references/executor.md +62 -0
- package/native/codex/plugins/devlyn/skills/devlyn-ideate/references/loop.md +4 -63
- package/native/codex/plugins/devlyn/skills/devlyn-ideate/references/package-format.md +1 -2
- package/native/codex/plugins/devlyn/skills/devlyn-ideate/scripts/acceptance.py +442 -21
- package/native/codex/plugins/devlyn/skills/devlyn-ideate/scripts/queue.py +58 -10
- package/package.json +1 -11
- package/release.json +99 -69
- /package/config/skills/_shared/adapters/{claude.md → claude.adapter.md} +0 -0
- /package/config/skills/_shared/adapters/{codex.md → codex.adapter.md} +0 -0
- /package/config/skills/_shared/adapters/{omp.md → omp.adapter.md} +0 -0
- /package/native/claude/skills/_shared/adapters/{claude.md → claude.adapter.md} +0 -0
- /package/native/claude/skills/_shared/adapters/{codex.md → codex.adapter.md} +0 -0
- /package/native/claude/skills/_shared/adapters/{omp.md → omp.adapter.md} +0 -0
- /package/native/codex/plugins/devlyn/skills/_shared/adapters/{claude.md → claude.adapter.md} +0 -0
- /package/native/codex/plugins/devlyn/skills/_shared/adapters/{codex.md → codex.adapter.md} +0 -0
- /package/native/codex/plugins/devlyn/skills/_shared/adapters/{omp.md → omp.adapter.md} +0 -0
|
@@ -62,9 +62,6 @@ The repository comes from the remote's GitHub URL and the base from the reposito
|
|
|
62
62
|
A non-default base is refused at allocation.
|
|
63
63
|
Every delivery allocation owns a linked worktree; `--worktree` is required and
|
|
64
64
|
must name an absent path.
|
|
65
|
-
Cleanup removes empty parents through the exact per-repo root shown above or its legacy `.devlyn.noindex`/`.devlyn` root, keeping the shared `.devlyn-worktrees` container. Allocation also sweeps empty legacy roots for this repo.
|
|
66
|
-
On Windows, allocation marks only a newly created shared container hidden and creates the worktree not content-indexed (content only; names stay indexed); files copied in keep their own attributes.
|
|
67
|
-
A marking failure continues allocation and is reported in `warnings`.
|
|
68
65
|
Remote allocation first cleans earlier merged deliveries, then allocation refuses with `BLOCKED:disk`
|
|
69
66
|
without creating a receipt, branch or worktree when that filesystem has less than max(10 GiB, 2%) free; free space and allocate again.
|
|
70
67
|
A publication allocation normally starts at the exact fetched remote base,
|
|
@@ -96,75 +93,7 @@ can an interrupted standalone receipt that never reached `allocation: owned`:
|
|
|
96
93
|
remove its worktree and branch if present, delete its receipt directory, then allocate again.
|
|
97
94
|
Unreceipted tasks remain owner-managed.
|
|
98
95
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
| `~/.devlyn/storage-client.json` | recovery marker | unenroll journal | Mode |
|
|
102
|
-
|---|---|---|---|
|
|
103
|
-
| absent | absent | not read | standalone (never enrolled) |
|
|
104
|
-
| valid | any | not read | managed; a fenced authority refuses |
|
|
105
|
-
| invalid or unreadable | any | not read | refuse; repair registration |
|
|
106
|
-
| absent | present | verified `retired: true` | standalone (unenrollment complete) |
|
|
107
|
-
| absent | present | absent, unreadable or incomplete | refuse; Open Devlyn to repair its storage registration |
|
|
108
|
-
|
|
109
|
-
An unfinished retirement takes precedence over enrollment; follow its
|
|
110
|
-
operation-specific retained-helper resume to reconcile or cancel as named.
|
|
111
|
-
The journal `storage-client-unenroll.json` is read no-follow for retirement
|
|
112
|
-
recovery and the last two rows. Schema 1, canonical UUIDv7 operation, permanent `storage-client-fence.json`,
|
|
113
|
-
matching operation/root/lock identities and Rust's archive ordering/path
|
|
114
|
-
validation are required. If native root/lock identities or the home path changed,
|
|
115
|
-
completed retirement permits standalone only when `storage`, `control`,
|
|
116
|
-
`harness/state.json` and `storage-client.json` are all absent under the current
|
|
117
|
-
lifecycle root (checked without following links). Live inventory requires repair.
|
|
118
|
-
No repository-wide receipt scan selects mode. Other keys can allocate after
|
|
119
|
-
completed unenrollment; a leftover managed key retains its own recovery command.
|
|
120
|
-
|
|
121
|
-
Existing operations keep their managed identity. Receipts pin the control
|
|
122
|
-
identity; current enrollment verifies the helper before allocation. Recovery uses
|
|
123
|
-
the current verified `storage-client-recovery/pins.json`, including after uninstall
|
|
124
|
-
or unenrollment, and requires its authority binding to match the receipt, except
|
|
125
|
-
for a proven moved task: use its unflagged, operation-specific `recover-storage`
|
|
126
|
-
resume to release it, retain its files, and archive its receipt under `moved/`. Missing
|
|
127
|
-
or invalid pins retain resources and require repair; saved artifacts are never a
|
|
128
|
-
fallback. Enrolled checkout preserves
|
|
129
|
-
`managed_pending`/`managed_owned` and Rust's `storage-allocation.json` subreceipt.
|
|
130
|
-
After an interrupted reply, use the returned `recover-storage` resume. Never
|
|
131
|
-
delete managed receipts or replace an unfinished operation ID.
|
|
132
|
-
|
|
133
|
-
Owner `recover-storage --receipt <receipt.json> --operation-id <saved ID>
|
|
134
|
-
--for-removal` fences allocation and reconciles closure and the exact cache set.
|
|
135
|
-
Never-published allocations are abandoned. Accepted LOCAL_ONLY, FAILED, and PRs
|
|
136
|
-
observed CLOSED without merge share custody/recovery-ref verification and fenced
|
|
137
|
-
removal, retaining task branches and a durable terminal delivery. Release writers
|
|
138
|
-
with `complete --writers-stopped` first. A queue-produced FAILED checkout is
|
|
139
|
-
advanced by `read-tree -m -u <source_sha> <publish_sha>` only when HEAD is at the
|
|
140
|
-
attached publish commit and the index and worktree are exactly at source; other
|
|
141
|
-
differences refuse as dirty.
|
|
142
|
-
|
|
143
|
-
Removal-finished receipts with final COMPLETE, LOCAL_ONLY, FAILED or CLOSED
|
|
144
|
-
delivery move under `devlyn-completion/retired/`, regardless of scratch status.
|
|
145
|
-
Retirement moves scratch and its saved cleanup record with the audit and deletes
|
|
146
|
-
nothing. `complete --writers-stopped` saves the writer release there and retries
|
|
147
|
-
pending scratch, including an interrupted cleanup with no saved result; the next
|
|
148
|
-
allocation retries completed deliveries with released writers. `clean-scratch`
|
|
149
|
-
remains available there and never downgrades CLEAN.
|
|
150
|
-
Saved managed `complete`, `recover-storage` and `clean-scratch` resumes carry an
|
|
151
|
-
operation ID validated under the receipt lock. Without an ID, a locator selects
|
|
152
|
-
its active receipt. An ID selects only that operation, including its verified
|
|
153
|
-
retired receipt; a mismatched ID refuses. Retired paths also remain usable.
|
|
154
|
-
A new allocation may reuse a retired key without disturbing its audit.
|
|
155
|
-
|
|
156
|
-
Retained submodule repositories in the admin directory block removal even when
|
|
157
|
-
the checkout is absent; preserve/publish their source and explicitly resolve
|
|
158
|
-
them. Manual checkout removal is advised only after portable source checks and
|
|
159
|
-
record preservation succeed; changed or new source requires retention. If native
|
|
160
|
-
Windows or unobservable writers then require retention, stop all
|
|
161
|
-
writers, remove the task checkout by hand, keep its branch and receipt, then run
|
|
162
|
-
the returned owner-removal resume. The absent-tree path completes the fenced
|
|
163
|
-
registration removal. Scratch cleanup requires positive allocation closure,
|
|
164
|
-
without a cache-removal fence; actual checkout removal owns that fence, including
|
|
165
|
-
empty cache sets. Missing helpers or uncertain closure retain resources with a
|
|
166
|
-
repair/resume action. Compatible recovery artifacts and Rust's LaunchGate remain
|
|
167
|
-
required while managed obligations exist.
|
|
96
|
+
Work is standalone when `~/.devlyn/storage-client.json` and its recovery marker `~/.devlyn/storage-client-recovery` are both absent and the receipt in hand is not a managed one; otherwise read [managed-storage.md](managed-storage.md) first.
|
|
168
97
|
|
|
169
98
|
Copy attributable task edits into the allocated worktree as a patch, never a
|
|
170
99
|
stash. Establish attribution from the pre-edit contents and starting Git state,
|
|
@@ -183,10 +112,6 @@ require the affected checks again.
|
|
|
183
112
|
Keep the original edits through pending or failed delivery; only reconciliation
|
|
184
113
|
removes them. Preserve all other work and report anything left behind.
|
|
185
114
|
|
|
186
|
-
Managed allocations currently start cold; `build_dirs` retains each returned cache
|
|
187
|
-
and publication lease ID. Managed warming is deferred. Standalone allocations keep
|
|
188
|
-
the warming behavior below.
|
|
189
|
-
|
|
190
115
|
Build in the task worktree's own default build directory, never in one
|
|
191
116
|
another checkout builds in, even one build at a time: tools such as Cargo take
|
|
192
117
|
one checkout's outputs as fresh for the other. When the shared repository-local
|
|
@@ -194,9 +119,7 @@ Git config `devlyn.warmBuildDirs` is true, or it is unset and the Cargo workspac
|
|
|
194
119
|
Cargo.toml sets `[workspace.metadata.devlyn] warm-build-dirs = true`,
|
|
195
120
|
allocation seeds that directory from a built checkout of the repository, so
|
|
196
121
|
the first build does not rebuild every dependency; `build_dirs` in its result
|
|
197
|
-
and receipt says what it did, and a skip means an ordinary cold build.
|
|
198
|
-
Git setting takes precedence on this machine: false disables and invalid values
|
|
199
|
-
skip; global, system, include and per-worktree values are not consulted. Seeding
|
|
122
|
+
and receipt says what it did, and a skip means an ordinary cold build. Seeding
|
|
200
123
|
keeps dependency outputs that may hold the other checkout's paths, and those
|
|
201
124
|
follow that checkout's later rebuilds. `warm-build-dirs.py <worktree>` does the same for a
|
|
202
125
|
worktree made without allocation. A warmed directory leaves with the worktree
|
|
@@ -216,14 +139,9 @@ release build caches when parking or after a failed run, use:
|
|
|
216
139
|
```sh
|
|
217
140
|
python3 "$DEVLYN_SHARED_DIR/task-complete.py" clean-scratch \
|
|
218
141
|
--receipt '<returned receipt.json>' --writers-stopped
|
|
219
|
-
# For managed receipts, use the saved resume with --operation-id <saved ID>.
|
|
220
142
|
```
|
|
221
143
|
|
|
222
|
-
|
|
223
|
-
identity for interrupted cleanup and later builds, checks live users and reports
|
|
224
|
-
the logical byte count separately from actual free disk space.
|
|
225
|
-
Unknown historical roots, source/recovery data, shared caches and terminal
|
|
226
|
-
history are never adopted or swept. A refused cleanup does not block source
|
|
144
|
+
A refused cleanup does not block source
|
|
227
145
|
delivery; it stays visible in the handoff with its receipt and retry command
|
|
228
146
|
(`CLEANUP_PENDING` after delivered source). No task is described as cleaned
|
|
229
147
|
while owned disposable files remain. On resumption, finish any pending scratch
|
|
@@ -321,8 +239,7 @@ the next allocation or resume after the restart.
|
|
|
321
239
|
On native Windows, locking and file durability are supported, but the helper cannot prove writer cessation and reports `writer observation unsupported on this platform; retain workspace`. Even `--writers-stopped` cannot authorize deletion without that observation: retain the workspace, its task refs and external receipt/custody, report delivery separately, and preserve the receipt-based resume command. A merged or on-base delivery therefore reports `CLEANUP_PENDING` (delivery `COMPLETE`) with `workspace_cleanup` naming the reason and resume. Do not kill unknown processes or force worktree removal to make completion pass.
|
|
322
240
|
|
|
323
241
|
The receipt directory holds byte-verified `custody/`, `manifest.json` and a guarded
|
|
324
|
-
`refs/devlyn/completed/<id>` recovery ref
|
|
325
|
-
`<id>.<operation>` so branch reuse preserves earlier recovery refs). Check evidence is copied there
|
|
242
|
+
`refs/devlyn/completed/<id>` recovery ref. Check evidence is copied there
|
|
326
243
|
before publication; removal rechecks it, the recovery ref and the merge or accepted commit on base.
|
|
327
244
|
Ignored build output and other ignored files are disposable under native
|
|
328
245
|
non-force worktree removal. The tree's `.devlyn/` files are byte/mode-verified
|
|
@@ -331,9 +248,6 @@ are preserved beside it as `records.<digest>/.devlyn`, never rewriting earlier c
|
|
|
331
248
|
Non-regular files or symlinks there, partial/corrupt custody, an unreadable
|
|
332
249
|
earlier copy or a mismatched `records.<digest>` copy retain the tree for repair.
|
|
333
250
|
|
|
334
|
-
|
|
335
|
-
checked out; their checkout is never modified. Local ref deletion is guarded by
|
|
336
|
-
the expected SHA; remote deletion uses an exact force-with-lease solely as
|
|
337
|
-
compare-and-delete, never for product publication. A changed remote branch is
|
|
251
|
+
A changed remote branch is
|
|
338
252
|
retained. Retry uses the external receipt/evidence after removal. Recovery
|
|
339
253
|
refs/evidence have no automatic expiry.
|
|
@@ -56,7 +56,7 @@ Verify DEVLYN_SHARED_DIR/role-config.py and DEVLYN_SHARED_DIR/engine-doctor.sh b
|
|
|
56
56
|
|
|
57
57
|
## Subcommands
|
|
58
58
|
|
|
59
|
-
- `executor <name>` — run the status command with `--set-executor <name>`. It refuses a name with no `_shared/adapters/<name>.md`, or whose adapter declares `executor: no`, and lists the valid names. An engine whose CLI is
|
|
59
|
+
- `executor <name>` — run the status command with `--set-executor <name>`. It refuses a name with no `_shared/adapters/<name>.adapter.md`, or whose adapter declares `executor: no`, and lists the valid names. An engine whose CLI is unavailable is still pinned, with a warning: pins are promises, and dispatch stops with `BLOCKED:<name>-unavailable` until installed.
|
|
60
60
|
- `clear` — run the status command with `--clear`: it removes the executor pin and the inactive keys, preserves unrelated keys, and deletes an empty file.
|
|
61
61
|
- `pair` and `role` were removed with the retired pipeline roles: say so and change nothing.
|
|
62
62
|
|
|
@@ -106,7 +106,7 @@ For read-only consumers, `python3 "$DEVLYN_SKILL_DIR/scripts/queue.py" inspect -
|
|
|
106
106
|
|
|
107
107
|
`status`, `add` and `drain` write the repository's Git directory, and auto/pr delivery needs network access, so a sandboxed host runs them with escalation or full access.
|
|
108
108
|
|
|
109
|
-
|
|
109
|
+
See [loop.md](references/loop.md) for an unclear result or `--worktree-root`. The executor is the configured route, the `.devlyn/engines.json` `executor` pin or else this CLI, checked per `_shared/engine-preflight.md`. Pass its argv after `--`. `<git dir>` is the output of `git rev-parse --path-format=absolute --git-common-dir`, where the executor commits and writes its submission; the drain fills `{worktree_git_dir}` with the task worktree's own Git directory, which Codex's sandbox keeps read-only unless that exact directory is listed:
|
|
110
110
|
|
|
111
111
|
| Executor | argv |
|
|
112
112
|
|---|---|
|
|
@@ -116,7 +116,7 @@ Follow [loop.md](references/loop.md). The executor is the configured route, the
|
|
|
116
116
|
Another engine needs an argv that starts one fresh non-interactive session able, without prompts, to edit, run commands and commit in its task worktree and to write the packet's submission file. `<prompt>` carries the absolute binding values, while `{packet}` stays literal for the drain to fill:
|
|
117
117
|
|
|
118
118
|
```text
|
|
119
|
-
Execute the devlyn loop task packet {packet}: read its workerPrompt when present; read methodology.defaults.path when named and apply those verified defaults, with explicit user and native owner/project instructions taking precedence, except a Devlyn managed block (devlyn:instructions markers), which those defaults replace; work only in its owned worktree under the installed instructions; meet its obligations, then write its submission and any review records as
|
|
119
|
+
Execute the devlyn loop task packet {packet}: read its workerPrompt when present; read methodology.defaults.path when named and apply those verified defaults, with explicit user and native owner/project instructions taking precedence, except a Devlyn managed block (devlyn:instructions markers), which those defaults replace; work only in its owned worktree under the installed instructions; meet its obligations, then write its submission and any review records as <DEVLYN_SKILL_DIR>/references/executor.md specifies. Use the literal DEVLYN_SKILL_DIR and DEVLYN_SHARED_DIR bindings in workerPrompt when present; otherwise set DEVLYN_SKILL_DIR=<DEVLYN_SKILL_DIR> and DEVLYN_SHARED_DIR=<DEVLYN_SHARED_DIR> from these literal values. Never use inherited environment values. Never push, open a pull request or merge: the drain delivers.
|
|
120
120
|
```
|
|
121
121
|
|
|
122
122
|
```sh
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Loop executor
|
|
2
|
+
|
|
3
|
+
## Executor exchange
|
|
4
|
+
|
|
5
|
+
The packet (`<receipt dir>/packet.json`) carries everything a fresh executor needs:
|
|
6
|
+
|
|
7
|
+
| Field | Meaning |
|
|
8
|
+
|---|---|
|
|
9
|
+
| `task`, `title`, `kind` | Queue identity and deliverable description. |
|
|
10
|
+
| `worktree`, `branch`, `receipt`, `scratch` | Absolute owned worktree, branch, receipt and disposable build directory. |
|
|
11
|
+
| `allocation_base`, `inputs_sha` | Allocation base and the committed-inputs commit. |
|
|
12
|
+
| `contract`, `expected`, `meta` | `{path, sha256}` of the committed task contract, expected acceptance and meta-prompt. |
|
|
13
|
+
| `requirements`, `review_requirements` | Requirement IDs and those needing review evidence. |
|
|
14
|
+
| `shared_constraints` | The meta-prompt's `Constraints and exclusions`. |
|
|
15
|
+
| `dependencies` | `{task, receipt, source_sha}` of each accepted prerequisite. |
|
|
16
|
+
| `schema_version`, `harness` | New packets use version 2 and the bound release reference, or null for legacy unbound queues. Existing v1 packets execute unchanged. |
|
|
17
|
+
| `methodology` | `projectInstructions`: `{path, sha256}` of instruction bytes at the inputs commit. `defaults`: `{source: "release", path, sha256}` from the verified binding; for an unbound queue `{source: "skill", path, sha256}` of the installation's own `_shared/runtime-principles.md`. Read the named defaults; explicit user and native owner/project instructions take precedence, except a Devlyn managed block (devlyn:instructions markers), which the defaults replace. No native engine applicability is inferred from filenames. |
|
|
18
|
+
| `workerPrompt` | `{path, sha256}` of the task’s worker instructions, or null for legacy unbound queues. Read it before working. |
|
|
19
|
+
| `delivery` | `local-only`, `auto` or `pr`. |
|
|
20
|
+
| `evidence_dir`, `runner` | Where loop evidence lives (`<worktree>/.devlyn/loop`, ignored by Git) and the runner argv. |
|
|
21
|
+
| `submission`, `obligations` | Where to write the submission, and the completion obligations. |
|
|
22
|
+
|
|
23
|
+
Writable tool caches go under `scratch`, for example `npm_config_cache=<scratch>/npm`: Codex's sandbox keeps caches in HOME read-only.
|
|
24
|
+
|
|
25
|
+
Build in the worktree's default build directory, never one redirected into `scratch` or shared with another checkout: drain re-runs the checks without your environment.
|
|
26
|
+
|
|
27
|
+
Run `runner` (`acceptance.py run --packet <packet>`) on the committed candidate before review. It prints `{"result": <path>, "passed": ..., "reasons": [...]}`; listing a wholly clean result (empty `reasons`) lets drain reuse its outcomes. Then write the submission:
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"schema_version": 1,
|
|
32
|
+
"task": "<identity>",
|
|
33
|
+
"source_sha": "<candidate commit on the owned branch>",
|
|
34
|
+
"runner_results": ["<worktree>/.devlyn/loop/runs/<n>/result.json"],
|
|
35
|
+
"reviews": ["<worktree>/.devlyn/reviews/<name>.json"],
|
|
36
|
+
"assumptions": ["<each assumption, recorded once>"],
|
|
37
|
+
"blockers": []
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`schema_version`, `task` and `source_sha` are required. A blocker `{"kind": "failed" | "needs-review", "detail": "<concrete question or cause>"}` records `[F]` with that reason and runs no checks; material ambiguity becomes `needs-review`, never a weakened acceptance. Only when all that stops the task is a cause a later run can clear (network, service, login, usage limit), write no submission: name it in your final message, and the next drain runs the task again; any other blocker is submitted.
|
|
42
|
+
|
|
43
|
+
## Review records
|
|
44
|
+
|
|
45
|
+
Each `review_requirements` ID needs review evidence: a JSON record under `<worktree>/.devlyn/`, listed in the submission's `reviews`, in this shape:
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
{
|
|
49
|
+
"schema_version": 1,
|
|
50
|
+
"kind": "devlyn-review",
|
|
51
|
+
"task": "<identity>",
|
|
52
|
+
"engine": "<engine>",
|
|
53
|
+
"model": "<exact model id>",
|
|
54
|
+
"source_sha": "<reviewed commit>",
|
|
55
|
+
"contract_sha256": "<packet.contract.sha256>",
|
|
56
|
+
"expected_sha256": "<packet.expected.sha256>",
|
|
57
|
+
"requirements": ["R3"],
|
|
58
|
+
"findings": [{"id": "F1", "binding": true, "disposition": "resolved", "requirement": "R3", "summary": "<finding>"}]
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
A record counts only when it binds this task, the candidate source and both contract digests; any other well-formed record is reported under `ignored_reviews` (a source change invalidates earlier reviews). A listed review or runner record that is unreadable or not this shape fails acceptance with its path named; keys a review record or finding adds beyond this shape are ignored. The counted records' `requirements` must cover `review_requirements`. Each finding's `disposition` is `open`, `resolved` or `rejected` (`rejected` needs a `reason`); an open binding finding blocks acceptance. The loop requires no particular engine or number of reviewers.
|
|
@@ -28,7 +28,7 @@ The host supplies the executor and maps its configured engine to the argv; the l
|
|
|
28
28
|
- Local: the first task uses `--local-base <HEAD of the branch the loop was added on>`, the add record's branch, never whichever branch is checked out; that HEAD must descend from the manifest `base_sha`, or the task waits naming it. Instructions committed there before this first allocation are therefore used. Its receipt keeps that start, and a later task with no accepted predecessor reuses it, also after the first task failed; a fixed start is never recalculated. Later tasks use `--from-receipt <latest accepted receipt of the loop>`, which starts from that receipt's exact `source_sha`, never its terminal commit, current HEAD or workspace. Every prerequisite's accepted source must be an ancestor of that frontier; divergent dependencies need a planned integration task, because the driver never merges. A failure leaves the frontier unchanged.
|
|
29
29
|
- `auto`/`pr`: `--start` the refreshed remote base, never an anchor-local commit, so no unpushed anchor commit reaches a task PR; push whatever the plan depends on. A task allocated while that base lacks `docs/specs/<loop-id>/` is the loop's carrier; a carrier that fails is never published, and the next task allocated becomes the carrier. The start must contain each prerequisite's merge commit, checked before allocation and again before every execution or resume. A squash merge needs no original-source ancestry. A refresh failure waits for restored remote access or the base ref. A missing merge before allocation waits for that merge to be restored on the remote base; an allocated baseline is immutable and needs a new loop or verified recovery of a never-executed allocation.
|
|
30
30
|
4. **Commit scoped inputs** on the owned branch before any executor write, only what the allocation base lacks. A base without `docs/specs/<loop-id>/` gets the package directory from the capture with the loop's queue file, its settled rows keeping their marks (receipt-proven `[x]` and `[F]`, prerequisite-blocked `[F]`): a local loop's first task, or an `auto`/`pr` carrier, whose PR lands the package before tasks diverge. Otherwise a local task commits the base's queue file with the loop's receipt-proven terminal rows (typically a predecessor's `[x]`), every other byte unchanged, and an `auto`/`pr` task commits nothing, so its PR changes only its own row and its product. A base that already carries them gets no inputs commit; unrelated product commits stay out.
|
|
31
|
-
5. **Exchange one packet.** Verify the bound release, defaults, packet and worker prompt before execution or recovery; changed or missing resources refuse dispatch. Write `<receipt dir>/packet.json`, then run the executor. An executor that exits without writing its submission leaves its task active, waiting with the exit code and
|
|
31
|
+
5. **Exchange one packet.** Verify the bound release, defaults, packet and worker prompt before execution or recovery; changed or missing resources refuse dispatch. Write `<receipt dir>/packet.json`, then run the executor. An executor that exits without writing its submission leaves its task active, waiting with the exit code and its output files, and the next drain runs it again; one that cannot start keeps its allocation, packet and durable start/not-started log, waits for executable, permissions or workspace repair, and retries on the next drain.
|
|
32
32
|
6. **Derive acceptance** with `acceptance.py accept`. It binds the committed contract to the packet digests; requires the candidate to be the owned branch head with HEAD checked out on that branch, to descend from the inputs, to leave the package (its queue file included) and `docs/specs/queue.md` untouched and to have a clean worktree; executes each declared command on that source, recording raw stdout/stderr and exit, timeout or spawn outcomes (a runner result is reused only when it is wholly clean — no reasons, so no failed check and no source change during its checks — for the same source and contract digests, with unchanged streams); evaluates the file and diff guards against the inputs; confirms the source is unchanged; checks review records; and writes `.devlyn/loop/acceptance.json`. Missing checks, missing review coverage and open binding findings cannot produce `[x]`, and acceptance weakening is not repair. This establishes command outcomes and evidence completeness, not a reviewer's semantic judgment; deliberate evidence forgery is outside the trust model.
|
|
33
33
|
7. **Bind before terminal metadata.** `task-complete.py accept --receipt <receipt> --acceptance <worktree>/.devlyn/loop/acceptance.json` takes evidence custody and points the recovery ref at the source. Failed results are bound the same way; they never become a frontier or a publishable product.
|
|
34
34
|
8. **Commit the terminal transition** under the queue lock: one commit whose sole parent is the bound source and whose only change is this task's row in `docs/specs/<loop-id>/queue.md` (`[x]`, or `[F] — <first reason> (receipt <id>)`), validated as the only legal transition. Then `task-complete.py attach --receipt <receipt> --commit <terminal> --file docs/specs/<loop-id>/queue.md` records it separately from the source and moves the recovery ref to it.
|
|
@@ -55,69 +55,10 @@ Package cleanup, parent pruning and report failures appear as diagnostics under
|
|
|
55
55
|
|
|
56
56
|
## Release binding and read-only consumers
|
|
57
57
|
|
|
58
|
-
`<git-common-dir>/devlyn-loops/harness.json` is authoritative across linked worktrees: `{schemaVersion: 1, release: {package, version, digest, root}, defaults: {path, sha256}}`. Paths are absolute and hashes cover exact bytes. The queue lock covers binding before the first capture. An interrupted first add, unfinished tasks, recovery conflicts and pending delivery retain it; settlement removes it.
|
|
58
|
+
`<git-common-dir>/devlyn-loops/harness.json` is authoritative across linked worktrees: `{schemaVersion: 1, release: {package, version, digest, root}, defaults: {path, sha256}}`. Paths are absolute and hashes cover exact bytes. The queue lock covers binding before the first capture. An interrupted first add, unfinished tasks, recovery conflicts and pending delivery retain it; settlement removes it. No caller substitutes another implementation.
|
|
59
59
|
|
|
60
|
-
`queue.py inspect --repo <dir> --json` reads without cleanup, locks or writes. It returns the status summary plus `binding` (null for legacy) and `compatibility`: `compatible`, `legacy`, `reasons`, `recoveryArgv`, `taskPacket` ranges and `queueBinding` ranges. Incompatible or unreadable state is reported, never migrated.
|
|
61
|
-
|
|
62
|
-
Codex projection writers place `.devlyn-harness-projection.json` at the copied plugin root, beside `skills` and `.codex-plugin`: `{schemaVersion: 1, release: <ReleaseRef>, source: "native/codex/plugins/devlyn"}`. The reader verifies the named release and every projected file’s bytes/mode against that subtree (excluding only the marker). Symlinked skill invocations resolve through their real path. Standalone installs and existing unfinished unbound queues retain legacy behavior; new legacy packets use v2 with a null release, and their `defaults` name the invoking installation's own `_shared/runtime-principles.md` (`source: "skill"`). No engine applicability is inferred from instruction filenames: bound queues use verified release defaults with native instructions taking precedence, except a Devlyn managed block, which the defaults replace. Worker and runner paths point to the bound release, independent of a projection cache’s lifetime.
|
|
60
|
+
`queue.py inspect --repo <dir> --json` reads without cleanup, locks or writes. It returns the status summary plus `binding` (null for legacy) and `compatibility`: `compatible`, `legacy`, `reasons`, `recoveryArgv`, `taskPacket` ranges and `queueBinding` ranges. Incompatible or unreadable state is reported, never migrated. For a mismatch, `recoveryArgv` names the bound queue’s inspect invocation when handoff is allowed, and is null when the refusal names Drain in Devlyn, tasks to finish first, or a repair. Use an exact recovery argv when provided; otherwise follow the refusal.
|
|
63
61
|
|
|
64
62
|
## Executor exchange
|
|
65
63
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
| Field | Meaning |
|
|
69
|
-
|---|---|
|
|
70
|
-
| `task`, `title`, `kind` | Queue identity and deliverable description. |
|
|
71
|
-
| `worktree`, `branch`, `receipt`, `scratch` | Absolute owned worktree, branch, receipt and disposable build directory. |
|
|
72
|
-
| `allocation_base`, `inputs_sha` | Allocation base and the committed-inputs commit. |
|
|
73
|
-
| `contract`, `expected`, `meta` | `{path, sha256}` of the committed task contract, expected acceptance and meta-prompt. |
|
|
74
|
-
| `requirements`, `review_requirements` | Requirement IDs and those needing review evidence. |
|
|
75
|
-
| `shared_constraints` | The meta-prompt's `Constraints and exclusions`. |
|
|
76
|
-
| `dependencies` | `{task, receipt, source_sha}` of each accepted prerequisite. |
|
|
77
|
-
| `schema_version`, `harness` | New packets use version 2 and the bound release reference, or null for legacy unbound queues. Existing v1 packets execute unchanged. |
|
|
78
|
-
| `methodology` | `projectInstructions`: `{path, sha256}` of instruction bytes at the inputs commit. `defaults`: `{source: "release", path, sha256}` from the verified binding; for an unbound queue `{source: "skill", path, sha256}` of the installation's own `_shared/runtime-principles.md`. A task whose executor would get no principles at all (no defaults and no committed CLAUDE.md/AGENTS.md) waits with that reason instead of running. Read the named defaults; explicit user and native owner/project instructions take precedence, except a Devlyn managed block (devlyn:instructions markers), which the defaults replace. No native engine applicability is inferred from filenames. |
|
|
79
|
-
| `workerPrompt` | `{path, sha256}` of the task’s worker instructions, or null for legacy unbound queues. Read it before working. |
|
|
80
|
-
| `delivery` | `local-only`, `auto` or `pr`. |
|
|
81
|
-
| `evidence_dir`, `runner` | Where loop evidence lives (`<worktree>/.devlyn/loop`, ignored by Git) and the runner argv. |
|
|
82
|
-
| `submission`, `obligations` | Where to write the submission, and the completion obligations. |
|
|
83
|
-
|
|
84
|
-
Writable tool caches go under `scratch`, for example `npm_config_cache=<scratch>/npm`: Codex's sandbox keeps caches in HOME read-only.
|
|
85
|
-
|
|
86
|
-
Build in the worktree's default build directory, never one redirected into `scratch` or shared with another checkout: drain re-runs the checks without your environment. Allocation may have warmed it (`build_dirs` in the receipt).
|
|
87
|
-
|
|
88
|
-
Run `runner` (`acceptance.py run --packet <packet>`) on the committed candidate before review. It prints `{"result": <path>, "passed": ..., "reasons": [...]}`; listing a wholly clean result (empty `reasons`) lets drain reuse its outcomes. Then write the submission:
|
|
89
|
-
|
|
90
|
-
```json
|
|
91
|
-
{
|
|
92
|
-
"schema_version": 1,
|
|
93
|
-
"task": "<identity>",
|
|
94
|
-
"source_sha": "<candidate commit on the owned branch>",
|
|
95
|
-
"runner_results": ["<worktree>/.devlyn/loop/runs/<n>/result.json"],
|
|
96
|
-
"reviews": ["<worktree>/.devlyn/reviews/<name>.json"],
|
|
97
|
-
"assumptions": ["<each assumption, recorded once>"],
|
|
98
|
-
"blockers": []
|
|
99
|
-
}
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
`schema_version`, `task` and `source_sha` are required. A blocker `{"kind": "failed" | "needs-review" | "blocked-infrastructure", "detail": "<concrete question or cause>"}` records `[F]` with that reason and runs no checks; material ambiguity becomes `needs-review`, never a weakened acceptance.
|
|
103
|
-
|
|
104
|
-
## Review records
|
|
105
|
-
|
|
106
|
-
Each `review_requirements` ID needs review evidence: a JSON record under `<worktree>/.devlyn/`, listed in the submission's `reviews`, in this shape:
|
|
107
|
-
|
|
108
|
-
```json
|
|
109
|
-
{
|
|
110
|
-
"schema_version": 1,
|
|
111
|
-
"kind": "devlyn-review",
|
|
112
|
-
"task": "<identity>",
|
|
113
|
-
"engine": "<engine>",
|
|
114
|
-
"model": "<exact model id>",
|
|
115
|
-
"source_sha": "<reviewed commit>",
|
|
116
|
-
"contract_sha256": "<packet.contract.sha256>",
|
|
117
|
-
"expected_sha256": "<packet.expected.sha256>",
|
|
118
|
-
"requirements": ["R3"],
|
|
119
|
-
"findings": [{"id": "F1", "binding": true, "disposition": "resolved", "requirement": "R3", "summary": "<finding>"}]
|
|
120
|
-
}
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
A record counts only when it binds this task, the candidate source and both contract digests; any other well-formed record is reported under `ignored_reviews` (a source change invalidates earlier reviews). A listed review or runner record that is unreadable or not this shape fails acceptance with its path named; keys a review record or finding adds beyond this shape are ignored. The counted records' `requirements` must cover `review_requirements`. Each finding's `disposition` is `open`, `resolved` or `rejected` (`rejected` needs a `reason`); an open binding finding blocks acceptance. The loop requires no particular engine or number of reviewers.
|
|
64
|
+
Executor exchange and Review records are in [executor.md](executor.md).
|
|
@@ -103,11 +103,10 @@ Coverage proves that every obligation has evidence. Whether the checks express t
|
|
|
103
103
|
```
|
|
104
104
|
|
|
105
105
|
- Links are repository-relative and exactly `docs/specs/<loop-id>/<task-id>/spec.md`; titles escape `\`, `[` and `]`. Packets and reports carry absolute paths.
|
|
106
|
-
- `[ ]` is pending or active (the receipt tells which). `[x]` is an accepted product. `[F]` is failed,
|
|
106
|
+
- `[ ]` is pending or active (the receipt tells which). `[x]` is an accepted product. `[F]` is failed, prerequisite-blocked or needs-review and ends with ` — <reason> (receipt <id>)`. Delivery state lives in receipts and the drain report; a pending PR never turns `[x]` into `[F]`.
|
|
107
107
|
- The only legal transition turns one pending row into `[x]` or `[F]` with every other byte unchanged, so a terminal commit changes one line of its loop's file. Terminal rows are never reinterpreted or rerun. The blank lines let transitions of adjacent rows merge, and loops never share a file.
|
|
108
108
|
- Identities are unique; equal titles are fine.
|
|
109
109
|
- `docs/specs/queue.md` is the legacy queue. Its raw rows are intents: readable, never executed, never written (a row there that fully matches this grammar is ignored). `add --materialize <line>` plans one pending legacy row as a loop whose `## Intent` reproduces it verbatim (whitespace may reflow); that loop's queue file starts with `<!-- replaces docs/specs/queue.md row (occurrence <n>): <row> -->`, the row's exact text and its occurrence among equal rows, line endings aside. Every checkout, a fresh clone included, then shows the loop's rows in that row's place. A claim matching no row replaces nothing; a row two loops claim (two clones each materializing it) is hidden, neither loop taking its place, so it is never planned again.
|
|
110
|
-
- Status and drain read the legacy rows in file order, each replaced row as its loop's rows; then the other recorded loops in add order, from their captures; then loops without a record here, as in a fresh clone, from the checkout's tracked queue files in loop-id order.
|
|
111
110
|
|
|
112
111
|
## Commands
|
|
113
112
|
|