devrites 7.3.1 → 7.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +38 -0
- package/README.md +19 -3
- package/bin/devrites.mjs +31 -5
- package/docs/cli.md +15 -7
- package/docs/engine/commands.md +37 -5
- package/docs/engine/workspace-schema.md +4 -3
- package/docs/release.md +5 -2
- package/engine/Makefile +2 -2
- package/engine/internal/acceptance/newfinal_origins.go +39 -0
- package/engine/internal/acceptance/preconditions.go +17 -11
- package/engine/internal/acceptance/transcript.go +23 -4
- package/engine/internal/gate/gate.go +8 -6
- package/engine/internal/gate/readiness_binding.go +50 -21
- package/engine/internal/hostpack/hostpack.go +1 -1
- package/engine/internal/install/apply.go +36 -21
- package/engine/internal/install/binary.go +67 -9
- package/engine/internal/install/install.go +63 -21
- package/engine/internal/install/manifest.go +4 -5
- package/engine/internal/install/merge.go +89 -33
- package/engine/internal/install/preflight.go +60 -19
- package/engine/internal/install/uninstall.go +19 -9
- package/engine/internal/install/update.go +0 -4
- package/engine/internal/lib/apply_bundle.go +25 -8
- package/engine/internal/lib/claims.go +170 -4
- package/engine/internal/lib/claims_holder.go +13 -16
- package/engine/internal/lib/closeout.go +52 -28
- package/engine/internal/lib/context_io.go +88 -4
- package/engine/internal/lib/continuation.go +111 -24
- package/engine/internal/lib/dispatch.go +157 -24
- package/engine/internal/lib/final_prerequisites.go +137 -0
- package/engine/internal/lib/manifest_union.go +62 -25
- package/engine/internal/lib/next.go +10 -10
- package/engine/internal/lib/packet.go +32 -8
- package/engine/internal/lib/packet_integrity.go +194 -2
- package/engine/internal/lib/regression.go +0 -5
- package/engine/internal/lib/resolve.go +148 -145
- package/engine/internal/lib/treebaseline.go +0 -1
- package/engine/internal/markdowntext/markdowntext.go +110 -31
- package/engine/internal/overhaul/records/check.go +45 -5
- package/engine/internal/overhaul/records/consistency.go +34 -8
- package/engine/internal/overhaul/records/run.go +55 -11
- package/engine/internal/parallel/landing.go +17 -0
- package/engine/internal/rootfacts/facts.go +11 -0
- package/engine/internal/state/acceptance.go +35 -5
- package/engine/internal/state/feature.go +3 -2
- package/engine/internal/state/lock_other.go +4 -0
- package/engine/internal/state/observation.go +1 -1
- package/engine/internal/state/write.go +56 -39
- package/engine/internal/workflow/commands.go +25 -3
- package/engine/main.go +21 -2
- package/pack/.claude/skills/devrites-lib/reference/parallel-dispatch.md +41 -39
- package/pack/.claude/skills/overhaul/SKILL.md +8 -4
- package/pack/.claude/skills/overhaul/references/lifecycle.md +13 -6
- package/pack/.claude/skills/overhaul/references/orchestration.md +32 -21
- package/pack/.claude/skills/overhaul/references/records.md +23 -8
- package/pack/.claude/skills/overhaul/references/scope-and-safety.md +7 -2
- package/pack/.claude/skills/overhaul/references/scoring.md +7 -0
- package/pack/.claude/skills/rite-build/reference/wright-dispatch.md +17 -17
- package/pack/.claude/skills/rite-clarify/SKILL.md +25 -25
- package/pack/.claude/skills/rite-doctor/SKILL.md +70 -68
- package/pack/generated/README.md +1 -1
- package/pack/generated/claude/skills/devrites-lib/reference/parallel-dispatch.md +41 -39
- package/pack/generated/claude/skills/overhaul/SKILL.md +8 -4
- package/pack/generated/claude/skills/overhaul/references/lifecycle.md +13 -6
- package/pack/generated/claude/skills/overhaul/references/orchestration.md +32 -21
- package/pack/generated/claude/skills/overhaul/references/records.md +23 -8
- package/pack/generated/claude/skills/overhaul/references/scope-and-safety.md +7 -2
- package/pack/generated/claude/skills/overhaul/references/scoring.md +7 -0
- package/pack/generated/claude/skills/rite-build/reference/wright-dispatch.md +17 -17
- package/pack/generated/claude/skills/rite-clarify/SKILL.md +25 -25
- package/pack/generated/claude/skills/rite-doctor/SKILL.md +70 -68
- package/pack/generated/codex/skills/devrites-lib/reference/parallel-dispatch.md +41 -39
- package/pack/generated/codex/skills/overhaul/SKILL.md +8 -4
- package/pack/generated/codex/skills/overhaul/references/lifecycle.md +13 -6
- package/pack/generated/codex/skills/overhaul/references/orchestration.md +32 -21
- package/pack/generated/codex/skills/overhaul/references/records.md +23 -8
- package/pack/generated/codex/skills/overhaul/references/scope-and-safety.md +7 -2
- package/pack/generated/codex/skills/overhaul/references/scoring.md +7 -0
- package/pack/generated/codex/skills/rite-build/reference/wright-dispatch.md +17 -17
- package/pack/generated/codex/skills/rite-clarify/SKILL.md +25 -25
- package/pack/generated/codex/skills/rite-doctor/SKILL.md +70 -68
- package/pack/generated/devin/skills/devrites-lib/reference/parallel-dispatch.md +41 -39
- package/pack/generated/devin/skills/overhaul/SKILL.md +8 -4
- package/pack/generated/devin/skills/overhaul/references/lifecycle.md +13 -6
- package/pack/generated/devin/skills/overhaul/references/orchestration.md +32 -21
- package/pack/generated/devin/skills/overhaul/references/records.md +23 -8
- package/pack/generated/devin/skills/overhaul/references/scope-and-safety.md +7 -2
- package/pack/generated/devin/skills/overhaul/references/scoring.md +7 -0
- package/pack/generated/devin/skills/rite-build/reference/wright-dispatch.md +17 -17
- package/pack/generated/devin/skills/rite-clarify/SKILL.md +25 -25
- package/pack/generated/devin/skills/rite-doctor/SKILL.md +70 -68
- package/pack/generated/dsh/runtime/cordis.patch.yml +28 -14
- package/pack/generated/dsh/skills/devrites-lib/reference/parallel-dispatch.md +41 -39
- package/pack/generated/dsh/skills/overhaul/SKILL.md +8 -4
- package/pack/generated/dsh/skills/overhaul/references/lifecycle.md +13 -6
- package/pack/generated/dsh/skills/overhaul/references/orchestration.md +32 -21
- package/pack/generated/dsh/skills/overhaul/references/records.md +23 -8
- package/pack/generated/dsh/skills/overhaul/references/scope-and-safety.md +7 -2
- package/pack/generated/dsh/skills/overhaul/references/scoring.md +6 -0
- package/pack/generated/dsh/skills/rite-build/reference/wright-dispatch.md +17 -17
- package/pack/generated/dsh/skills/rite-clarify/SKILL.md +25 -25
- package/pack/generated/dsh/skills/rite-doctor/SKILL.md +70 -68
- package/pack/generated/omp/skills/devrites-lib/reference/parallel-dispatch.md +41 -39
- package/pack/generated/omp/skills/devrites-lib/reference/standards/agents.md +1 -1
- package/pack/generated/omp/skills/overhaul/SKILL.md +8 -4
- package/pack/generated/omp/skills/overhaul/references/lifecycle.md +13 -6
- package/pack/generated/omp/skills/overhaul/references/orchestration.md +32 -21
- package/pack/generated/omp/skills/overhaul/references/records.md +23 -8
- package/pack/generated/omp/skills/overhaul/references/scope-and-safety.md +7 -2
- package/pack/generated/omp/skills/overhaul/references/scoring.md +7 -0
- package/pack/generated/omp/skills/rite-build/reference/wright-dispatch.md +18 -18
- package/pack/generated/omp/skills/rite-clarify/SKILL.md +25 -25
- package/pack/generated/omp/skills/rite-doctor/SKILL.md +70 -68
- package/pack/generated/pi/AGENTS.md +1 -1
- package/pack/generated/pi/skills/devrites-lib/reference/parallel-dispatch.md +41 -39
- package/pack/generated/pi/skills/overhaul/SKILL.md +8 -4
- package/pack/generated/pi/skills/overhaul/references/lifecycle.md +13 -6
- package/pack/generated/pi/skills/overhaul/references/orchestration.md +32 -21
- package/pack/generated/pi/skills/overhaul/references/records.md +23 -8
- package/pack/generated/pi/skills/overhaul/references/scope-and-safety.md +7 -2
- package/pack/generated/pi/skills/overhaul/references/scoring.md +7 -0
- package/pack/generated/pi/skills/rite-build/reference/wright-dispatch.md +17 -17
- package/pack/generated/pi/skills/rite-clarify/SKILL.md +25 -25
- package/pack/generated/pi/skills/rite-doctor/SKILL.md +70 -68
- package/package.json +1 -1
- package/scripts/build-host-artifacts.sh +1 -1
- package/scripts/dsh-generate.py +6 -2
- package/scripts/install-lib.sh +29 -0
- package/scripts/omp-generate.sh +4 -4
- package/scripts/pi-generate.sh +3 -3
- package/uninstall.sh +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to DevRites are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and DevRites adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Releases are generated automatically by [semantic-release](https://semantic-release.gitbook.io/) from Conventional Commits on `main`.
|
|
4
4
|
|
|
5
|
+
## [7.3.3](https://github.com/ViktorsBaikers/DevRites/compare/v7.3.2...v7.3.3) (2026-11-02)
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
* **devrites:** harden workflow mutations and host diagnostics ([#206](https://github.com/ViktorsBaikers/DevRites/issues/206)) ([849a7d1](https://github.com/ViktorsBaikers/DevRites/commit/849a7d182c13453f4a542bd2b4c58f9adccebe22))
|
|
10
|
+
|
|
11
|
+
## [7.3.2](https://github.com/ViktorsBaikers/DevRites/compare/v7.3.1...v7.3.2) (2026-11-02)
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
* **devrites:** harden lifecycle, installer and host safety ([#205](https://github.com/ViktorsBaikers/DevRites/issues/205)) ([cac2f8a](https://github.com/ViktorsBaikers/DevRites/commit/cac2f8ab657bbe0d44198ed587413779cf5ac44d))
|
|
16
|
+
|
|
5
17
|
## [7.3.1](https://github.com/ViktorsBaikers/DevRites/compare/v7.3.0...v7.3.1) (2026-11-01)
|
|
6
18
|
|
|
7
19
|
### Fixed
|
|
@@ -25,6 +37,16 @@ All notable changes to DevRites are documented here. The format follows [Keep a
|
|
|
25
37
|
|
|
26
38
|
### Fixed
|
|
27
39
|
|
|
40
|
+
- Confine question resolution to checked workspaces and restore question/cursor
|
|
41
|
+
files together when publication fails. Bundle rollback now includes the failing
|
|
42
|
+
target and reports incomplete restoration.
|
|
43
|
+
- Verify Windows root-handle rename protection in resolution race tests while
|
|
44
|
+
preserving replacement coverage on platforms that permit renaming open roots.
|
|
45
|
+
- Honor explicitly acknowledged backward-phase repair checkpoints in regression
|
|
46
|
+
baselines while retaining ordinary regression and empty-acceptance checks.
|
|
47
|
+
- Check Pi/omp native role loading in doctor, clarify instruction-only shell
|
|
48
|
+
boundaries, and validate OMP invocations plus Codex reference documents.
|
|
49
|
+
|
|
28
50
|
- Recover exact recorded parallel landings after interrupted lease persistence;
|
|
29
51
|
preserve late worker edits, ignored data, extra commits, and missing-checkout
|
|
30
52
|
Git metadata during cleanup.
|
|
@@ -43,6 +65,22 @@ All notable changes to DevRites are documented here. The format follows [Keep a
|
|
|
43
65
|
workspaces or reviving their controller leases.
|
|
44
66
|
- Execute Autocomplete's global frontier at cold Prove/Vet/Review and native wait
|
|
45
67
|
boundaries; preserve frozen candidates, standalone scope, and non-green gates.
|
|
68
|
+
- Preserve selected workspace identity across native command forms, resolver
|
|
69
|
+
resumes, and handoffs; reject empty answers and invalid wait transitions before
|
|
70
|
+
publishing question changes.
|
|
71
|
+
- Keep candidate and next-step observations read-only, and retain unfinished Build
|
|
72
|
+
and Polish work even when their required documents already exist.
|
|
73
|
+
- Confine installer reads and mutations to the validated project root; acquire
|
|
74
|
+
shared-marker ownership before changing configuration, replay disabled-host
|
|
75
|
+
update flags, and reject linked pin destinations outside the project.
|
|
76
|
+
- Preserve uninstall ownership after binary-removal failures so ordinary retries
|
|
77
|
+
work without touching another installation. Stage Windows launcher uninstalls
|
|
78
|
+
outside the installed image; refuse direct self-removal before project changes.
|
|
79
|
+
- Bind Overhaul scorecards to current calculator inputs and admitted receipts to
|
|
80
|
+
immutable generation hashes; require complete writer reservations and
|
|
81
|
+
lane-matched coverage proof.
|
|
82
|
+
- Serialize Overhaul's shared-tree writer observation windows without filtering
|
|
83
|
+
away unauthorized changes; retain parallel read-only review on immutable inputs.
|
|
46
84
|
|
|
47
85
|
## [7.2.8](https://github.com/ViktorsBaikers/DevRites/compare/v7.2.7...v7.2.8) (2026-10-31)
|
|
48
86
|
|
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ history can't lose it. After you clear the conversation, switch hosts, or hand
|
|
|
31
31
|
the feature to another engineer, the next session reads the same plan,
|
|
32
32
|
decisions, and evidence before it continues.
|
|
33
33
|
|
|
34
|
-
**Status:** [`v7.3.
|
|
34
|
+
**Status:** [`v7.3.3`](https://github.com/ViktorsBaikers/DevRites/releases/tag/v7.3.3): see [`CHANGELOG.md`](CHANGELOG.md) for release notes.
|
|
35
35
|
|
|
36
36
|
This is the latest published release; `main` may contain unreleased work.
|
|
37
37
|
|
|
@@ -288,6 +288,12 @@ npx devrites@latest uninstall
|
|
|
288
288
|
npx devrites@latest uninstall --keep-binary
|
|
289
289
|
```
|
|
290
290
|
|
|
291
|
+
On Windows, use `npx devrites@latest uninstall` or the Bash `uninstall.sh`
|
|
292
|
+
launcher to remove the shared engine binary. These launchers run a private
|
|
293
|
+
temporary copy so Windows can delete the original installed executable.
|
|
294
|
+
Direct `devrites-engine uninstall` cannot delete its own running image and
|
|
295
|
+
refuses before changing project files; use the launcher or `--keep-binary`.
|
|
296
|
+
|
|
291
297
|
Run `npx devrites@latest --help` for common flags and
|
|
292
298
|
`npx devrites@latest <command> --help` for command-specific flags.
|
|
293
299
|
|
|
@@ -387,6 +393,10 @@ blocks uninstall and dropped-host pruning, including dry runs, before writes.
|
|
|
387
393
|
Existing files retain their permission bits. Claude permission ownership is
|
|
388
394
|
recorded before settings replacement, so interrupted merges retain the records
|
|
389
395
|
needed to remove installer-added grants on retry and uninstall.
|
|
396
|
+
User-pinned aliases keep their recorded ownership hashes; refreshing does not
|
|
397
|
+
silently adopt local edits. Shared-config marker collisions fail before writes.
|
|
398
|
+
Legacy hook cleanup matches known shipped commands, not user commands that merely
|
|
399
|
+
mention DevRites.
|
|
390
400
|
Marker-merged files keep user content outside the DevRites block, and
|
|
391
401
|
`.devrites/` runtime state stays in place. `update` reuses the install flags
|
|
392
402
|
recorded in the manifest; flags you pass explicitly win and are recorded. The installer refuses symlinks, junctions, and paths that
|
|
@@ -395,8 +405,11 @@ escape the target.
|
|
|
395
405
|
The optional shared `devrites-engine` binary is the only artifact installed
|
|
396
406
|
outside the project. Before replacement, the staged release binary must report
|
|
397
407
|
the requested version; after installation, the binary at its final path must
|
|
398
|
-
report it again in a new process. The
|
|
399
|
-
|
|
408
|
+
report it again in a new process. The selected binary is installed after merge
|
|
409
|
+
preflight but before project payload and version publication, so binary failure
|
|
410
|
+
leaves an ordinary update retry possible. The installer keeps a backup until the
|
|
411
|
+
second check passes and restores it on failure. If restoration also fails, it
|
|
412
|
+
retains the backup and reports its recovery path.
|
|
400
413
|
|
|
401
414
|
`devrites-engine update` resolves the latest stable release, acquires its
|
|
402
415
|
bundle and platform binary, and hands local paths to the downloaded engine, so
|
|
@@ -549,6 +562,9 @@ npm test
|
|
|
549
562
|
(cd engine && go test ./... -count=1)
|
|
550
563
|
```
|
|
551
564
|
|
|
565
|
+
Native Windows launcher integration tests also require Node and Git for Windows
|
|
566
|
+
(including Git Bash). They exercise the real installed engine through both launchers.
|
|
567
|
+
|
|
552
568
|
Canonical pack changes belong in `pack/.claude/`. Rebuild host payloads with
|
|
553
569
|
`bash scripts/build-host-artifacts.sh` instead of editing `pack/generated/`.
|
|
554
570
|
See [`CONTRIBUTING.md`](CONTRIBUTING.md) for the development workflow and
|
package/bin/devrites.mjs
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
// devrites - npx entry point for the engine-owned DevRites installer.
|
|
3
3
|
import { spawn, spawnSync } from 'node:child_process';
|
|
4
4
|
import { createHash } from 'node:crypto';
|
|
5
|
-
import { chmodSync, createReadStream, existsSync, mkdtempSync, readFileSync, rmSync } from 'node:fs';
|
|
5
|
+
import { chmodSync, copyFileSync, createReadStream, existsSync, mkdtempSync, readFileSync, rmSync, statSync } from 'node:fs';
|
|
6
6
|
import { open } from 'node:fs/promises';
|
|
7
7
|
import { constants as osConstants, tmpdir } from 'node:os';
|
|
8
|
-
import { dirname, join } from 'node:path';
|
|
8
|
+
import { dirname, join, resolve } from 'node:path';
|
|
9
9
|
import { fileURLToPath } from 'node:url';
|
|
10
10
|
|
|
11
11
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
@@ -108,6 +108,31 @@ function runEngine(command, args, env) {
|
|
|
108
108
|
});
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
function windowsInstalledEngine() {
|
|
112
|
+
const dirs = (process.env.PATH || '').split(';');
|
|
113
|
+
if (process.env.NoDefaultCurrentDirectoryInExePath === undefined) dirs.unshift(process.cwd());
|
|
114
|
+
for (const dir of dirs) {
|
|
115
|
+
if (!dir) continue;
|
|
116
|
+
const candidate = resolve(dir.replace(/^"|"$/g, ''), 'devrites-engine.exe');
|
|
117
|
+
try {
|
|
118
|
+
if (statSync(candidate, { throwIfNoEntry: false })?.isFile()) return candidate;
|
|
119
|
+
} catch {}
|
|
120
|
+
}
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function stageWindowsUninstall(engine) {
|
|
125
|
+
if (!engine || command !== 'uninstall' || process.platform !== 'win32' || temporaryDirs.has(dirname(engine))) return engine;
|
|
126
|
+
try {
|
|
127
|
+
// Windows cannot unlink the running installed image; launch a private copy.
|
|
128
|
+
const staged = join(privateTempDir(), 'devrites-engine.exe');
|
|
129
|
+
copyFileSync(engine, staged);
|
|
130
|
+
return staged;
|
|
131
|
+
} catch (error) {
|
|
132
|
+
throw new Error(`could not stage devrites-engine for Windows uninstall: ${error.message}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
111
136
|
async function acquireEngine() {
|
|
112
137
|
const repo = process.env.DEVRITES_REPO || 'ViktorsBaikers/DevRites';
|
|
113
138
|
const tag = normalizeReleaseTag(process.env.DEVRITES_REF || pkgVersion());
|
|
@@ -123,7 +148,8 @@ async function acquireEngine() {
|
|
|
123
148
|
acquisitionFailure = `${envEngine} reports version '${got}', want ${tag}`;
|
|
124
149
|
return null;
|
|
125
150
|
}
|
|
126
|
-
|
|
151
|
+
const installedEngine = command === 'uninstall' && process.platform === 'win32' ? windowsInstalledEngine() : 'devrites-engine';
|
|
152
|
+
if (command === 'uninstall' && installedEngine && spawnSync(installedEngine, ['uninstall', '-h'], { stdio: 'ignore' }).status === 0) return installedEngine;
|
|
127
153
|
|
|
128
154
|
// A git checkout's payload can be ahead of the published engine of the same
|
|
129
155
|
// version, so a checkout builds its own engine before downloading one.
|
|
@@ -135,7 +161,7 @@ async function acquireEngine() {
|
|
|
135
161
|
const built = checkout ? null : buildEngine(tag);
|
|
136
162
|
if (built) return built;
|
|
137
163
|
|
|
138
|
-
if (command === 'uninstall') return
|
|
164
|
+
if (command === 'uninstall') return installedEngine;
|
|
139
165
|
const probe = spawnSync('devrites-engine', ['version'], { encoding: 'utf8' });
|
|
140
166
|
if (!probe.error && probe.status === 0 && String(probe.stdout).trim().replace(/^v/, '') === tag.slice(1)) return 'devrites-engine';
|
|
141
167
|
return null;
|
|
@@ -316,7 +342,7 @@ if (first && SUBCOMMAND_ALIASES.has(first)) {
|
|
|
316
342
|
|
|
317
343
|
let engine;
|
|
318
344
|
try {
|
|
319
|
-
engine = await acquireEngine();
|
|
345
|
+
engine = stageWindowsUninstall(await acquireEngine());
|
|
320
346
|
} catch (error) {
|
|
321
347
|
console.error(`devrites: ${error.message}`);
|
|
322
348
|
process.exit(1);
|
package/docs/cli.md
CHANGED
|
@@ -143,11 +143,15 @@ self-contained updater.
|
|
|
143
143
|
against the durable `regression-baseline.json` high-water mark. A lost fact
|
|
144
144
|
prints `REGRESSION: <detail>` and exits `3`. `--update` rewrites the baseline
|
|
145
145
|
under the feature lock at a durable checkpoint and prints each regression it
|
|
146
|
-
blesses as `REGRESSION-BLESSED
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
146
|
+
blesses as `REGRESSION-BLESSED`. A deliberate owner-authorized repair checkpoint
|
|
147
|
+
may accept a backward phase along with other acknowledged facts: the current
|
|
148
|
+
phase becomes the new baseline. Ordinary checks never bless losses or change
|
|
149
|
+
the baseline. If the update would remove every checked acceptance criterion and
|
|
150
|
+
met gate, `--allow-empty` is also required. With no baseline recorded the check
|
|
151
|
+
reports `unproven` and exits `0`. Recommended placement: compare on a cold
|
|
152
|
+
resume or scheduled wake before acting on durable state, ratchet after a landed
|
|
153
|
+
slice checkpoint or phase advance, and treat `BLOCKED` at Seal as NO-GO.
|
|
154
|
+
Never use automatic updates to hide an unreconciled regression.
|
|
151
155
|
- `check continuation [slug]` prints `continuation: required` and
|
|
152
156
|
`invoke: <next_action>` when `.devrites/AFK` is present, `status` is
|
|
153
157
|
`running`, no question cursor is set, no blocking or escalating question is
|
|
@@ -254,8 +258,12 @@ without it any `answered_by:` line in the resolved block, whatever its indentati
|
|
|
254
258
|
`state close` transactionally archives a shipped workspace and clears matching
|
|
255
259
|
`ACTIVE`.
|
|
256
260
|
|
|
257
|
-
|
|
258
|
-
|
|
261
|
+
Mutations share root-safety checks, feature locks, and atomic per-file writes.
|
|
262
|
+
`state resolve` and `state apply-bundle` restore attempted files after returned
|
|
263
|
+
publication errors; `rollback incomplete` identifies files needing inspection
|
|
264
|
+
and recovery. These multi-file operations are not crash-atomic: process
|
|
265
|
+
termination can leave partial updates, and unlocked readers can see intermediate
|
|
266
|
+
state. Inspect both artifacts before resuming after an interrupted publication.
|
|
259
267
|
|
|
260
268
|
The root scans and rechecks `questions.md` to allocate the next unused qid,
|
|
261
269
|
manages Clarify return fields without rewriting unrelated Markdown, charges an
|
package/docs/engine/commands.md
CHANGED
|
@@ -20,7 +20,7 @@ workflow strategy.
|
|
|
20
20
|
| `parallel select --cap <1-10> [--root <dir>] [--slug <controller>] [--from-plan [--workspaces <slug,...>] \| <json-file> \| -]` | Choose a plan-order path-disjoint ready subset, seeded by all retained repository-controller slots and paths. `--from-plan` uses canonical built checkpoints, local and declared workspace producer dependencies, exact write sets and required dependency manifests. The internal `--workspaces` data-source parameter pools recorded forward continuations with current readiness and explicit native-vetted per-slice declarations; blocked/stale/missing candidates print scoped notices without hiding other ready work. Foreign identities print `<workspace>/<slice>`. |
|
|
21
21
|
| `parallel <create\|add\|integrate\|prune\|cleanup\|status\|abort\|lease-read\|lease-write\|lease-clear\|record-green>` | One repository controller owns isolated workers across source workspaces. `create` accepts a single initial worker; `create`/`add --cap N` persist the global admission capacity. Explicit `add --cap N --json -` with stdin `[]` updates capacity only; lowering below active count refuses without canceling workers. A completed retained lease can refill at unchanged control tip. `record-green` and per-slice `integrate --slice <workspace/id> --apply-to-control` target source-qualified lanes (bare IDs mean controller lanes); integration keeps exact ancestry/path/dirty-work checks and lands `WIP(<source>):`. `prune` removes only verified clean integrated checkouts, retaining branches, proof identities, lease rows and controller. Status exposes the retained controller and pending landing; cleanup preserves salvage/unknown work. |
|
|
22
22
|
| `check task-graph <slug>` | Validate `tasks.md` slice dependency graph for cycles, unknown deps, malformed tokens, duplicate IDs, missing `Dependencies`, and `depends_on` mismatch. Prints a non-blocking `ADVISORY size:` line (and records a `size-advisory` metric) for each slice with 12+ Writer allowlist files and no `Size rationale:`. A `Dependencies` ID may carry a `(via <interface>)` suffix (empty or unclosed is a malformed token); prints a non-blocking `ADVISORY edge:` line (and records an `edge-advisory` metric) for each edge with no via, or whose via text is not a case-insensitive substring of the dependency's `Consumes / Produces:`. Prints `schedule: <n> slices in <r> build rounds at cap 10` (the `/rite-build` batch selection replayed over each slice's Writer allowlist, else Files likely touched, plus the manifest parallel select reserves; omitted when a slice has no path) and a non-blocking `ADVISORY hub:` line (top five; records a `hub-advisory` metric) for each path, compared case-folded, that three or more slices without `Serial rationale:` write. Advisories print only when the graph is valid. |
|
|
23
|
-
| `check regression <slug> [--update]` | Compare the workspace's structural progress facts (phase ordinal, checked ACs, met/abandoned gates, done slices, resolved questions, present artifacts) against the recorded `regression-baseline.json` high-water mark
|
|
23
|
+
| `check regression <slug> [--update [--allow-empty]]` | Compare the workspace's structural progress facts (phase ordinal, checked ACs, met/abandoned gates, done slices, resolved questions, present artifacts) against the recorded `regression-baseline.json` high-water mark. Ordinary checks block lost facts without changing the baseline. `--update` records the current facts under the feature lock and prints each blessed regression; a deliberate owner-authorized repair checkpoint can accept a backward phase as the new baseline. Removing every checked AC and met gate also requires `--allow-empty`. Never automatically bless an unreconciled regression. |
|
|
24
24
|
| `check continuation [slug]` | Print whether an armed AFK run must invoke its cursor instead of replying. `continuation: required` plus `invoke: <next_action>` when `.devrites/AFK` is a regular file, `status` is `running`, `question_id` is empty, no blocking or escalating question is open, and `next_action` is an internal phase command (`/rite-spec` through `/rite-seal`, including `/rite-plan`, not `/rite-ship` or `/rite-autocomplete`). Otherwise `continuation: clear` with a one-line `reason`. Exit `0` either way. `handoff` prints the same lines. |
|
|
25
25
|
| `check drift <slug> [--record]` | Attribute readiness-input changes to the exact artifact. `--record` writes the per-input `readiness-inputs.json` digest baseline under the feature lock; without it the workspace diffs current inputs against that baseline (or falls back to the aggregate binding recorded in `eng-review.md`). Advisory: exit `0` on drift; a corrupt baseline exits `3`. A `test-plan.md` change limited to exact `Workflow Artifact reviewed identity: <64hex>` lines is reported as `reviewed-identity-only` (ADR-0034). `--record` also stores each slice's proof bytes (its `tasks.md` block plus the `test-plan.md` lines naming it); both modes print `ADVISORY proportion:` for a slice whose proof grew over 2x since that baseline or carries over 3x the median proof per AC. |
|
|
26
26
|
| `check windows <slug> [--worktree\|--staged\|--base <ref>\|--since-dispatch <wave>]` | Fail on deferral and bar-lowering markers the change introduces without a waiver row in the workspace's `windows.md`: added TODO/FIXME/XXX/HACK, skipped/focused-test and stub idioms, checker suppressions, one-line empty catch; removed `deleted-test` files and net `removed-assertion` lines in remaining test files. `--since-dispatch <wave>` reports only `deleted-test`/`removed-assertion` against the work-tree snapshot (`tree` in `dispatch.json`) recorded when that build wright wave opened, untracked files included; it takes no `--cwd` and records the `windows-since` metric. |
|
|
@@ -38,7 +38,7 @@ workflow strategy.
|
|
|
38
38
|
| `handoff [slug] [--resume]` | Emit the deterministic resume record: cursor fields, `awaiting_human`, open `questions.md` gate kinds, the `gates.md` reduction with unmet/stale/abandoned ids, `decisions.md` dead ends, in-flight dispatch waves (`inflight_wave:` lines: phase, state, pending roles, slices), missing required files, the canonical read-next order, and the same `continuation:` lines as `check continuation`. Read-only unless `--resume`, which appends one `resume` event (role root) to `metrics.jsonl`; `metrics summary` then reports minutes from each resume to the next dispatch. The `/rite-handoff` spine and the compaction-fallback entry point. |
|
|
39
39
|
| `recall --paths <p1,p2,...> [--limit n]` | Read-time query over `.devrites/archive/`: list archived features whose `touched-files.md` candidate manifest covers a queried project-relative path (exact or directory prefix, case-folded), ranked by overlap count, then newest archive-directory mtime, then slug (default 10 features). Each feature prints up to 8 `archive/<slug>/<file>:<line>` pointers, then `+N more`: `decisions.md` `DEC-###` entries and `## Carry-forward constraints` bullets whose `Files:` overlap (or a constraint naming no files), plus `review.md`/`seal.md`/`drift.md` lines that mention a queried path; fenced examples are ignored. Never writes, caches, or indexes (no metric either); symlinked archive entries are refused. An unreadable archive file prints `unreadable:` and the scan continues. Exit `0` (including `recall: no archive`), `1` invalid archive directory, `2` usage. |
|
|
40
40
|
| `context [slug] (--phase <p>\|--skill <name>) [--role <r>] [--slice <SLICE-ID>] [--recheck <inventory>] [--trigger a,b] [--inline-contract] [--whole]` | Emit one deduplicated read-set bundle per phase/role/skill from each skill's `loads:` manifest; `--skill` works without a workspace when `--out` is given. A root `--phase build\|autocomplete` bundle (no `--role`, no `--slice`) defaults to `--slice <active_slice>` while the state.md cursor phase is `build` and active_slice names a slice present in tasks.md (prints `scope: slice SLICE-### (auto from state.md) ...`), unless `.devrites/AFK` `max_parallel` is 2 or more or a parallel lease is `running` (the bundle stays whole); `--whole` opts out. Every bundle opens, after its header comments, with a table of contents: one `L<start>-<end> <section label>` line per `===== label =====` section, giving its exact line range in the bundle file. A bundle without `--role` (the root's own read) gives over-budget artifacts as by-reference indexes; a `--role` bundle leads with the role's agent contract and names the manifest's `rootOnly` paths instead of inlining them. When the contract resolves under `.claude/agents`, `.omp/agents`, or `.codex/agents` (the host loads it as the agent's system prompt), the bundle writes one header line naming its path, size, and SHA-256 instead of the body; other layouts (pi, devin, unknown) and `--inline-contract` (only with `--role`) inline the body, and a missing contract still exits `3`. Inside inlined files, a `--role` bundle replaces each `<!-- root-only -->`…`<!-- /root-only -->` block with a one-line stub naming the omitted heading, and does the same for each `<!-- model-surface -->` block unless the role is `security-auditor`, the feature has an `ai-spec.md`, or `spec.md`/`plan.md`/`tasks.md`/`design-brief.md` are absent or (outside the spec template's `AI-SPEC annex`) name a model, agent, RAG, or tool surface; an unbalanced or headingless marker exits `2`. `--slice` inlines that slice's `tasks.md` block and turns each over-budget artifact, and each artifact over 16 KiB except `spec.md`, into a by-reference index (path, SHA-256, headings, line hits for every ID the block cites); repeat `--slice` for several slices; an unknown slice exits `3`. In phase `build`, a `--role` bundle other than `slice-wright` without `--slice` exits `3` (`unscoped build reviewer bundle`). `--recheck <inventory>` (phase `build`, with `--role` and `--slice`; path relative to the workspace) writes `<phase>-<role>.<slice>.recheck.bundle.md`: the inventory file and `core.md` inline whole, every other non-`rootOnly` pack file becomes a by-reference index (path, SHA-256, headings with line numbers) followed by each section the inventory cites as `<file>.md § <heading>`; workspace handling matches `--slice`. A missing or empty inventory exits `2`. First reviews, full-inventory rechecks, Vet, `/rite-review`, and Seal do not use it. |
|
|
41
|
-
| `dispatch <slug> <open\|start\|seal\|return\|status\|abandon\|attribute>` | Launch-wave barrier for parallel dispatch: seal needs a handle per role, return needs seal; the start that gives the last role its handle seals the wave, and an explicit seal on a sealed wave is a no-op; `open --claim-session <id> --claim <path>... [--claim-ttl <minutes>]` records one file claim for the wave in claims.jsonl and exits `3`, creating no wave and no claim, while a live foreign session holds an overlapping path; the return that completes the wave releases that claim; `abandon --release` releases it on any other host-confirmed terminal path (a stop or a refused launch);
|
|
41
|
+
| `dispatch <slug> <open\|start\|seal\|return\|status\|abandon\|attribute>` | Launch-wave barrier for parallel dispatch: seal needs a handle per role, return needs seal; the start that gives the last role its handle seals the wave, and an explicit seal on a sealed wave is a no-op; `open --claim-session <id> --claim <path>... [--claim-ttl <minutes>]` records one file claim for the wave in claims.jsonl and exits `3`, creating no wave and no claim, while a live foreign session holds an overlapping path; the return that completes the wave releases that claim; `abandon --release` releases it on any other host-confirmed terminal path (a stop or a refused launch); wave IDs are single-use, including after abandonment; a new attempt needs a new ID, and retained overlapping writer claims require explicit release or host-grounded termination rather than TTL expiry or session reuse; claim flags apply to open only and `--release` to abandon only; start/return auto-record metrics, `return --outcome findings|no-findings|gap` records the role's Outcome line, and `abandon` records its reason, so `metrics summary` shows per-role yield and lost waves. Bare `status` prints every open, sealed, or abandoned wave and folds complete waves into one `N complete wave(s) (use --all)` line; `--all` lists every wave; it still exits nonzero while any wave is open or sealed. For a reviewer, auditor, or test-analyst role, `return --result <file>` stores the return verbatim as `packets/<wave>-<role>.return.md` (exit `3` if that file exists), derives the outcome from its `Outcome:`/`Counts:` head (a malformed or inconsistent head records `gap` with a warning, never `no-findings`), exits `3` when an explicit `--outcome` disagrees, and records each `Finding:` row's `role|slice|file|section|severity` key, so `metrics summary` adds per-role `resolution: resolved re-raised open late` counts. A `--result` file that already is that `packets/` path is kept as the stored return. A build wave with a wright role needs `--slice <SLICE-ID>` per slice, and a slice-bound vet wave takes it too; a reviewer-only wave (for example a Build slice review) may take it, uncapped, so its `Finding:` keys name that slice; a reviewer, auditor, or test-analyst wave needs `--packet <file>` and `open` exits `3` on a packet that seeds a conclusion (gate `*-PASS` token, `no-findings`, an `Outcome:`/`Counts:` line, an "already passed" claim, or zero Critical/Important counts) or tells the agent to read an installed `agents/devrites-*.md`/`.toml` role contract (on the line or under a "Read …" heading or label; canonical `pack/` paths excepted), and a wright wave's `--contract` gets the same role-contract check; a Review or Seal reviewer wave also exits `3` unless the packet names an existing non-empty `.diff`/`.patch` file and the manifest covers every checkpointed path; every 5 rounds of either kind on one slice, `open` exits `3` until `--reslice-review <DRIFT-ID>` names an unused `drift.md` `## <DRIFT-ID>` heading for that slice. `open` also warns when an earlier wave for the same slice never returned. Under `.devrites/AFK` max_parallel >= 2, a build wright wave that leaves `parallel select --from-plan` picks unstarted exits `3` (printing the batch, the command and the parallel-batch lifecycle) until evidence.md records `serial: <skipped ids> — <reason>`; a repair round or a running lease slice does not waive those picks, and a reason that only says the skipped slice is repairing, already started, or in a wave does not count. A wave naming two or more slices needs one `--role slice-wright.<SLICE-ID>` per slice; one shared role exits `3`. HITL runs are never width-refused. A build wright `--contract` must name `gates precheck` under `Writer-safe checks:` when gates.md has a `RUN: writer` gate. `open` exits `3` when a `--role` names no pack agent in a project-level install (a `.<lane>` suffix is allowed), and lints a reviewer packet against the wave's roles, so a recheck may name its own role's fingerprint. Root-edit tripwire (ADR-0038): the first `open` records `<feature>/tree-baseline.json`, which maps dirty product paths outside `.devrites/` and `.scratch/` to their mode plus SHA-256, accepted as user work. The return that completes a wright wave (any phase) adds its slice write sets and claim paths. `open` exits `3` with `unattributed root edit: <paths>` while a dirty product path differs from that baseline and no live claim or in-flight wright wave covers it, unless the new wright wave covers the path through `--claim` or a slice write set. `attribute --reason <evidence> --path <path>...` records owner work, gate output, a standalone wright, or a sanctioned root write for flagged paths only. Each finding writes an `unattributed-edit` metrics row. The engine never reverts a file, and a workspace without the baseline is never blocked. |
|
|
42
42
|
| `candidate freeze <slug> --wave <w> (--slice <SLICE-ID> \| --paths <path>...) [--base <ref>] [--worktree <path>]` | Writes packets/<w>.diff (tracked changes vs base, default HEAD, plus untracked files as /dev/null diffs) and packets/<w>.sha256 (base, diff digest, per-file sha256, aggregate check-candidate digest); never replaces a wave's frozen candidate with a different one. `--worktree <path>` (requires `--base`) reads the same path set from a parallel sibling's linked worktree before it lands: the path must be a symlink-free worktree top level that `git worktree list` names, shares the control repository, and is neither a submodule nor the control checkout. It exits `3` on uncommitted path-set changes there and adds a `head <sha>` row naming the worktree HEAD (the transfer commit); packets stay in the control workspace. |
|
|
43
43
|
| `packet <slug> --wave <w> --phase <p> --role <r> [--slice <SLICE-ID>] [--recheck] [--checks <G-ID,...>] [--focus <file>]` | Renders one reviewer/auditor/test-analyst or same-worktree slice-wright by-reference packet (packets/<w>.<role>.md, or packets/<w>.contract.md for a wright) naming the context bundle (the .recheck bundle with `--recheck`) and frozen diff. It copies packets/<w>.sha256 (header rows only when large), lists a wright slice's tasks.md `Tests/proof` as its first writer-safe check, resolves `--checks` through gates.md and approved preflight rows (adding `gates precheck` for `RUN: writer` gates), and accepts only anchored `--focus` rows (a doubt-reviewer's 1-3 row claim is required). It runs the dispatch open lints on a draft and never replaces a prior packet with a refused one (exit `3`). |
|
|
44
44
|
| `metrics record <slug> --phase <p> --event <e> [--role r] [--bytes n] [--note s]` | Append an event to the workspace's `metrics.jsonl` ledger. |
|
|
@@ -46,11 +46,11 @@ workflow strategy.
|
|
|
46
46
|
| `claim <add\|release\|list\|check>` | Advisory session-scoped file claims in `.devrites/claims.jsonl` (append-only). `add` records intent to write a path set with a TTL (1–240 min, default 30); a live claim by another session exits `3` and names the holder. `release` appends a release event owned by the claiming session. `list` shows every unreleased claim (live, expired, dead) plus one `N released (use --all)` count; `--all` includes released rows; a claim whose holding session has provably exited on this host shows `dead` and no longer conflicts. `check` is the read-only preflight for a path set. Coordination aid only — never a substitute for one-writer-per-worktree. |
|
|
47
47
|
| `note <add\|list\|check\|rm> <slug> ...` | Anchored workspace notes in `notes.md`: `add <slug> <subject> <quote> <title> [body]` binds a note to a verbatim quote in one repository file and assigns `NOTE-###`; `list` and `check` regrade each anchor `exact`/`moved`/`stale`/`ambiguous`/`lost` by searching the repo for the quote (dependency and workspace trees excluded); `check --repair` rewrites a `moved` subject to its new file; `rm` deletes by ID. `check seal` refuses a malformed `notes.md` and any non-`exact` anchor. |
|
|
48
48
|
| `gates <scaffold\|status\|run\|reverify\|lint\|precheck\|attest\|abandon> <slug>` | Operate the `gates.md` acceptance ledger: seed per-AC gates, execute runnable gates bound to `test-plan.md` preflight rows, attest manual gates, and reduce to `all-met`/`not-met`/`handoff`/`malformed`. A gate with `INPUTS:` records its files' content digest and is reused by `run` and `reverify` while they are unchanged. `run`/`reverify --transcripts` atomically publishes a private redacted v2 executed transcript, or returns its verified originating record unchanged, under `references/gates/<gate-id>/`; legacy v1/reuse-only logs cannot authorize reuse. `precheck <slug> --worktree <path> [--slice <SLICE-ID>]` runs every `RUN: writer` gate and `check windows` against the wright's worktree (with `--slice`, it prints `SKIP <gate-id>` for a gate whose id only other unbuilt slices name in `tasks.md`, since gate ids reuse `AC-###` ids; a gate no slice names still runs), prints PASS/FAIL per gate id, exits `3` on any FAIL or on a malformed ledger, and writes no ledger, EVIDENCE line or transcript (`check windows` still appends its metric event); its output is never evidence. |
|
|
49
|
-
| `state resolve <qid> "<answer>"` | Resolve an open question and update `questions.md` plus `state.md`
|
|
50
|
-
| `state merge-manifest <slug> [pred...]` | Fold the recorded predecessor chain's manifests into the release candidate manifest. |
|
|
49
|
+
| `state resolve <qid> "<answer>"` | Resolve an open question and update `questions.md` plus `state.md` under the feature lock, with rollback on returned publication errors. Per-file writes are atomic; the pair is not crash-atomic. Incomplete rollback requires inspection and recovery. |
|
|
50
|
+
| `state merge-manifest <slug> [pred...]` | Fold the complete recorded predecessor chain's manifests into the release candidate manifest. Numbered sequences must reach position 1 through consecutive, unique positive positions; explicit predecessor lists cannot omit recorded members. Fully unnumbered legacy chains remain supported. Invalid or missing members leave the manifest unchanged. |
|
|
51
51
|
| `state set <slug> <key>=<value>... [--unset <key>]...` | Write `state.md` cursor fields all-or-nothing under the feature lock. Refuses an unknown `phase`, the engine-owned `schema` row, a pipe or line break in a value, and an overwrite of a saved `return_phase`/`return_next_action`. Setting `status=awaiting_human` under `.devrites/AFK` prints each open question the sentinel's `allow_gates` auto-picks. |
|
|
52
52
|
| `state append <slug> <ledger.md> --heading "<text>" --body-file <path\|->` | Append one `## <heading>` section to `assumptions.md`, `decisions.md`, `drift.md`, `evidence.md`, or `questions.md` atomically. Refuses a heading that already exists outside fenced code and a body line that opens another `#`/`##` section; for a new `questions.md` `q-` entry, also refuses a non-open status, a missing or unknown gate, a raised_at that is not a full RFC 3339 timestamp, options outside 2-4 concrete choices plus the closing "Something else" escape hatch, `(Recommended)` on any option but 1, and `proposed:` without a `(Recommended)` option 1 (or option 1 without `proposed:`); question or entry length is advisory only; reports an advisory when the ledger passes its line budget, and blocks (exit 3, nothing written) when a ledger other than evidence.md would land at or above 1.5x its line or byte budget without a covering `Budget override: <=N lines; <reason>` (N at most 2x the line budget). |
|
|
53
|
-
| `state close <slug>` | Archive a
|
|
53
|
+
| `state close <slug>` | Archive a workspace and clear matching `ACTIVE`. Final phases/statuses enforce Seal structure, candidate freshness, release-union integrity, retained reviewer settlement, and runnable gate origins before mutation. Explicit nonfinal exits remain physical archival, not proof of delivery. |
|
|
54
54
|
| `migrate <slug> [--dry-run] [--answer id=choice]` | Normalize a pre-v5 workspace to the current schema; fail-closed, one-shot. |
|
|
55
55
|
| `secret-scan [--staged] [--stdin] [slug]` | Scan exact staged blobs, stdin, or touched regular files for credential material. |
|
|
56
56
|
| `open-visual <path-or-name> [--slug <slug>] [--no-open]` | Resolve a local visual HTML file, optionally open it in the OS browser, warn if the sibling outline is missing or inventory ids are absent from HTML, and print agent path tips. No network. |
|
|
@@ -65,6 +65,38 @@ The full label remains in bundle filenames and attribution. Reviewer
|
|
|
65
65
|
`--outcome findings|no-findings|gap`; omitting both refuses the return without
|
|
66
66
|
changing the wave or releasing its claim. Non-reviewer returns are unchanged.
|
|
67
67
|
|
|
68
|
+
Generated packets bind an immutable wave/role context snapshot and its actual
|
|
69
|
+
SHA-256, not a mutable generic or recheck bundle. Workspace overrides retain their
|
|
70
|
+
validated metadata namespace; foreign paths, escapes, and linked context parents
|
|
71
|
+
cannot replace that authority.
|
|
72
|
+
|
|
73
|
+
Seal and final closeout require settled native accounts for every launched role;
|
|
74
|
+
open/sealed proof, custom, and writer roles block too. At least one actual reviewer
|
|
75
|
+
account remains required. Abandoned proof/reviewer accounts need a genuinely newer
|
|
76
|
+
settled account with the same canonical phase, role, and exact unordered slice set,
|
|
77
|
+
a distinct native handle, and valid start/return times. An abandoned writer or
|
|
78
|
+
claim-holder instead needs its actual native returns or host-confirmed termination
|
|
79
|
+
recorded by `dispatch abandon --release`; replacement work alone cannot prove death.
|
|
80
|
+
That command records `terminal_at` even for a claimless or refused launch, without
|
|
81
|
+
fabricating returned work or review approval. A failed claim release cannot stamp it.
|
|
82
|
+
An empty, missing, or malformed dispatch ledger cannot establish settlement.
|
|
83
|
+
Every met runnable gate must retain its approved, bound execution origin, including
|
|
84
|
+
complete v2 metadata/footer and matching body bytes when unredacted and untruncated.
|
|
85
|
+
These final checks do not rerun proof commands.
|
|
86
|
+
|
|
87
|
+
Feature mutation locks live at `.devrites/.locks/features/<slug>/.lock`, outside
|
|
88
|
+
movable workspace directories, and remain held through archival and `ACTIVE`
|
|
89
|
+
updates. A closed slug cannot recreate a work directory through a stale writer.
|
|
90
|
+
Stop existing controllers before upgrading, then restart them together; old and
|
|
91
|
+
new lock protocols must not run concurrently.
|
|
92
|
+
|
|
93
|
+
Vet readiness identities bind normalized `gates.md` definitions: gate membership,
|
|
94
|
+
IDs, titles, `CHECK`, `EXPECT`, `CWD`, `INPUTS`, and `RUN`. Checkbox, evidence, and
|
|
95
|
+
progress changes do not invalidate Vet; changing an oracle or converting a
|
|
96
|
+
runnable gate to manual requires Plan repair and re-Vet. Older identities that
|
|
97
|
+
omitted these definitions require normal re-Vet after upgrade, never a manual
|
|
98
|
+
hash refresh or inferred approval.
|
|
99
|
+
|
|
68
100
|
`help`, `-h`, and `--help` print this operational inventory. Each operational
|
|
69
101
|
command and subcommand also accepts `-h` / `--help` and prints that command's
|
|
70
102
|
usage to stdout without requiring a workspace. `version` and `--version` print
|
|
@@ -157,8 +157,9 @@ not runtime authorities. See
|
|
|
157
157
|
|
|
158
158
|
- New question IDs are `q-YYYY-MM-DD-NNN`. The root scans all same-day headers,
|
|
159
159
|
chooses the next unused suffix, re-reads immediately before append, and
|
|
160
|
-
recomputes on collision. `state resolve` remains the
|
|
161
|
-
writer; it does not allocate IDs.
|
|
160
|
+
recomputes on collision. `state resolve` remains the locked, rollback-protected
|
|
161
|
+
answer/drop/batch writer; it does not allocate IDs. Paired file updates are not
|
|
162
|
+
a crash-atomic transaction.
|
|
162
163
|
- The root directly owns later-phase Clarify entry/restore cursor edits and AFK
|
|
163
164
|
slice accounting, preserving unrelated Markdown. See
|
|
164
165
|
[`state-schema.md`](state-schema.md).
|
|
@@ -187,7 +188,7 @@ The engine provides only:
|
|
|
187
188
|
- `check seal`: final file completeness and open-human-gate checks, the
|
|
188
189
|
`tasks.md` slice graph, then the readiness-binding recheck, then exact
|
|
189
190
|
candidate bindings after that aggregate gate passes;
|
|
190
|
-
-
|
|
191
|
+
- locked, rollback-protected `state resolve` answer/drop/batch and transactional `state close`;
|
|
191
192
|
- secret scanning, version reporting, and local install lifecycle primitives.
|
|
192
193
|
|
|
193
194
|
A marker or file can satisfy structural presence without proving semantic
|
package/docs/release.md
CHANGED
|
@@ -24,8 +24,11 @@ version and run these steps:
|
|
|
24
24
|
|
|
25
25
|
The release archive
|
|
26
26
|
consumes the validated generated files from the same Git index as the rest of
|
|
27
|
-
its payload.
|
|
28
|
-
|
|
27
|
+
its payload. Prior build outputs under `engine/dist/` remain excluded even when
|
|
28
|
+
indexed.
|
|
29
|
+
|
|
30
|
+
Package and release installs validate and copy
|
|
31
|
+
`pack/generated/{claude,codex,omp,pi,devin,dsh}`. When a shell install or update shim runs from a
|
|
29
32
|
source checkout whose generated payload is incomplete, the shim may regenerate
|
|
30
33
|
the missing host payload before handing that local candidate to the engine. The
|
|
31
34
|
engine itself only validates and copies host payloads; it never generates them.
|
package/engine/Makefile
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
BINARY := devrites
|
|
2
2
|
DIST := dist
|
|
3
|
-
# darwin/amd64 kept for Intel Macs; the
|
|
4
|
-
PLATFORMS := darwin/arm64 darwin/amd64 linux/amd64 linux/arm64 windows/amd64
|
|
3
|
+
# darwin/amd64 kept for Intel Macs; the six targets the release matrix ships.
|
|
4
|
+
PLATFORMS := darwin/arm64 darwin/amd64 linux/amd64 linux/arm64 windows/amd64 windows/arm64
|
|
5
5
|
|
|
6
6
|
.PHONY: build test test-race vet quality govulncheck lint cover crosscompile clean
|
|
7
7
|
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
package acceptance
|
|
2
|
+
|
|
3
|
+
import "fmt"
|
|
4
|
+
|
|
5
|
+
// CheckRetainedOrigins checks current runnable met gates without executing commands.
|
|
6
|
+
// Historical candidate labels remain valid when the definition and INPUTS match.
|
|
7
|
+
func CheckRetainedOrigins(root, slug string) error {
|
|
8
|
+
work, ledgerPath, testPlanPath, project, err := featurePaths(root, slug)
|
|
9
|
+
if err != nil {
|
|
10
|
+
return err
|
|
11
|
+
}
|
|
12
|
+
text, err := ReadLedgerFile(ledgerPath)
|
|
13
|
+
if err != nil {
|
|
14
|
+
return err
|
|
15
|
+
}
|
|
16
|
+
doc := ParseLedger(text)
|
|
17
|
+
if len(doc.Errors) != 0 {
|
|
18
|
+
return fmt.Errorf("retained gate origins: invalid %s: %v", GatesFile, doc.Errors)
|
|
19
|
+
}
|
|
20
|
+
approved := loadTestPlan(testPlanPath)
|
|
21
|
+
for _, gate := range doc.Gates {
|
|
22
|
+
if gate.Check == "" || GateState(gate, doc) != StateMet {
|
|
23
|
+
continue
|
|
24
|
+
}
|
|
25
|
+
if !Approved(gate.Check, gate.Cwd, approved) {
|
|
26
|
+
return fmt.Errorf("gate %s: CHECK/CWD is not approved by current test-plan.md; restore the approved definition and record its passing origin", gate.ID)
|
|
27
|
+
}
|
|
28
|
+
if gate.Inputs != "" {
|
|
29
|
+
inputs, err := InputsDigest(project, gate.Inputs)
|
|
30
|
+
if err != nil || inputs == "" || inputs != recordedInputs(gate) {
|
|
31
|
+
return fmt.Errorf("gate %s: retained origin does not match current INPUTS; record a passing approved check against current inputs", gate.ID)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if passingOrigin(work, gate) == "" {
|
|
35
|
+
return fmt.Errorf("gate %s: no usable retained passing origin; record the approved check with gates run --transcripts", gate.ID)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return nil
|
|
39
|
+
}
|
|
@@ -44,13 +44,20 @@ func PreflightRowsPassed(root, slug string, rows []PreflightRow) (bool, error) {
|
|
|
44
44
|
return true, nil
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
// PreflightPassed
|
|
48
|
-
//
|
|
49
|
-
// candidate but the lookup does not compare it: Vet writes workspace artifacts
|
|
50
|
-
// after the run, and only a change to the row's command or cwd retires a pass.
|
|
47
|
+
// PreflightPassed requires the newest transcript to be a complete passing record
|
|
48
|
+
// for this row. Historical candidate labels do not retire an unchanged row.
|
|
51
49
|
func PreflightPassed(work string, row PreflightRow) bool {
|
|
52
|
-
dir :=
|
|
53
|
-
|
|
50
|
+
dir, err := transcriptDirectory(work, PreflightGateID(row), false)
|
|
51
|
+
if err != nil {
|
|
52
|
+
return false
|
|
53
|
+
}
|
|
54
|
+
defer func() { _ = dir.Close() }()
|
|
55
|
+
listing, err := dir.Open(".")
|
|
56
|
+
if err != nil {
|
|
57
|
+
return false
|
|
58
|
+
}
|
|
59
|
+
entries, err := listing.ReadDir(-1)
|
|
60
|
+
_ = listing.Close()
|
|
54
61
|
if err != nil {
|
|
55
62
|
return false
|
|
56
63
|
}
|
|
@@ -67,11 +74,10 @@ func PreflightPassed(work string, row PreflightRow) bool {
|
|
|
67
74
|
if newest == "" {
|
|
68
75
|
return false
|
|
69
76
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
return err == nil && strings.Contains(header, "\nRESULT: pass\n")
|
|
77
|
+
body, err := readTranscript(dir, newest)
|
|
78
|
+
gate := &Gate{ID: PreflightGateID(row), Title: row.Gate, Check: row.Command, Cwd: row.Cwd,
|
|
79
|
+
Expect: "exit 0 (Expected: " + valueOr(row.Expected, "unstated") + ")"}
|
|
80
|
+
return err == nil && validTranscript(string(body), gate, nil)
|
|
75
81
|
}
|
|
76
82
|
|
|
77
83
|
// RunPreconditions is `check preconditions <slug> [--slice S]`: it executes
|
|
@@ -236,6 +236,11 @@ func readTranscript(dir *os.Root, name string) ([]byte, error) {
|
|
|
236
236
|
}
|
|
237
237
|
|
|
238
238
|
func validOrigin(raw string, gate *Gate, evidence []string) bool {
|
|
239
|
+
return validTranscript(raw, gate, evidence)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// validTranscript validates complete executed records; evidence additionally binds ledger proof.
|
|
243
|
+
func validTranscript(raw string, gate *Gate, evidence []string) bool {
|
|
239
244
|
const footer = "TRANSCRIPT_SHA256: "
|
|
240
245
|
end := strings.LastIndex(raw, footer)
|
|
241
246
|
if end < 0 || raw[end:] != footer+OutputDigest(raw[:end])+"\n" {
|
|
@@ -261,14 +266,25 @@ func validOrigin(raw string, gate *Gate, evidence []string) bool {
|
|
|
261
266
|
for key, want := range map[string]string{
|
|
262
267
|
"GATE": transcriptMetadata(gate.ID), "TITLE": transcriptMetadata(gate.Title),
|
|
263
268
|
"COMMAND": transcriptMetadata(gate.Check), "CWD": transcriptMetadata(valueOr(gate.Cwd, ".")),
|
|
264
|
-
"EXPECT": transcriptMetadata(gate.Expect), "DEFINITION_SHA256":
|
|
265
|
-
"INPUTS": transcriptMetadata(valueOr(gate.Inputs, "none")),
|
|
266
|
-
"RESULT": "pass", "EXIT": "0",
|
|
269
|
+
"EXPECT": transcriptMetadata(gate.Expect), "DEFINITION_SHA256": DefinitionDigest(gate),
|
|
270
|
+
"INPUTS": transcriptMetadata(valueOr(gate.Inputs, "none")),
|
|
271
|
+
"RESULT": "pass", "EXIT": "0",
|
|
267
272
|
} {
|
|
268
273
|
if got, exists := fields[key]; !exists || got != want {
|
|
269
274
|
return false
|
|
270
275
|
}
|
|
271
276
|
}
|
|
277
|
+
if evidence != nil && (fields["DEFINITION_SHA256"] != evidence[1] || fields["OUTPUT_SHA256"] != evidence[2] ||
|
|
278
|
+
fields["OUTPUT_BYTES"] != evidence[3] || fields["INPUTS_SHA256"] != valueOr(evidence[4], "none")) {
|
|
279
|
+
return false
|
|
280
|
+
}
|
|
281
|
+
if fields["INPUTS_SHA256"] != "none" && !transcriptDigestRE.MatchString(fields["INPUTS_SHA256"]) {
|
|
282
|
+
return false
|
|
283
|
+
}
|
|
284
|
+
bytes, err := strconv.ParseInt(fields["OUTPUT_BYTES"], 10, 64)
|
|
285
|
+
if err != nil || bytes < 0 || !transcriptDigestRE.MatchString(fields["OUTPUT_SHA256"]) {
|
|
286
|
+
return false
|
|
287
|
+
}
|
|
272
288
|
started, err := time.Parse(time.RFC3339Nano, fields["STARTED"])
|
|
273
289
|
if err != nil || started.IsZero() {
|
|
274
290
|
return false
|
|
@@ -286,7 +302,10 @@ func validOrigin(raw string, gate *Gate, evidence []string) bool {
|
|
|
286
302
|
return false
|
|
287
303
|
}
|
|
288
304
|
truncated, err := strconv.ParseBool(fields["TRUNCATED"])
|
|
289
|
-
|
|
305
|
+
if err != nil || len(output) > maxTranscriptOutput || (truncated && len(output) != maxTranscriptOutput) {
|
|
306
|
+
return false
|
|
307
|
+
}
|
|
308
|
+
return redactions != 0 || truncated || (bytes == int64(len(output)) && fields["OUTPUT_SHA256"] == OutputDigest(output))
|
|
290
309
|
}
|
|
291
310
|
|
|
292
311
|
func exitLabel(outcome RunOutcome) string {
|
|
@@ -316,23 +316,25 @@ var (
|
|
|
316
316
|
specAcceptanceClaimRE = regexp.MustCompile(`(?im)^\s{0,3}#{1,6}\s+.*acceptance criteria|\bAC-\d+\b`)
|
|
317
317
|
)
|
|
318
318
|
|
|
319
|
-
// specClaimsAcceptance reports whether
|
|
319
|
+
// specClaimsAcceptance reports whether spec prose looks
|
|
320
320
|
// like it carries acceptance criteria, so a parse that finds none is a defect
|
|
321
321
|
// in the spec rather than a spec with nothing to map.
|
|
322
322
|
func specClaimsAcceptance(spec []byte) bool {
|
|
323
|
-
structural, err := markdowntext.
|
|
323
|
+
structural, err := markdowntext.Prose(spec)
|
|
324
324
|
return err == nil && specAcceptanceClaimRE.Match(structural)
|
|
325
325
|
}
|
|
326
326
|
|
|
327
|
-
// specUnreadAcceptanceProblems names acceptance text the parser skips:
|
|
328
|
-
//
|
|
329
|
-
// canonical one, which would otherwise escape the coverage check.
|
|
327
|
+
// specUnreadAcceptanceProblems names acceptance text the parser skips:
|
|
328
|
+
// noncanonical ids/headings and declarations outside the canonical section.
|
|
330
329
|
func specUnreadAcceptanceProblems(spec []byte) []string {
|
|
331
|
-
structural, err := markdowntext.
|
|
330
|
+
structural, err := markdowntext.Prose(spec)
|
|
332
331
|
if err != nil {
|
|
333
332
|
return nil
|
|
334
333
|
}
|
|
335
334
|
var problems []string
|
|
335
|
+
for _, id := range state.AcceptanceDeclarationsOutsideSection(spec) {
|
|
336
|
+
problems = append(problems, fmt.Sprintf("spec.md declares %s outside the first `## Acceptance criteria` section, so the gate does not check it", id))
|
|
337
|
+
}
|
|
336
338
|
seen := map[string]bool{}
|
|
337
339
|
readHeading := false
|
|
338
340
|
for _, line := range strings.Split(string(structural), "\n") {
|