@afokapu/atdd-bun 0.10.99 → 0.10.100
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/integrity.json +2 -2
- package/package.json +1 -1
- package/src/hooks.ts +20 -15
package/integrity.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.10.
|
|
2
|
+
"version": "0.10.100",
|
|
3
3
|
"files": {
|
|
4
4
|
"HOOK_AUDIT.md": "5329d840db37671b1918f688ead26865473b87db73dbc75f7c8b2a8bbe8d6d43",
|
|
5
5
|
"PLANNER_PORT.md": "fb5935bac8b7ac18994de21e43ace3a5ef8cd55f85b0e3349fca261280054f11",
|
|
@@ -1261,7 +1261,7 @@
|
|
|
1261
1261
|
"src/cli.ts": "d93c3aebb78e752fd96347a5b177c0ed0697010c6e2533e116de93e772cd65a8",
|
|
1262
1262
|
"src/docs-capability.ts": "bdeeb414649049b1cd4fa8008cada3a576318be51c43bec91bfa46a8e250c270",
|
|
1263
1263
|
"src/enforce.ts": "e0c10519aeecd7080f2900ea230fad2f09482af47c1cee885d5de71573e29732",
|
|
1264
|
-
"src/hooks.ts": "
|
|
1264
|
+
"src/hooks.ts": "8194c2dec14afc307b5ce9e90f64d8659210c579d8f427492823293f302e2876",
|
|
1265
1265
|
"src/index.ts": "4081f8b08d1d3ee9e15a9964143e53d9cfb94a5616ab3e58a180d24cc16dc381",
|
|
1266
1266
|
"src/integrity.ts": "f1ede7cb04924ae88e11917047277e264af6596b9759e381bd9c497cd34a797d",
|
|
1267
1267
|
"src/journey-docs.ts": "994d229376244c84212cff951032823068a76565dd2fd427b9d44e341bd14853",
|
package/package.json
CHANGED
package/src/hooks.ts
CHANGED
|
@@ -61,44 +61,49 @@ async function ratchetCommitGate(root: string, staged: string[], registryChanged
|
|
|
61
61
|
try { candidateConfig = Bun.YAML.parse(stagedConfig.out) as Record<string, unknown>; policy = parseRatchetPolicy(candidateConfig); }
|
|
62
62
|
catch (error) { return bad(`ratchet activation commit gate configuration is invalid: ${String(error)}`); }
|
|
63
63
|
if (policy?.mode !== "reject-new" || !Array.isArray(candidateConfig.profiles) || !candidateConfig.profiles.every(profile => typeof profile === "string")) return null;
|
|
64
|
-
if ((await git(root, ["diff", "--quiet"])).code !== 0 || (await git(root, ["ls-files", "--others", "--exclude-standard"])).out) return bad("ratchet activation commit gate requires no unstaged or untracked candidate state");
|
|
65
64
|
const head = await git(root, ["rev-parse", "--verify", "HEAD^{commit}"]);
|
|
66
65
|
if (head.code || !/^[0-9a-f]{40}$/i.test(head.out)) return bad("ratchet activation commit gate requires an exact committed HEAD base");
|
|
67
|
-
|
|
66
|
+
let scratch: string | undefined;
|
|
68
67
|
try {
|
|
68
|
+
scratch = await mkdtemp(join(tmpdir(), "atdd-ratchet-commit-gate-"));
|
|
69
69
|
const added = await git(root, ["worktree", "add", "--detach", "--no-checkout", scratch, head.out]);
|
|
70
|
-
if (added.code)
|
|
70
|
+
if (added.code) throw new Error(`could not materialize ratchet activation base: ${added.err || added.out}`);
|
|
71
71
|
const checkedOut = await git(scratch, ["checkout", "--detach", head.out]);
|
|
72
|
-
if (checkedOut.code)
|
|
72
|
+
if (checkedOut.code) throw new Error(`could not checkout ratchet activation base: ${checkedOut.err || checkedOut.out}`);
|
|
73
73
|
const baseConfig = await configAt(scratch), candidateProfiles = stable(candidateConfig.profiles.map(normalizeProfileName));
|
|
74
74
|
const baseProfiles = stable(await enabledProfiles(scratch));
|
|
75
75
|
const policyMatches = stable(policy.profiles.map(normalizeProfileName)).join(",") === candidateProfiles.join(",");
|
|
76
76
|
const activation = isStrictProfileExpansion(baseProfiles, candidateProfiles) && activationContextDigest(baseConfig) === activationContextDigest(candidateConfig);
|
|
77
77
|
const unchanged = baseProfiles.join(",") === candidateProfiles.join(",") && contextDigest(baseConfig, baseProfiles) === contextDigest(candidateConfig, candidateProfiles);
|
|
78
78
|
if (candidateProfiles.some(profile => !concreteProfiles.includes(profile as typeof concreteProfiles[number])) || !policyMatches || (!activation && !unchanged)) return bad("ratchet reject-new requires either unchanged explicit profiles/context or an explicit monotonic profile expansion with identical non-profile context");
|
|
79
|
-
const selectedProfiles = activation || staged.some(needsFullCommitRatchetProfiles) ? candidateProfiles : (await affectedProfiles(root, staged, registryChanged)).filter(profile => candidateProfiles.includes(profile));
|
|
80
79
|
const tree = await git(root, ["write-tree"]);
|
|
81
80
|
if (tree.code || !/^[0-9a-f]{40}$/i.test(tree.out)) return bad(`could not identify staged ratchet activation candidate: ${tree.err || tree.out}`);
|
|
82
81
|
const baseTree = await git(root, ["rev-parse", "--verify", `${head.out}^{tree}`]);
|
|
83
82
|
if (baseTree.code || !/^[0-9a-f]{40}$/i.test(baseTree.out)) return bad(`could not identify ratchet activation base tree: ${baseTree.err || baseTree.out}`);
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (applied.code) return bad(`could not materialize staged ratchet activation candidate: ${applied.err || applied.out}`);
|
|
83
|
+
// Scan only exact Git trees in private scratch: live unstaged/untracked bytes never enter, and cache keys stay tree IDs.
|
|
84
|
+
const materialize = async (treeId: string, label: string) => {
|
|
85
|
+
const reset = await git(scratch!, ["read-tree", "--reset", "-u", treeId]);
|
|
86
|
+
if (reset.code) throw new Error(`could not materialize ${label}: ${reset.err || reset.out}`);
|
|
87
|
+
const exact = await git(scratch!, ["write-tree"]);
|
|
88
|
+
if (exact.code || exact.out !== treeId) throw new Error(`${label} did not materialize exactly: ${exact.err || exact.out}`);
|
|
89
|
+
};
|
|
90
|
+
await materialize(tree.out, "staged ratchet activation candidate");
|
|
93
91
|
const materialized = await configAt(scratch), materializedProfiles = stable(await enabledProfiles(scratch));
|
|
94
92
|
const materializedContext = activation ? activationContextDigest(materialized) === activationContextDigest(candidateConfig) : materializedProfiles.join(",") === candidateProfiles.join(",") && contextDigest(materialized, materializedProfiles) === contextDigest(candidateConfig, candidateProfiles);
|
|
95
93
|
if (!materializedContext) return bad("staged ratchet candidate did not materialize exactly");
|
|
94
|
+
const selectedProfiles = activation || staged.some(needsFullCommitRatchetProfiles) ? candidateProfiles : (await affectedProfiles(scratch, staged, registryChanged)).filter(profile => candidateProfiles.includes(profile));
|
|
95
|
+
const normalize = (findings: Awaited<ReturnType<typeof enforce>>) => findings.map(finding => ({ ...finding, file: canonicalFindingPath(scratch!, finding.file) }));
|
|
96
|
+
const cacheDirectory = await findingsCacheDirectory(root), toolkit = await toolkitIdentity().catch(() => null);
|
|
97
|
+
const findingsAt = async (treeId: string) => cachedFindings(cacheDirectory, treeId, toolkit, selectedProfiles, async () => normalize(await enforce({ root: scratch, profiles: selectedProfiles as any })));
|
|
98
|
+
await materialize(baseTree.out, "ratchet activation base tree");
|
|
99
|
+
const baseFindings = selectedProfiles.length ? await findingsAt(baseTree.out) : [];
|
|
100
|
+
await materialize(tree.out, "staged ratchet activation candidate");
|
|
96
101
|
const candidateFindings = selectedProfiles.length ? await findingsAt(tree.out) : [];
|
|
97
102
|
const delta = compareFindings(baseFindings, candidateFindings);
|
|
98
103
|
const report = JSON.stringify({ schema: "atdd-bun.ratchet-report/v1", mode: policy.mode, base: head.out, candidate: tree.out, profiles: selectedProfiles, context: contextDigest(candidateConfig, candidateProfiles), ...delta });
|
|
99
104
|
return { ok: delta.new.length === 0, message: report };
|
|
100
105
|
} catch (error) { return bad(`ratchet activation commit gate failed closed: ${String(error)}`); }
|
|
101
|
-
finally { await git(root, ["worktree", "remove", "--force", scratch]); await rm(scratch, { recursive: true, force: true }); }
|
|
106
|
+
finally { if (scratch) { await git(root, ["worktree", "remove", "--force", scratch]); await rm(scratch, { recursive: true, force: true }); } }
|
|
102
107
|
}
|
|
103
108
|
|
|
104
109
|
/** A pushed reject-new candidate is judged from its immutable object IDs, never the hook's checkout or index. */
|