@deftai/directive-core 0.103.0 → 0.105.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/authz/classify.js +682 -35
- package/dist/check/cached-orchestrator.d.ts +1 -1
- package/dist/check/cached-orchestrator.js +2 -2
- package/dist/check/gate-lists.d.ts +3 -2
- package/dist/check/gate-lists.js +5 -2
- package/dist/check/index.d.ts +1 -0
- package/dist/check/index.js +1 -0
- package/dist/check/named-cause.d.ts +1 -1
- package/dist/check/named-cause.js +33 -3
- package/dist/check/session-completed-ac.d.ts +39 -0
- package/dist/check/session-completed-ac.js +198 -0
- package/dist/codebase/default-extractor.d.ts +5 -0
- package/dist/codebase/default-extractor.js +7 -15
- package/dist/deposit/copy-tree.js +60 -0
- package/dist/deposit/python-free.js +26 -18
- package/dist/doctor/checks.d.ts +8 -0
- package/dist/doctor/checks.js +37 -25
- package/dist/doctor/doctor-state.d.ts +4 -0
- package/dist/doctor/doctor-state.js +7 -1
- package/dist/doctor/main.js +9 -14
- package/dist/doctor/types.d.ts +9 -0
- package/dist/fs/contained-write.d.ts +115 -1
- package/dist/fs/contained-write.js +242 -5
- package/dist/fs/mutation-ledger.d.ts +54 -0
- package/dist/fs/mutation-ledger.js +133 -0
- package/dist/fs/non-product-dirs.d.ts +79 -0
- package/dist/fs/non-product-dirs.js +96 -0
- package/dist/hooks/dispatcher.d.ts +3 -1
- package/dist/hooks/dispatcher.js +14 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/dist/init-deposit/agent-hooks.js +17 -15
- package/dist/init-deposit/core-guard-pin-content.d.ts +7 -0
- package/dist/init-deposit/core-guard-pin-content.js +393 -0
- package/dist/init-deposit/gitignore.js +9 -0
- package/dist/init-deposit/hygiene.d.ts +47 -11
- package/dist/init-deposit/hygiene.js +172 -37
- package/dist/init-deposit/legacy-detect.js +6 -2
- package/dist/init-deposit/refresh.d.ts +21 -1
- package/dist/init-deposit/refresh.js +106 -9
- package/dist/init-deposit/scaffold.js +28 -279
- package/dist/init-deposit/slash-deposit.js +13 -7
- package/dist/init-deposit/xbrief-projections.d.ts +8 -1
- package/dist/init-deposit/xbrief-projections.js +24 -11
- package/dist/intake/clause-derivation.d.ts +74 -0
- package/dist/intake/clause-derivation.js +409 -0
- package/dist/intake/index.d.ts +1 -0
- package/dist/intake/index.js +1 -0
- package/dist/intake/issue-ingest.d.ts +3 -1
- package/dist/intake/issue-ingest.js +36 -43
- package/dist/lifecycle/completed-tracked-on-delivery.d.ts +5 -0
- package/dist/lifecycle/completed-tracked-on-delivery.js +85 -18
- package/dist/literal-acceptance/capture.d.ts +10 -0
- package/dist/literal-acceptance/capture.js +224 -75
- package/dist/literal-acceptance/evaluate.d.ts +29 -4
- package/dist/literal-acceptance/evaluate.js +70 -2
- package/dist/literal-acceptance/index.d.ts +3 -3
- package/dist/literal-acceptance/index.js +3 -3
- package/dist/literal-acceptance/safety.d.ts +41 -0
- package/dist/literal-acceptance/safety.js +126 -2
- package/dist/literal-acceptance/types.d.ts +8 -0
- package/dist/orphan-active/evaluate.d.ts +7 -1
- package/dist/orphan-active/evaluate.js +102 -23
- package/dist/platform/platform-capabilities.js +2 -3
- package/dist/policy/base-branch.d.ts +37 -0
- package/dist/policy/base-branch.js +114 -0
- package/dist/policy/branch-sync.d.ts +80 -0
- package/dist/policy/branch-sync.js +211 -0
- package/dist/policy/ceremony-dial-escalation.js +1 -0
- package/dist/policy/ceremony-dial.js +4 -14
- package/dist/policy/delivery-branch.d.ts +5 -3
- package/dist/policy/delivery-branch.js +7 -3
- package/dist/policy/file-size-thresholds.d.ts +24 -0
- package/dist/policy/file-size-thresholds.js +23 -0
- package/dist/policy/forge-outage-retry.d.ts +47 -0
- package/dist/policy/forge-outage-retry.js +157 -0
- package/dist/policy/index.d.ts +7 -0
- package/dist/policy/index.js +66 -7
- package/dist/policy/project-invariants.d.ts +50 -0
- package/dist/policy/project-invariants.js +205 -0
- package/dist/policy/sync-default.d.ts +129 -0
- package/dist/policy/sync-default.js +452 -0
- package/dist/policy/sync-max-files.d.ts +93 -0
- package/dist/policy/sync-max-files.js +192 -0
- package/dist/pr-monitor/absent-required.d.ts +11 -0
- package/dist/pr-monitor/absent-required.js +61 -0
- package/dist/pr-monitor/constants.d.ts +7 -0
- package/dist/pr-monitor/constants.js +7 -0
- package/dist/pr-monitor/index.d.ts +2 -1
- package/dist/pr-monitor/index.js +2 -1
- package/dist/pr-monitor/main.js +4 -0
- package/dist/pr-monitor/monitor.js +24 -1
- package/dist/pr-wait-mergeable/classify.js +6 -0
- package/dist/preflight/evaluate.d.ts +9 -0
- package/dist/preflight/evaluate.js +49 -1
- package/dist/preflight/index.d.ts +5 -1
- package/dist/preflight/index.js +2 -0
- package/dist/preflight/intended-placement.d.ts +62 -0
- package/dist/preflight/intended-placement.js +275 -0
- package/dist/preflight/project-invariants-gate.d.ts +29 -0
- package/dist/preflight/project-invariants-gate.js +82 -0
- package/dist/product-first-done-gate/acceptance-resolver.d.ts +128 -0
- package/dist/product-first-done-gate/acceptance-resolver.js +144 -0
- package/dist/product-first-done-gate/acceptance.d.ts +7 -4
- package/dist/product-first-done-gate/acceptance.js +79 -8
- package/dist/product-first-done-gate/empty-resolution.d.ts +5 -2
- package/dist/product-first-done-gate/empty-resolution.js +13 -2
- package/dist/product-first-done-gate/evaluate.d.ts +29 -0
- package/dist/product-first-done-gate/evaluate.js +307 -33
- package/dist/product-first-done-gate/index.d.ts +3 -1
- package/dist/product-first-done-gate/index.js +2 -1
- package/dist/release/build-dist.d.ts +101 -0
- package/dist/release/build-dist.js +428 -46
- package/dist/run-summary/emit.d.ts +50 -4
- package/dist/run-summary/emit.js +262 -26
- package/dist/run-summary/index.d.ts +3 -2
- package/dist/run-summary/index.js +2 -1
- package/dist/run-summary/session-id.d.ts +21 -0
- package/dist/run-summary/session-id.js +92 -0
- package/dist/run-summary/share.d.ts +1 -1
- package/dist/run-summary/share.js +2 -2
- package/dist/run-summary/types.d.ts +38 -6
- package/dist/run-summary/types.js +1 -0
- package/dist/scope/acceptance-activate-gate.d.ts +1 -1
- package/dist/scope/acceptance-activate-gate.js +4 -4
- package/dist/scope/acceptance-evidence.d.ts +27 -0
- package/dist/scope/acceptance-evidence.js +66 -0
- package/dist/scope/capacity-stamp.d.ts +2 -0
- package/dist/scope/capacity-stamp.js +4 -0
- package/dist/scope/delivery-evidence.d.ts +9 -3
- package/dist/scope/delivery-evidence.js +39 -27
- package/dist/scope/index.d.ts +1 -0
- package/dist/scope/index.js +1 -0
- package/dist/scope/parent-lineage.js +7 -6
- package/dist/scope/project-invariant-coverage.d.ts +54 -0
- package/dist/scope/project-invariant-coverage.js +322 -0
- package/dist/scope/transition.js +66 -4
- package/dist/scope-provenance/compare-intent.d.ts +27 -0
- package/dist/scope-provenance/compare-intent.js +233 -0
- package/dist/scope-provenance/digest.d.ts +18 -1
- package/dist/scope-provenance/digest.js +16 -11
- package/dist/scope-provenance/evaluate.d.ts +5 -1
- package/dist/scope-provenance/evaluate.js +64 -4
- package/dist/scope-provenance/extract-intent.d.ts +43 -0
- package/dist/scope-provenance/extract-intent.js +262 -0
- package/dist/scope-provenance/index.d.ts +7 -1
- package/dist/scope-provenance/index.js +7 -1
- package/dist/scope-provenance/intent-digest.d.ts +12 -0
- package/dist/scope-provenance/intent-digest.js +33 -0
- package/dist/scope-provenance/intent-evaluate.d.ts +43 -0
- package/dist/scope-provenance/intent-evaluate.js +149 -0
- package/dist/scope-provenance/json-tokenizer.d.ts +19 -0
- package/dist/scope-provenance/json-tokenizer.js +225 -0
- package/dist/scope-provenance/known-machine.d.ts +38 -0
- package/dist/scope-provenance/known-machine.js +157 -0
- package/dist/scope-provenance/mint-artifacts.d.ts +102 -0
- package/dist/scope-provenance/mint-artifacts.js +425 -0
- package/dist/session/ac-pass-banking.d.ts +4 -36
- package/dist/session/ac-pass-banking.js +30 -77
- package/dist/session/ac-pass-reuse.d.ts +53 -0
- package/dist/session/ac-pass-reuse.js +95 -0
- package/dist/session/ceremony-dial-evidence.d.ts +42 -0
- package/dist/session/ceremony-dial-evidence.js +194 -0
- package/dist/session/git.js +19 -4
- package/dist/session/index.d.ts +5 -0
- package/dist/session/index.js +5 -0
- package/dist/session/occupancy-stress-worker.d.ts +7 -0
- package/dist/session/occupancy-stress-worker.js +36 -0
- package/dist/session/occupancy.d.ts +97 -0
- package/dist/session/occupancy.js +422 -0
- package/dist/session/openclaw-soft-rebind-deposit.js +6 -6
- package/dist/session/product-state-hash.d.ts +26 -0
- package/dist/session/product-state-hash.js +683 -0
- package/dist/session/session-ready.d.ts +2 -0
- package/dist/session/session-ready.js +52 -30
- package/dist/session/session-start.d.ts +7 -0
- package/dist/session/session-start.js +88 -5
- package/dist/session/verify-ac-session-cache.d.ts +42 -0
- package/dist/session/verify-ac-session-cache.js +126 -0
- package/dist/session/verify-session-ritual.d.ts +5 -0
- package/dist/session/verify-session-ritual.js +9 -0
- package/dist/slash/openclaw-deposit.js +13 -9
- package/dist/slice/constants.d.ts +2 -2
- package/dist/slice/constants.js +2 -2
- package/dist/slice/existing.js +21 -4
- package/dist/slice/lock.d.ts +16 -1
- package/dist/slice/lock.js +137 -11
- package/dist/slice/record.d.ts +8 -5
- package/dist/slice/record.js +21 -13
- package/dist/story-ready/evaluate.d.ts +2 -0
- package/dist/story-ready/evaluate.js +13 -0
- package/dist/swarm/complete-cohort.d.ts +3 -0
- package/dist/swarm/complete-cohort.js +59 -11
- package/dist/swarm/finalize-cohort.js +33 -2
- package/dist/swarm/launch.d.ts +22 -0
- package/dist/swarm/launch.js +155 -0
- package/dist/swarm/routing-set-cli.d.ts +11 -1
- package/dist/swarm/routing-set-cli.js +13 -3
- package/dist/swarm/routing-verify.d.ts +12 -0
- package/dist/swarm/routing-verify.js +59 -20
- package/dist/swarm/routing.d.ts +14 -0
- package/dist/swarm/routing.js +53 -3
- package/dist/telemetry-coverage/evaluate.d.ts +42 -0
- package/dist/telemetry-coverage/evaluate.js +148 -0
- package/dist/telemetry-coverage/fake-trial.d.ts +45 -0
- package/dist/telemetry-coverage/fake-trial.js +204 -0
- package/dist/telemetry-coverage/index.d.ts +5 -0
- package/dist/telemetry-coverage/index.js +5 -0
- package/dist/telemetry-coverage/kinds.d.ts +19 -0
- package/dist/telemetry-coverage/kinds.js +54 -0
- package/dist/telemetry-coverage/scan-callers.d.ts +25 -0
- package/dist/telemetry-coverage/scan-callers.js +168 -0
- package/dist/triage/actions/index.js +4 -1
- package/dist/ts-check-lane/index.d.ts +3 -0
- package/dist/ts-check-lane/index.js +2 -0
- package/dist/ts-check-lane/progress-reporter.d.ts +17 -0
- package/dist/ts-check-lane/progress-reporter.js +38 -0
- package/dist/ts-check-lane/progress.d.ts +32 -0
- package/dist/ts-check-lane/progress.js +57 -0
- package/dist/ts-check-lane/run-lane.d.ts +3 -1
- package/dist/ts-check-lane/run-lane.js +8 -6
- package/dist/validate-content/validate-links.d.ts +8 -0
- package/dist/validate-content/validate-links.js +9 -15
- package/dist/value/feedback-file.js +12 -2
- package/dist/vbrief-build/index.d.ts +1 -0
- package/dist/vbrief-build/index.js +1 -0
- package/dist/vbrief-build/project-invariants-io.d.ts +16 -0
- package/dist/vbrief-build/project-invariants-io.js +41 -0
- package/dist/vbrief-reconcile/graph.js +18 -1
- package/dist/vbrief-reconcile/index.d.ts +3 -1
- package/dist/vbrief-reconcile/index.js +2 -1
- package/dist/vbrief-reconcile/origin-freshness.d.ts +44 -0
- package/dist/vbrief-reconcile/origin-freshness.js +221 -0
- package/dist/vbrief-reconcile/types.d.ts +13 -0
- package/dist/vbrief-reconcile/umbrellas.d.ts +14 -0
- package/dist/vbrief-reconcile/umbrellas.js +261 -26
- package/dist/vbrief-validate/index.d.ts +1 -1
- package/dist/vbrief-validate/index.js +1 -1
- package/dist/vbrief-validate/plan-hooks.d.ts +4 -0
- package/dist/vbrief-validate/plan-hooks.js +68 -0
- package/dist/verify-ac/evaluate.d.ts +7 -1
- package/dist/verify-ac/evaluate.js +34 -26
- package/dist/verify-ac/flag.d.ts +4 -0
- package/dist/verify-ac/flag.js +16 -0
- package/dist/verify-ac/index.d.ts +2 -2
- package/dist/verify-ac/index.js +2 -2
- package/dist/verify-source/contained-writes.js +2 -0
- package/dist/verify-source/index.d.ts +1 -0
- package/dist/verify-source/index.js +1 -0
- package/dist/verify-source/verify-stubs.d.ts +15 -1
- package/dist/verify-source/verify-stubs.js +20 -16
- package/dist/vitest-runner/win32-coverage-tmp-setup.d.ts +5 -6
- package/dist/vitest-runner/win32-coverage-tmp-setup.js +5 -6
- package/dist/xbrief-migrate/migrate-project.js +2 -5
- package/package.json +3 -3
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One mint writes record + preimage + both digests (#3376 R7 / #3385).
|
|
3
|
+
* Dest publish is two-phase under a per-plan lock: both dests land as
|
|
4
|
+
* `.next` first, then dests are copied from that pair. A crash leaves
|
|
5
|
+
* `.next` and/or a bak journal; recover finishes the flip or restores
|
|
6
|
+
* the prior pair (or neither dest) without waiting for a remint.
|
|
7
|
+
* Caught write failures still restore or clear dests.
|
|
8
|
+
*/
|
|
9
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync } from "node:fs";
|
|
10
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
11
|
+
import { ContainedWriteError, ContainedWriteErrorCode, containedRemove, containedWrite, } from "../fs/contained-write.js";
|
|
12
|
+
import { approvedScopeDir, approvedScopeIntentPath, approvedScopeRecordPath, buildApprovedScopeRecord, } from "./digest.js";
|
|
13
|
+
import { extractIntentFromRaw, } from "./extract-intent.js";
|
|
14
|
+
import { computeIntentDigest, INTENT_DIGEST_ALGO } from "./intent-digest.js";
|
|
15
|
+
export { approvedScopeIntentPath, approvedScopeRecordPath };
|
|
16
|
+
export class MintPairRollbackError extends Error {
|
|
17
|
+
writeError;
|
|
18
|
+
restoreError;
|
|
19
|
+
clearError;
|
|
20
|
+
constructor(writeError, restoreError, clearError) {
|
|
21
|
+
const writeMsg = errorMessage(writeError);
|
|
22
|
+
const restoreMsg = errorMessage(restoreError);
|
|
23
|
+
const clearSuffix = clearError === undefined
|
|
24
|
+
? "; leftover dests were cleared"
|
|
25
|
+
: `; leftover dests could not be cleared (${errorMessage(clearError)})`;
|
|
26
|
+
super(`Mint dest publish failed (${writeMsg}); rollback also failed (${restoreMsg})${clearSuffix}. Do not commit a partial approved-scope pair.`);
|
|
27
|
+
this.name = "MintPairRollbackError";
|
|
28
|
+
this.writeError = writeError;
|
|
29
|
+
this.restoreError = restoreError;
|
|
30
|
+
this.clearError = clearError;
|
|
31
|
+
this.cause = writeError;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function errorMessage(err) {
|
|
35
|
+
return err instanceof Error ? err.message : String(err);
|
|
36
|
+
}
|
|
37
|
+
function snapshotText(path) {
|
|
38
|
+
if (!existsSync(path))
|
|
39
|
+
return null;
|
|
40
|
+
return readFileSync(path, "utf8");
|
|
41
|
+
}
|
|
42
|
+
function restoreText(root, path, snapshot) {
|
|
43
|
+
rmSync(path, { force: true, recursive: true });
|
|
44
|
+
if (snapshot === null)
|
|
45
|
+
return;
|
|
46
|
+
containedWrite({
|
|
47
|
+
root,
|
|
48
|
+
target: path,
|
|
49
|
+
data: snapshot,
|
|
50
|
+
mode: "replace",
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
function writeTempPayload(root, target, data) {
|
|
54
|
+
containedWrite({
|
|
55
|
+
root,
|
|
56
|
+
target,
|
|
57
|
+
data,
|
|
58
|
+
mode: "create",
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
function restoreDestDefault(input) {
|
|
62
|
+
restoreText(input.root, input.target, input.snapshot);
|
|
63
|
+
}
|
|
64
|
+
function removeDestDefault(input) {
|
|
65
|
+
rmSync(input.target, { force: true });
|
|
66
|
+
}
|
|
67
|
+
function pairStem(recordPath) {
|
|
68
|
+
return basename(recordPath).replace(/\.json$/i, "");
|
|
69
|
+
}
|
|
70
|
+
export function approvedScopePairLockPath(dir, recordPath) {
|
|
71
|
+
return join(dir, `.${pairStem(recordPath)}.pair.lock.tmp`);
|
|
72
|
+
}
|
|
73
|
+
export function approvedScopePairJournalPaths(dir, intentPath, recordPath) {
|
|
74
|
+
return {
|
|
75
|
+
publishing: join(dir, `.${pairStem(recordPath)}.publishing.bak`),
|
|
76
|
+
intentBak: join(dir, `${basename(intentPath)}.bak`),
|
|
77
|
+
recordBak: join(dir, `${basename(recordPath)}.bak`),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
export function approvedScopePairNextPaths(dir, intentPath, recordPath) {
|
|
81
|
+
return {
|
|
82
|
+
intentNext: join(dir, `${basename(intentPath)}.next.tmp`),
|
|
83
|
+
recordNext: join(dir, `${basename(recordPath)}.next.tmp`),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function publishFromNext(root, next, dest) {
|
|
87
|
+
if (!existsSync(next)) {
|
|
88
|
+
throw new Error(`mint pair next payload missing: ${basename(next)}`);
|
|
89
|
+
}
|
|
90
|
+
containedWrite({
|
|
91
|
+
root,
|
|
92
|
+
target: dest,
|
|
93
|
+
data: readFileSync(next, "utf8"),
|
|
94
|
+
mode: "replace",
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
function writeBak(root, src, bak) {
|
|
98
|
+
if (!existsSync(src)) {
|
|
99
|
+
rmSync(bak, { force: true });
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
containedWrite({
|
|
103
|
+
root,
|
|
104
|
+
target: bak,
|
|
105
|
+
data: readFileSync(src, "utf8"),
|
|
106
|
+
mode: "replace",
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
function clearJournal(paths) {
|
|
110
|
+
rmSync(paths.publishing, { force: true });
|
|
111
|
+
rmSync(paths.intentBak, { force: true });
|
|
112
|
+
rmSync(paths.recordBak, { force: true });
|
|
113
|
+
}
|
|
114
|
+
function pairStemFromPublishingName(name) {
|
|
115
|
+
const match = name.match(/^\.(.+)\.publishing\.bak$/i);
|
|
116
|
+
return match?.[1] ?? null;
|
|
117
|
+
}
|
|
118
|
+
function pairStemFromNextName(name) {
|
|
119
|
+
if (!name.endsWith(".next.tmp"))
|
|
120
|
+
return null;
|
|
121
|
+
const base = name.slice(0, -".next.tmp".length);
|
|
122
|
+
if (base.endsWith(".intent.json"))
|
|
123
|
+
return base.slice(0, -".intent.json".length);
|
|
124
|
+
if (base.endsWith(".json"))
|
|
125
|
+
return base.slice(0, -".json".length);
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Finish a staged dest flip from `.next`, or restore the bak pair / clear dests.
|
|
130
|
+
* Does not wait for a remint.
|
|
131
|
+
*/
|
|
132
|
+
export function recoverIncompleteApprovedScopePair(input) {
|
|
133
|
+
const root = resolve(input.projectRoot);
|
|
134
|
+
const dir = approvedScopeDir(input.projectRoot);
|
|
135
|
+
const journal = approvedScopePairJournalPaths(dir, input.intentPath, input.recordPath);
|
|
136
|
+
const next = approvedScopePairNextPaths(dir, input.intentPath, input.recordPath);
|
|
137
|
+
const intentNext = snapshotText(next.intentNext);
|
|
138
|
+
const recordNext = snapshotText(next.recordNext);
|
|
139
|
+
if (intentNext !== null && recordNext !== null) {
|
|
140
|
+
restoreText(root, input.intentPath, intentNext);
|
|
141
|
+
restoreText(root, input.recordPath, recordNext);
|
|
142
|
+
rmSync(next.intentNext, { force: true });
|
|
143
|
+
rmSync(next.recordNext, { force: true });
|
|
144
|
+
clearJournal(journal);
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
if (!existsSync(journal.publishing)) {
|
|
148
|
+
const leftover = intentNext !== null || recordNext !== null;
|
|
149
|
+
rmSync(next.intentNext, { force: true });
|
|
150
|
+
rmSync(next.recordNext, { force: true });
|
|
151
|
+
return leftover;
|
|
152
|
+
}
|
|
153
|
+
const intentBak = snapshotText(journal.intentBak);
|
|
154
|
+
const recordBak = snapshotText(journal.recordBak);
|
|
155
|
+
if (intentBak !== null || recordBak !== null) {
|
|
156
|
+
restoreText(root, input.intentPath, intentBak);
|
|
157
|
+
restoreText(root, input.recordPath, recordBak);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
rmSync(input.intentPath, { force: true });
|
|
161
|
+
rmSync(input.recordPath, { force: true });
|
|
162
|
+
}
|
|
163
|
+
rmSync(next.intentNext, { force: true });
|
|
164
|
+
rmSync(next.recordNext, { force: true });
|
|
165
|
+
clearJournal(journal);
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
/** Recover every incomplete pair under `.deft/approved-scope/`. */
|
|
169
|
+
export function recoverApprovedScopePairs(projectRoot) {
|
|
170
|
+
const dir = approvedScopeDir(projectRoot);
|
|
171
|
+
if (!existsSync(dir))
|
|
172
|
+
return 0;
|
|
173
|
+
const stems = new Set();
|
|
174
|
+
for (const name of readdirSync(dir)) {
|
|
175
|
+
const fromPub = pairStemFromPublishingName(name);
|
|
176
|
+
if (fromPub !== null)
|
|
177
|
+
stems.add(fromPub);
|
|
178
|
+
const fromNext = pairStemFromNextName(name);
|
|
179
|
+
if (fromNext !== null)
|
|
180
|
+
stems.add(fromNext);
|
|
181
|
+
}
|
|
182
|
+
let recovered = 0;
|
|
183
|
+
for (const stem of stems) {
|
|
184
|
+
const did = recoverIncompleteApprovedScopePair({
|
|
185
|
+
projectRoot,
|
|
186
|
+
intentPath: join(dir, `${stem}.intent.json`),
|
|
187
|
+
recordPath: join(dir, `${stem}.json`),
|
|
188
|
+
});
|
|
189
|
+
if (did)
|
|
190
|
+
recovered += 1;
|
|
191
|
+
}
|
|
192
|
+
return recovered;
|
|
193
|
+
}
|
|
194
|
+
function ownerPidAlive(lockPath) {
|
|
195
|
+
try {
|
|
196
|
+
const owner = Number(readFileSync(lockPath, "utf8").trim());
|
|
197
|
+
if (!Number.isInteger(owner) || owner <= 0)
|
|
198
|
+
return true;
|
|
199
|
+
process.kill(owner, 0);
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
catch (err) {
|
|
203
|
+
const code = err.code;
|
|
204
|
+
return code !== "ESRCH";
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
function reclaimDeadPairLock(lockPath) {
|
|
208
|
+
const stale = `${lockPath}.stale`;
|
|
209
|
+
try {
|
|
210
|
+
renameSync(lockPath, stale);
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
rmSync(stale, { force: true });
|
|
216
|
+
}
|
|
217
|
+
function isCreateExistsError(err) {
|
|
218
|
+
return err instanceof ContainedWriteError && err.code === ContainedWriteErrorCode.EXISTS;
|
|
219
|
+
}
|
|
220
|
+
function withPairLock(root, lockPath, waitMs, fn) {
|
|
221
|
+
mkdirSync(dirname(lockPath), { recursive: true });
|
|
222
|
+
const deadline = Date.now() + waitMs;
|
|
223
|
+
let held = false;
|
|
224
|
+
while (!held) {
|
|
225
|
+
try {
|
|
226
|
+
containedWrite({
|
|
227
|
+
root,
|
|
228
|
+
target: lockPath,
|
|
229
|
+
data: `${process.pid}\n`,
|
|
230
|
+
mode: "create",
|
|
231
|
+
mutation: false,
|
|
232
|
+
});
|
|
233
|
+
held = true;
|
|
234
|
+
}
|
|
235
|
+
catch (err) {
|
|
236
|
+
if (!isCreateExistsError(err))
|
|
237
|
+
throw err;
|
|
238
|
+
if (!ownerPidAlive(lockPath)) {
|
|
239
|
+
reclaimDeadPairLock(lockPath);
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
if (Date.now() > deadline) {
|
|
243
|
+
throw new Error(`timed out acquiring mint pair lock for ${basename(lockPath)}`);
|
|
244
|
+
}
|
|
245
|
+
const spinEnd = Date.now() + 20;
|
|
246
|
+
while (Date.now() < spinEnd) {
|
|
247
|
+
/* spin */
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
try {
|
|
252
|
+
return fn();
|
|
253
|
+
}
|
|
254
|
+
finally {
|
|
255
|
+
try {
|
|
256
|
+
containedRemove({ root, target: lockPath, mutation: false });
|
|
257
|
+
}
|
|
258
|
+
catch {
|
|
259
|
+
rmSync(lockPath, { force: true });
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
function restorePair(restore, dests) {
|
|
264
|
+
const failures = [];
|
|
265
|
+
for (const dest of dests) {
|
|
266
|
+
try {
|
|
267
|
+
restore(dest);
|
|
268
|
+
}
|
|
269
|
+
catch (err) {
|
|
270
|
+
failures.push(`${basename(dest.target)}: ${errorMessage(err)}`);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
if (failures.length > 0) {
|
|
274
|
+
throw new Error(failures.join("; "));
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
function clearDestPair(input) {
|
|
278
|
+
const dests = [
|
|
279
|
+
{ root: input.root, target: input.intentPath, snapshot: input.prevIntent },
|
|
280
|
+
{ root: input.root, target: input.recordPath, snapshot: input.prevRecord },
|
|
281
|
+
];
|
|
282
|
+
const removed = [];
|
|
283
|
+
const failures = [];
|
|
284
|
+
for (const dest of dests) {
|
|
285
|
+
try {
|
|
286
|
+
input.remove({ root: dest.root, target: dest.target });
|
|
287
|
+
removed.push(dest);
|
|
288
|
+
}
|
|
289
|
+
catch (err) {
|
|
290
|
+
failures.push(`${basename(dest.target)}: ${errorMessage(err)}`);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
if (failures.length === 0)
|
|
294
|
+
return;
|
|
295
|
+
const putBackFailures = [];
|
|
296
|
+
for (const dest of removed) {
|
|
297
|
+
try {
|
|
298
|
+
restoreDestDefault(dest);
|
|
299
|
+
}
|
|
300
|
+
catch (err) {
|
|
301
|
+
putBackFailures.push(`${basename(dest.target)}: ${errorMessage(err)}`);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
const putBackSuffix = putBackFailures.length > 0
|
|
305
|
+
? `; removed dests could not be put back (${putBackFailures.join("; ")})`
|
|
306
|
+
: "";
|
|
307
|
+
throw new Error(`${failures.join("; ")}${putBackSuffix}`);
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Write record + preimage as one fail-closed pair.
|
|
311
|
+
* Phase 1 writes both dests to `.next`. Phase 2 copies dests from that pair.
|
|
312
|
+
* Recover finishes the flip or restores the bak pair without a remint.
|
|
313
|
+
*/
|
|
314
|
+
export function writeApprovedScopePair(input) {
|
|
315
|
+
const root = resolve(input.projectRoot);
|
|
316
|
+
const dir = approvedScopeDir(input.projectRoot);
|
|
317
|
+
mkdirSync(dir, { recursive: true });
|
|
318
|
+
const lockPath = approvedScopePairLockPath(dir, input.recordPath);
|
|
319
|
+
const journal = approvedScopePairJournalPaths(dir, input.intentPath, input.recordPath);
|
|
320
|
+
const restore = input.restoreDest ?? restoreDestDefault;
|
|
321
|
+
const remove = input.removeDest ?? removeDestDefault;
|
|
322
|
+
const customPublish = input.publishDest;
|
|
323
|
+
const next = approvedScopePairNextPaths(dir, input.intentPath, input.recordPath);
|
|
324
|
+
withPairLock(root, lockPath, input.lockWaitMs ?? 30_000, () => {
|
|
325
|
+
recoverIncompleteApprovedScopePair({
|
|
326
|
+
projectRoot: input.projectRoot,
|
|
327
|
+
intentPath: input.intentPath,
|
|
328
|
+
recordPath: input.recordPath,
|
|
329
|
+
});
|
|
330
|
+
const prevIntent = snapshotText(input.intentPath);
|
|
331
|
+
const prevRecord = snapshotText(input.recordPath);
|
|
332
|
+
const dests = [
|
|
333
|
+
{ root, target: input.intentPath, snapshot: prevIntent },
|
|
334
|
+
{ root, target: input.recordPath, snapshot: prevRecord },
|
|
335
|
+
];
|
|
336
|
+
let destPublished = false;
|
|
337
|
+
try {
|
|
338
|
+
writeTempPayload(root, next.intentNext, input.intentData);
|
|
339
|
+
writeTempPayload(root, next.recordNext, input.recordData);
|
|
340
|
+
writeBak(root, input.intentPath, journal.intentBak);
|
|
341
|
+
writeBak(root, input.recordPath, journal.recordBak);
|
|
342
|
+
containedWrite({
|
|
343
|
+
root,
|
|
344
|
+
target: journal.publishing,
|
|
345
|
+
data: "publishing\n",
|
|
346
|
+
mode: "replace",
|
|
347
|
+
});
|
|
348
|
+
destPublished = true;
|
|
349
|
+
if (customPublish) {
|
|
350
|
+
customPublish({ root, target: input.intentPath, data: input.intentData });
|
|
351
|
+
customPublish({ root, target: input.recordPath, data: input.recordData });
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
publishFromNext(root, next.intentNext, input.intentPath);
|
|
355
|
+
publishFromNext(root, next.recordNext, input.recordPath);
|
|
356
|
+
}
|
|
357
|
+
clearJournal(journal);
|
|
358
|
+
}
|
|
359
|
+
catch (err) {
|
|
360
|
+
if (destPublished) {
|
|
361
|
+
try {
|
|
362
|
+
restorePair(restore, dests);
|
|
363
|
+
clearJournal(journal);
|
|
364
|
+
}
|
|
365
|
+
catch (restoreErr) {
|
|
366
|
+
try {
|
|
367
|
+
clearDestPair({
|
|
368
|
+
root,
|
|
369
|
+
intentPath: input.intentPath,
|
|
370
|
+
recordPath: input.recordPath,
|
|
371
|
+
prevIntent,
|
|
372
|
+
prevRecord,
|
|
373
|
+
remove,
|
|
374
|
+
});
|
|
375
|
+
clearJournal(journal);
|
|
376
|
+
}
|
|
377
|
+
catch (clearErr) {
|
|
378
|
+
throw new MintPairRollbackError(err, restoreErr, clearErr);
|
|
379
|
+
}
|
|
380
|
+
throw new MintPairRollbackError(err, restoreErr);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
throw err;
|
|
384
|
+
}
|
|
385
|
+
finally {
|
|
386
|
+
rmSync(next.intentNext, { force: true });
|
|
387
|
+
rmSync(next.recordNext, { force: true });
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
export function mintApprovedScopeArtifacts(input) {
|
|
392
|
+
const extracted = extractIntentFromRaw(input.rawText, {
|
|
393
|
+
projectRoot: input.projectRoot,
|
|
394
|
+
...input.extract,
|
|
395
|
+
});
|
|
396
|
+
if (!extracted.ok) {
|
|
397
|
+
throw new Error(extracted.error);
|
|
398
|
+
}
|
|
399
|
+
const intentDigest = computeIntentDigest(extracted.preimage);
|
|
400
|
+
const record = {
|
|
401
|
+
...buildApprovedScopeRecord({
|
|
402
|
+
xbriefRelPath: input.xbriefRelPath,
|
|
403
|
+
payload: input.payload,
|
|
404
|
+
approvedAt: input.approvedAt,
|
|
405
|
+
humanApproval: input.humanApproval,
|
|
406
|
+
}),
|
|
407
|
+
intentDigest,
|
|
408
|
+
digestAlgo: INTENT_DIGEST_ALGO,
|
|
409
|
+
};
|
|
410
|
+
const intentPath = approvedScopeIntentPath(input.projectRoot, record.planId);
|
|
411
|
+
const recordPath = approvedScopeRecordPath(input.projectRoot, record.planId);
|
|
412
|
+
writeApprovedScopePair({
|
|
413
|
+
projectRoot: input.projectRoot,
|
|
414
|
+
intentPath,
|
|
415
|
+
recordPath,
|
|
416
|
+
intentData: `${JSON.stringify(extracted.preimage, null, 2)}\n`,
|
|
417
|
+
recordData: `${JSON.stringify(record, null, 2)}\n`,
|
|
418
|
+
publishDest: input.publishDest,
|
|
419
|
+
restoreDest: input.restoreDest,
|
|
420
|
+
removeDest: input.removeDest,
|
|
421
|
+
lockWaitMs: input.lockWaitMs,
|
|
422
|
+
});
|
|
423
|
+
return { record, preimage: extracted.preimage, recordPath, intentPath };
|
|
424
|
+
}
|
|
425
|
+
//# sourceMappingURL=mint-artifacts.js.map
|
|
@@ -38,6 +38,8 @@ export interface AcPassBankRecord {
|
|
|
38
38
|
readonly scopeId: string;
|
|
39
39
|
readonly bankedAt: string;
|
|
40
40
|
readonly headSha: string | null;
|
|
41
|
+
/** Product+acceptance digest; missing/empty is stale for #3387 reuse. */
|
|
42
|
+
readonly productStateHash?: string | null;
|
|
41
43
|
readonly remainingTurns: number | null;
|
|
42
44
|
readonly remainingBudget: number | null;
|
|
43
45
|
readonly maxTurns: number | null;
|
|
@@ -75,6 +77,7 @@ export interface BankAcPassInput {
|
|
|
75
77
|
readonly surplus: SurplusEvaluation;
|
|
76
78
|
readonly nextAction: Exclude<AcPassNextAction, "still_open">;
|
|
77
79
|
readonly headSha?: string | null;
|
|
80
|
+
readonly productStateHash?: string | null;
|
|
78
81
|
readonly now?: string;
|
|
79
82
|
readonly environ?: Readonly<Record<string, string | undefined>>;
|
|
80
83
|
}
|
|
@@ -130,42 +133,6 @@ export declare function readAcPassBank(projectRoot: string, scopeId: string): Ac
|
|
|
130
133
|
* Append a post-bank finding to an existing bank record (report/chase/fix).
|
|
131
134
|
*/
|
|
132
135
|
export declare function recordPostBankFinding(projectRoot: string, scopeId: string, finding: PostBankFinding): AcPassBankRecord | null;
|
|
133
|
-
export interface BankEventPayload {
|
|
134
|
-
readonly type: "ac_pass_bank" | "ac_pass_post_bank_finding" | "ac_pass_deepening_skipped";
|
|
135
|
-
readonly schemaVersion: number;
|
|
136
|
-
readonly scopeId?: string;
|
|
137
|
-
readonly bankedAt?: string;
|
|
138
|
-
readonly nextAction?: string;
|
|
139
|
-
readonly hadSurplus?: boolean;
|
|
140
|
-
readonly surplusThreshold?: number;
|
|
141
|
-
readonly remainingFraction?: number | null;
|
|
142
|
-
readonly remainingTurns?: number | null;
|
|
143
|
-
readonly remainingBudget?: number | null;
|
|
144
|
-
readonly maxTurns?: number | null;
|
|
145
|
-
readonly maxBudget?: number | null;
|
|
146
|
-
readonly headSha?: string | null;
|
|
147
|
-
readonly path?: string;
|
|
148
|
-
readonly finding?: PostBankFinding;
|
|
149
|
-
readonly note?: string;
|
|
150
|
-
readonly [key: string]: unknown;
|
|
151
|
-
}
|
|
152
|
-
/**
|
|
153
|
-
* Format one JSONL bank-event line for run-summary telemetry (#3285 / #3282).
|
|
154
|
-
*/
|
|
155
|
-
export declare function formatBankEventLine(event: BankEventPayload): string;
|
|
156
|
-
/**
|
|
157
|
-
* Append bank event to DEFT_RUN_SUMMARY_PATH when set.
|
|
158
|
-
* Unset = silent. `-` = stdout. Failures warn once to stderr (fail-open).
|
|
159
|
-
*/
|
|
160
|
-
export declare function appendBankEventToRunSummary(input: {
|
|
161
|
-
readonly environ?: Readonly<Record<string, string | undefined>>;
|
|
162
|
-
readonly event: BankEventPayload;
|
|
163
|
-
readonly writeLine?: (path: string, line: string) => void;
|
|
164
|
-
readonly warn?: (message: string) => void;
|
|
165
|
-
}): {
|
|
166
|
-
written: boolean;
|
|
167
|
-
path: string | null;
|
|
168
|
-
};
|
|
169
136
|
/**
|
|
170
137
|
* Simulated run used by the surplus-insufficient acceptance test (#3285):
|
|
171
138
|
* AC passes → bank → extra checks would exceed budget → ship + report finding.
|
|
@@ -205,6 +172,7 @@ export interface MaybeBankOnAcPassInput {
|
|
|
205
172
|
*/
|
|
206
173
|
readonly executableRuns: number;
|
|
207
174
|
readonly quiet?: boolean;
|
|
175
|
+
readonly productStateHash?: string | null;
|
|
208
176
|
}
|
|
209
177
|
export interface MaybeBankOnAcPassResult {
|
|
210
178
|
readonly banked: boolean;
|
|
@@ -12,9 +12,10 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { createHash } from "node:crypto";
|
|
14
14
|
import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync } from "node:fs";
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
15
|
+
import { join, resolve } from "node:path";
|
|
16
|
+
import { containedWrite } from "../fs/contained-write.js";
|
|
17
17
|
import { DEFAULT_SURPLUS_THRESHOLD, resolveAcPassBanking, } from "../policy/ac-pass-banking.js";
|
|
18
|
+
import { RunSummaryEmitter } from "../run-summary/emit.js";
|
|
18
19
|
import { detectHardEffortBudget, formatDeepeningSkippedNote, recommendVerificationDepth, } from "./effort-budget.js";
|
|
19
20
|
/** Durable bank ledger directory (gitignored under `.deft/`). */
|
|
20
21
|
export const AC_PASS_BANK_DIR = ".deft/ac-pass-banks";
|
|
@@ -320,6 +321,7 @@ export function bankAcPass(input) {
|
|
|
320
321
|
scopeId: input.scopeId,
|
|
321
322
|
bankedAt,
|
|
322
323
|
headSha: input.headSha ?? null,
|
|
324
|
+
productStateHash: input.productStateHash ?? prior?.productStateHash ?? null,
|
|
323
325
|
remainingTurns: input.budget.remainingTurns,
|
|
324
326
|
remainingBudget: input.budget.remainingBudget,
|
|
325
327
|
maxTurns: input.budget.maxTurns,
|
|
@@ -358,25 +360,31 @@ export function bankAcPass(input) {
|
|
|
358
360
|
durablePath = tmpPath;
|
|
359
361
|
}
|
|
360
362
|
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
363
|
+
try {
|
|
364
|
+
const env = { ...(input.environ ?? process.env) };
|
|
365
|
+
const emitter = new RunSummaryEmitter({
|
|
366
|
+
projectRoot: root,
|
|
367
|
+
env,
|
|
368
|
+
component: "ac-pass-banking",
|
|
369
|
+
});
|
|
370
|
+
emitter.emitAcPassBank({
|
|
371
|
+
scope_id: record.scopeId,
|
|
372
|
+
banked_at: record.bankedAt,
|
|
373
|
+
next_action: record.nextAction,
|
|
374
|
+
had_surplus: record.hadSurplus,
|
|
375
|
+
surplus_threshold: record.surplusThreshold,
|
|
376
|
+
remaining_fraction: input.surplus.remainingFraction,
|
|
377
|
+
remaining_turns: record.remainingTurns,
|
|
378
|
+
remaining_budget: record.remainingBudget,
|
|
379
|
+
max_turns: record.maxTurns,
|
|
380
|
+
max_budget: record.maxBudget,
|
|
381
|
+
head_sha: record.headSha,
|
|
377
382
|
path: durablePath,
|
|
378
|
-
}
|
|
379
|
-
}
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
catch {
|
|
386
|
+
// fail-open
|
|
387
|
+
}
|
|
380
388
|
return record;
|
|
381
389
|
}
|
|
382
390
|
/**
|
|
@@ -487,6 +495,7 @@ function recoveredStubRecord(scopeId, findings) {
|
|
|
487
495
|
// valid primary ledger (#3285 Greptile residual).
|
|
488
496
|
bankedAt: "1970-01-01T00:00:00Z",
|
|
489
497
|
headSha: null,
|
|
498
|
+
productStateHash: null,
|
|
490
499
|
remainingTurns: null,
|
|
491
500
|
remainingBudget: null,
|
|
492
501
|
maxTurns: null,
|
|
@@ -616,63 +625,6 @@ export function recordPostBankFinding(projectRoot, scopeId, finding) {
|
|
|
616
625
|
}
|
|
617
626
|
return updated;
|
|
618
627
|
}
|
|
619
|
-
/**
|
|
620
|
-
* Format one JSONL bank-event line for run-summary telemetry (#3285 / #3282).
|
|
621
|
-
*/
|
|
622
|
-
export function formatBankEventLine(event) {
|
|
623
|
-
return JSON.stringify({
|
|
624
|
-
...event,
|
|
625
|
-
ts: typeof event.bankedAt === "string" ? event.bankedAt : utcIso(),
|
|
626
|
-
source: "ac-pass-banking",
|
|
627
|
-
issue: 3285,
|
|
628
|
-
});
|
|
629
|
-
}
|
|
630
|
-
/**
|
|
631
|
-
* Append bank event to DEFT_RUN_SUMMARY_PATH when set.
|
|
632
|
-
* Unset = silent. `-` = stdout. Failures warn once to stderr (fail-open).
|
|
633
|
-
*/
|
|
634
|
-
export function appendBankEventToRunSummary(input) {
|
|
635
|
-
const environ = input.environ ?? process.env;
|
|
636
|
-
const raw = (environ[ENV_RUN_SUMMARY_PATH] ?? "").trim();
|
|
637
|
-
if (!raw) {
|
|
638
|
-
return { written: false, path: null };
|
|
639
|
-
}
|
|
640
|
-
const line = `${formatBankEventLine(input.event)}\n`;
|
|
641
|
-
if (raw === "-") {
|
|
642
|
-
process.stdout.write(line);
|
|
643
|
-
return { written: true, path: "-" };
|
|
644
|
-
}
|
|
645
|
-
try {
|
|
646
|
-
if (input.writeLine) {
|
|
647
|
-
input.writeLine(raw, line);
|
|
648
|
-
}
|
|
649
|
-
else {
|
|
650
|
-
const abs = resolve(raw);
|
|
651
|
-
const parent = dirname(abs);
|
|
652
|
-
if (!existsSync(parent)) {
|
|
653
|
-
mkdirSync(parent, { recursive: true });
|
|
654
|
-
}
|
|
655
|
-
// Contain under the parent directory of the summary file.
|
|
656
|
-
containedWrite({
|
|
657
|
-
root: parent,
|
|
658
|
-
target: abs,
|
|
659
|
-
data: line,
|
|
660
|
-
mode: "append",
|
|
661
|
-
});
|
|
662
|
-
}
|
|
663
|
-
return { written: true, path: raw };
|
|
664
|
-
}
|
|
665
|
-
catch (err) {
|
|
666
|
-
const msg = err instanceof ContainedWriteError
|
|
667
|
-
? err.message
|
|
668
|
-
: err instanceof Error
|
|
669
|
-
? err.message
|
|
670
|
-
: String(err);
|
|
671
|
-
const warn = input.warn ?? ((m) => process.stderr.write(`${m}\n`));
|
|
672
|
-
warn(`[deft ac-pass-banking] run-summary write failed path=${raw}: ${msg} (#3285)`);
|
|
673
|
-
return { written: false, path: raw };
|
|
674
|
-
}
|
|
675
|
-
}
|
|
676
628
|
/**
|
|
677
629
|
* Simulated run used by the surplus-insufficient acceptance test (#3285):
|
|
678
630
|
* AC passes → bank → extra checks would exceed budget → ship + report finding.
|
|
@@ -748,6 +700,7 @@ export function maybeBankOnAcPass(input) {
|
|
|
748
700
|
surplus: decision.surplus,
|
|
749
701
|
nextAction,
|
|
750
702
|
headSha: input.headSha ?? null,
|
|
703
|
+
productStateHash: input.productStateHash ?? null,
|
|
751
704
|
now: input.now,
|
|
752
705
|
environ: input.environ,
|
|
753
706
|
});
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bank / same-session cache reuse for verify:ac and scope:complete (#3387).
|
|
3
|
+
*
|
|
4
|
+
* Green bank + matching product-state hash → accept without re-execution.
|
|
5
|
+
* Missing, stale, or mismatched hash → full walk. Empty/failing still refuse.
|
|
6
|
+
*/
|
|
7
|
+
import { type AcPassBankRecord } from "./ac-pass-banking.js";
|
|
8
|
+
import { type HashProductStateInput } from "./product-state-hash.js";
|
|
9
|
+
import { type CachedVerifyAcSnapshot, type VerifyAcSessionCacheRecord } from "./verify-ac-session-cache.js";
|
|
10
|
+
export type { AcServedFrom } from "./verify-ac-session-cache.js";
|
|
11
|
+
export type AcReuseDecision = {
|
|
12
|
+
readonly kind: "cache";
|
|
13
|
+
readonly servedFrom: "cache";
|
|
14
|
+
readonly hash: string;
|
|
15
|
+
readonly cache: VerifyAcSessionCacheRecord;
|
|
16
|
+
} | {
|
|
17
|
+
readonly kind: "bank";
|
|
18
|
+
readonly servedFrom: "bank";
|
|
19
|
+
readonly hash: string;
|
|
20
|
+
readonly bank: AcPassBankRecord;
|
|
21
|
+
} | {
|
|
22
|
+
readonly kind: "miss";
|
|
23
|
+
readonly servedFrom: "executed";
|
|
24
|
+
readonly hash: string | null;
|
|
25
|
+
readonly reason: string;
|
|
26
|
+
};
|
|
27
|
+
export interface ResolveAcReuseInput extends HashProductStateInput {
|
|
28
|
+
readonly scopeId?: string | null;
|
|
29
|
+
readonly sessionId?: string | null;
|
|
30
|
+
readonly env?: Readonly<Record<string, string | undefined>>;
|
|
31
|
+
/** Default true. Complete walk uses bank; check uses cache then bank. */
|
|
32
|
+
readonly allowCache?: boolean;
|
|
33
|
+
readonly allowBank?: boolean;
|
|
34
|
+
}
|
|
35
|
+
export declare function resolveScopeIdForAcReuse(plan: Record<string, unknown>, explicit?: string | null): string | null;
|
|
36
|
+
/**
|
|
37
|
+
* Decide whether a green bank or same-session cache can replace execution.
|
|
38
|
+
*/
|
|
39
|
+
export declare function resolveAcReuse(input: ResolveAcReuseInput): AcReuseDecision;
|
|
40
|
+
export declare function snapshotFromReuseFields(input: {
|
|
41
|
+
readonly ok: boolean;
|
|
42
|
+
readonly code: 0 | 1 | 2;
|
|
43
|
+
readonly message: string;
|
|
44
|
+
readonly commands: readonly unknown[];
|
|
45
|
+
readonly runs: readonly unknown[];
|
|
46
|
+
readonly rejected?: readonly unknown[];
|
|
47
|
+
readonly sourceRung: string;
|
|
48
|
+
readonly noneStated: boolean;
|
|
49
|
+
readonly acceptance: unknown;
|
|
50
|
+
readonly resolution: string;
|
|
51
|
+
readonly resolvedCommandCount: number;
|
|
52
|
+
}): CachedVerifyAcSnapshot;
|
|
53
|
+
//# sourceMappingURL=ac-pass-reuse.d.ts.map
|