vdelta 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +43 -7
- package/dist/adapters/vitest/recorder.js +11 -3
- package/dist/adapters/vitest/recorder.js.map +1 -1
- package/dist/adapters/vitest/reporter.js +13 -7
- package/dist/adapters/vitest/reporter.js.map +1 -1
- package/dist/canonical.js.map +1 -1
- package/dist/cli-io.d.ts +10 -0
- package/dist/cli-io.js +48 -0
- package/dist/cli-io.js.map +1 -0
- package/dist/cli.js +122 -31
- package/dist/cli.js.map +1 -1
- package/dist/compare.d.ts +21 -3
- package/dist/compare.js +128 -25
- package/dist/compare.js.map +1 -1
- package/dist/gate.d.ts +7 -1
- package/dist/gate.js +11 -6
- package/dist/gate.js.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.js +6 -6
- package/dist/index.js.map +1 -1
- package/dist/redact.js +4 -1
- package/dist/redact.js.map +1 -1
- package/dist/render.js +12 -2
- package/dist/render.js.map +1 -1
- package/dist/run.d.ts +6 -1
- package/dist/run.js +129 -15
- package/dist/run.js.map +1 -1
- package/dist/schema.d.ts +12 -0
- package/dist/schema.js +127 -19
- package/dist/schema.js.map +1 -1
- package/dist/store.d.ts +159 -5
- package/dist/store.js +353 -10
- package/dist/store.js.map +1 -1
- package/dist/tree-digest.js +51 -14
- package/dist/tree-digest.js.map +1 -1
- package/package.json +6 -3
- package/spec/veridelta-1.md +58 -4
package/dist/tree-digest.js
CHANGED
|
@@ -5,19 +5,31 @@
|
|
|
5
5
|
* excluding committed-gitignore'd paths, computed against a dedicated
|
|
6
6
|
* throwaway index seeded from HEAD, with host-dependent git settings pinned.
|
|
7
7
|
* Rendered as a bare 40-hex OID (documented deviation from sha256:).
|
|
8
|
+
*
|
|
9
|
+
* treeDigest() is read-only with respect to the observed repo: all git
|
|
10
|
+
* object writes (blobs from `add -A`, the tree from `write-tree`) are
|
|
11
|
+
* redirected to a throwaway GIT_OBJECT_DIRECTORY under tmpdir(), while the
|
|
12
|
+
* repo's real objects (resolved via `--git-path objects`, which also
|
|
13
|
+
* handles linked worktrees whose objects live in the common dir) are made
|
|
14
|
+
* available for reads through GIT_ALTERNATE_OBJECT_DIRECTORIES. The
|
|
15
|
+
* throwaway directory is removed again once the digest has been computed.
|
|
8
16
|
*/
|
|
9
17
|
import { execFile } from 'node:child_process';
|
|
10
|
-
import { promisify } from 'node:util';
|
|
11
18
|
import { randomUUID } from 'node:crypto';
|
|
12
|
-
import { rm } from 'node:fs/promises';
|
|
19
|
+
import { mkdir, rm } from 'node:fs/promises';
|
|
13
20
|
import { tmpdir } from 'node:os';
|
|
14
|
-
import { join } from 'node:path';
|
|
21
|
+
import { delimiter, join } from 'node:path';
|
|
22
|
+
import { promisify } from 'node:util';
|
|
15
23
|
const execFileP = promisify(execFile);
|
|
16
24
|
const PIN = [
|
|
17
|
-
'-c',
|
|
18
|
-
'
|
|
19
|
-
'-c',
|
|
20
|
-
'
|
|
25
|
+
'-c',
|
|
26
|
+
'core.autocrlf=false',
|
|
27
|
+
'-c',
|
|
28
|
+
'core.eol=lf',
|
|
29
|
+
'-c',
|
|
30
|
+
'core.excludesFile=/dev/null',
|
|
31
|
+
'-c',
|
|
32
|
+
'advice.addEmbeddedRepo=false',
|
|
21
33
|
];
|
|
22
34
|
async function git(worktree, args, env) {
|
|
23
35
|
const { stdout } = await execFileP('git', [...PIN, '-C', worktree, ...args], {
|
|
@@ -28,8 +40,24 @@ async function git(worktree, args, env) {
|
|
|
28
40
|
}
|
|
29
41
|
export async function treeDigest(worktree) {
|
|
30
42
|
const idx = join(tmpdir(), `vd-idx-${randomUUID()}`);
|
|
31
|
-
const
|
|
43
|
+
const objDir = join(tmpdir(), `vd-obj-${randomUUID()}`);
|
|
32
44
|
try {
|
|
45
|
+
await mkdir(objDir, { recursive: true });
|
|
46
|
+
const realObjects = await git(worktree, [
|
|
47
|
+
'rev-parse',
|
|
48
|
+
'--path-format=absolute',
|
|
49
|
+
'--git-path',
|
|
50
|
+
'objects',
|
|
51
|
+
]);
|
|
52
|
+
const existingAlternates = process.env.GIT_ALTERNATE_OBJECT_DIRECTORIES;
|
|
53
|
+
const alternates = existingAlternates
|
|
54
|
+
? `${realObjects}${delimiter}${existingAlternates}`
|
|
55
|
+
: realObjects;
|
|
56
|
+
const env = {
|
|
57
|
+
GIT_INDEX_FILE: idx,
|
|
58
|
+
GIT_OBJECT_DIRECTORY: objDir,
|
|
59
|
+
GIT_ALTERNATE_OBJECT_DIRECTORIES: alternates,
|
|
60
|
+
};
|
|
33
61
|
let hasHead = true;
|
|
34
62
|
try {
|
|
35
63
|
await git(worktree, ['rev-parse', '--verify', '-q', 'HEAD']);
|
|
@@ -38,16 +66,17 @@ export async function treeDigest(worktree) {
|
|
|
38
66
|
hasHead = false;
|
|
39
67
|
}
|
|
40
68
|
if (hasHead) {
|
|
41
|
-
await git(worktree, ['read-tree', 'HEAD'],
|
|
69
|
+
await git(worktree, ['read-tree', 'HEAD'], env);
|
|
42
70
|
}
|
|
43
71
|
else {
|
|
44
|
-
await git(worktree, ['read-tree', '--empty'],
|
|
72
|
+
await git(worktree, ['read-tree', '--empty'], env);
|
|
45
73
|
}
|
|
46
|
-
await git(worktree, ['add', '-A'],
|
|
47
|
-
return await git(worktree, ['write-tree'],
|
|
74
|
+
await git(worktree, ['add', '-A'], env);
|
|
75
|
+
return await git(worktree, ['write-tree'], env);
|
|
48
76
|
}
|
|
49
77
|
finally {
|
|
50
78
|
await rm(idx, { force: true });
|
|
79
|
+
await rm(objDir, { recursive: true, force: true });
|
|
51
80
|
}
|
|
52
81
|
}
|
|
53
82
|
export async function gitHead(worktree) {
|
|
@@ -78,7 +107,11 @@ export async function gitRepoRoot(dir) {
|
|
|
78
107
|
/** Resolve a ref to {commitSha, treeOid}; null when unresolvable. */
|
|
79
108
|
export async function resolveRef(worktree, ref) {
|
|
80
109
|
try {
|
|
81
|
-
const commit = await git(worktree, [
|
|
110
|
+
const commit = await git(worktree, [
|
|
111
|
+
'rev-parse',
|
|
112
|
+
'--verify',
|
|
113
|
+
`${ref}^{commit}`,
|
|
114
|
+
]);
|
|
82
115
|
const tree = await git(worktree, ['rev-parse', '--verify', `${ref}^{tree}`]);
|
|
83
116
|
return { commit, tree };
|
|
84
117
|
}
|
|
@@ -89,7 +122,11 @@ export async function resolveRef(worktree, ref) {
|
|
|
89
122
|
/** Deterministic digest material for the dirty state (status + diff vs HEAD). */
|
|
90
123
|
export async function dirtyDiffMaterial(worktree) {
|
|
91
124
|
try {
|
|
92
|
-
const status = await git(worktree, [
|
|
125
|
+
const status = await git(worktree, [
|
|
126
|
+
'status',
|
|
127
|
+
'--porcelain=v1',
|
|
128
|
+
'--untracked-files=all',
|
|
129
|
+
]);
|
|
93
130
|
let diff = '';
|
|
94
131
|
if ((await gitHead(worktree)) !== null) {
|
|
95
132
|
diff = await git(worktree, ['diff', 'HEAD', '--no-color']);
|
package/dist/tree-digest.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tree-digest.js","sourceRoot":"","sources":["../src/tree-digest.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"tree-digest.js","sourceRoot":"","sources":["../src/tree-digest.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vdelta",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Reference implementation of veridelta/1 — proof-carrying verification deltas for coding-agent development loops",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -35,17 +35,20 @@
|
|
|
35
35
|
"spec"
|
|
36
36
|
],
|
|
37
37
|
"engines": {
|
|
38
|
-
"node": ">=
|
|
38
|
+
"node": ">=22"
|
|
39
39
|
},
|
|
40
40
|
"scripts": {
|
|
41
41
|
"build": "tsc -p tsconfig.build.json",
|
|
42
|
-
"
|
|
42
|
+
"lint": "biome check .",
|
|
43
|
+
"lint:fix": "biome check --write .",
|
|
44
|
+
"prepublishOnly": "npm run lint && npm run build && npm run test",
|
|
43
45
|
"test": "vitest run",
|
|
44
46
|
"test:unit": "vitest run --project unit",
|
|
45
47
|
"test:conformance": "vitest run --project conformance",
|
|
46
48
|
"typecheck": "tsc --noEmit"
|
|
47
49
|
},
|
|
48
50
|
"devDependencies": {
|
|
51
|
+
"@biomejs/biome": "^2.5.4",
|
|
49
52
|
"@types/node": "^24.0.0",
|
|
50
53
|
"typescript": "^5.9.0",
|
|
51
54
|
"vitest": "^4.0.0"
|
package/spec/veridelta-1.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|---|---|
|
|
5
5
|
| **Status** | Draft |
|
|
6
6
|
| **Schema identifier** | `veridelta/1` |
|
|
7
|
-
| **Spec revision** | 0.3.
|
|
7
|
+
| **Spec revision** | 0.3.1 — 2026-07-16 |
|
|
8
8
|
| **License** | MIT |
|
|
9
9
|
| **Reference implementation** | `vdelta` (this repository; in development) |
|
|
10
10
|
|
|
@@ -375,6 +375,37 @@ sufficient for the consumer to verify the choice (e.g.,
|
|
|
375
375
|
comparator MUST abstain with comparability `none` (§6.3), never fall back
|
|
376
376
|
silently to a weaker match.
|
|
377
377
|
|
|
378
|
+
### 5.4. Near-miss disclosure
|
|
379
|
+
|
|
380
|
+
When `previous-comparable` selection abstains with `baseline-missing` (§6.3)
|
|
381
|
+
and the store contains at least one complete run other than the current run,
|
|
382
|
+
the report MUST enrich `comparability_detail` with a `near_miss` disclosure
|
|
383
|
+
identifying the nearest stored complete run and the stream-key component(s)
|
|
384
|
+
(§5.1) on which it differs from the current run.
|
|
385
|
+
|
|
386
|
+
Selection rule (normative, deterministic): candidates are all stored complete
|
|
387
|
+
runs excluding the current run, in store insertion order; each candidate is
|
|
388
|
+
scored by the number of mismatching components among the nine stream-key
|
|
389
|
+
components in the canonical order `repo.identity, repo.worktree, repo.branch,
|
|
390
|
+
repo.cwd, invocation.command, invocation.selector, instrument.adapter,
|
|
391
|
+
instrument.adapter_version, instrument.config_digest`; the candidate with the
|
|
392
|
+
fewest mismatches is selected; ties break by recency, where recency is store
|
|
393
|
+
insertion order, never timestamps (§7.8).
|
|
394
|
+
|
|
395
|
+
`mismatches` lists each differing component in the canonical order above with
|
|
396
|
+
the candidate's recorded value and the current run's value; array-valued
|
|
397
|
+
components (`invocation.command`, `invocation.selector`) are rendered as their
|
|
398
|
+
elements joined by a single space. Values are drawn from stored run records,
|
|
399
|
+
which are redacted before persistence (§15), so the disclosure never widens
|
|
400
|
+
the exposure surface.
|
|
401
|
+
|
|
402
|
+
The disclosure is informative context only: it MUST NOT alter baseline
|
|
403
|
+
selection, the abstention reason, or comparability — the report still
|
|
404
|
+
abstains with `baseline-missing`/`determined`, and this section does not
|
|
405
|
+
weaken the stream key. Other baseline modes (`git-ref`, `explicit-run-id`,
|
|
406
|
+
`previous-superset`) MUST NOT emit `near_miss`. The component-name vocabulary
|
|
407
|
+
is a closed enum (§14).
|
|
408
|
+
|
|
378
409
|
## 6. Comparability
|
|
379
410
|
|
|
380
411
|
### 6.1. Levels and permitted claims
|
|
@@ -412,6 +443,9 @@ is REQUIRED whenever `comparability` is `none`:
|
|
|
412
443
|
{ "reason": "baseline-missing", "kind": "determined" }
|
|
413
444
|
```
|
|
414
445
|
|
|
446
|
+
For `previous-comparable` `baseline-missing`, `comparability_detail`
|
|
447
|
+
additionally carries the `near_miss` disclosure when §5.4 requires it.
|
|
448
|
+
|
|
415
449
|
| `reason` | `kind` |
|
|
416
450
|
|---|---|
|
|
417
451
|
| `baseline-missing` | `determined` |
|
|
@@ -656,6 +690,10 @@ rendering of this schema with no independent logic.
|
|
|
656
690
|
Additional REQUIRED-when-applicable fields:
|
|
657
691
|
|
|
658
692
|
- `comparability_detail` when `comparability` is `none` (§6.3).
|
|
693
|
+
- `comparability_detail.near_miss` when §5.4 applies — an object
|
|
694
|
+
`{"run_id": ..., "mismatches": [{"field": ..., "recorded": ..., "current": ...}]}`
|
|
695
|
+
where `field` is one of the nine stream-key component names (closed enum)
|
|
696
|
+
and `mismatches` is non-empty.
|
|
659
697
|
- `budget_exceeded_for_safety: true` when §8's budget rule fires.
|
|
660
698
|
- `masking_applied` with count and example when §7.6 dedup fired.
|
|
661
699
|
- `trust.record_integrity`: `advisory | tamper-evident | trusted-environment`
|
|
@@ -920,9 +958,17 @@ Adapter roadmap for the reference implementation (informative): `vitest`
|
|
|
920
958
|
native reporter first — agent-authored code skews heavily toward TS/JS, the
|
|
921
959
|
maintainer's own repositories (the cheapest dogfood subjects) are TypeScript,
|
|
922
960
|
and empirical probing confirmed the channel satisfies §3.6 with no
|
|
923
|
-
stale-cache path in run mode.
|
|
924
|
-
|
|
925
|
-
|
|
961
|
+
stale-cache path in run mode. Playwright native reporter second, by the same
|
|
962
|
+
dogfood-first criterion: maintained end-to-end suites are available as
|
|
963
|
+
dogfood subjects, e2e suites are where flakiness attribution and fail-closed
|
|
964
|
+
delta gating pay most, and first-class retries, per-project repetition of
|
|
965
|
+
the same title, and worker parallelism exercise adapter-interface edges
|
|
966
|
+
vitest does not. Commitment is contingent on an empirical probe confirming
|
|
967
|
+
the structured reporter channel satisfies §3.6. `pytest` native is
|
|
968
|
+
demand-driven rather than scheduled — its structured channel is already
|
|
969
|
+
probed and its composition pre-designed, so the banked design does not decay
|
|
970
|
+
while parked. JUnit XML is likewise demand-driven, for CI breadth, under the
|
|
971
|
+
constraints above.
|
|
926
972
|
|
|
927
973
|
## 13. Conformance
|
|
928
974
|
|
|
@@ -1071,6 +1117,14 @@ None violates an invariant; each is disclosed rather than papered over.
|
|
|
1071
1117
|
|
|
1072
1118
|
## Revision history
|
|
1073
1119
|
|
|
1120
|
+
- **0.3.1 (2026-07-16)** — Adapter roadmap reordered (§12, informative):
|
|
1121
|
+
Playwright second, by the same dogfood-first criterion that flipped the
|
|
1122
|
+
first target to vitest in 0.3.0 — maintained e2e suites are available as
|
|
1123
|
+
dogfood subjects, flakiness attribution and fail-closed gating pay most in
|
|
1124
|
+
e2e, and retries/projects/worker parallelism exercise adapter-interface
|
|
1125
|
+
edges vitest lacks — contingent on a §3.6 probe of its structured reporter
|
|
1126
|
+
channel. `pytest` and JUnit XML moved to demand-driven; pytest's probe
|
|
1127
|
+
results and pre-designed composition remain banked. No normative changes.
|
|
1074
1128
|
- **0.3.0 (2026-07-16)** — First adapter target flipped to vitest after an
|
|
1075
1129
|
empirical probe confirmed §3.6 satisfiability (core digest stable across
|
|
1076
1130
|
rerun/line-shift/unrelated-edit, sensitive only to genuine signal; no
|