@lannguyensi/harness 0.32.0 → 0.34.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/CHANGELOG.md +40 -0
- package/README.md +8 -5
- package/dist/cli/adopt/index.d.ts +7 -0
- package/dist/cli/adopt/index.js +10 -0
- package/dist/cli/adopt/index.js.map +1 -1
- package/dist/cli/apply/apply.d.ts +14 -0
- package/dist/cli/apply/apply.js +21 -6
- package/dist/cli/apply/apply.js.map +1 -1
- package/dist/cli/approve/branch-protection.d.ts +69 -0
- package/dist/cli/approve/branch-protection.js +157 -0
- package/dist/cli/approve/branch-protection.js.map +1 -0
- package/dist/cli/approve/understanding.d.ts +12 -0
- package/dist/cli/approve/understanding.js +50 -3
- package/dist/cli/approve/understanding.js.map +1 -1
- package/dist/cli/gc/index.d.ts +47 -0
- package/dist/cli/gc/index.js +148 -0
- package/dist/cli/gc/index.js.map +1 -0
- package/dist/cli/index.js +174 -14
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init/composer.js +11 -5
- package/dist/cli/init/composer.js.map +1 -1
- package/dist/cli/init/profiles.d.ts +2 -2
- package/dist/cli/init/profiles.js +2 -2
- package/dist/cli/init/templates.d.ts +1 -1
- package/dist/cli/init/templates.js +8 -4
- package/dist/cli/init/templates.js.map +1 -1
- package/dist/cli/pack/hook-branch-protection.d.ts +8 -0
- package/dist/cli/pack/hook-branch-protection.js +59 -15
- package/dist/cli/pack/hook-branch-protection.js.map +1 -1
- package/dist/cli/pack/hook-pre-tool-use.js +31 -2
- package/dist/cli/pack/hook-pre-tool-use.js.map +1 -1
- package/dist/cli/pack/hook-solution-acceptance.d.ts +2 -0
- package/dist/cli/pack/hook-solution-acceptance.js +24 -10
- package/dist/cli/pack/hook-solution-acceptance.js.map +1 -1
- package/dist/cli/pack/read-only-bash.js +127 -4
- package/dist/cli/pack/read-only-bash.js.map +1 -1
- package/dist/cli/uninstall/index.d.ts +24 -5
- package/dist/cli/uninstall/index.js +73 -6
- package/dist/cli/uninstall/index.js.map +1 -1
- package/dist/policy-packs/builtin/branch-protection-runtime.d.ts +47 -6
- package/dist/policy-packs/builtin/branch-protection-runtime.js +53 -6
- package/dist/policy-packs/builtin/branch-protection-runtime.js.map +1 -1
- package/dist/policy-packs/builtin/branch-protection.js +24 -14
- package/dist/policy-packs/builtin/branch-protection.js.map +1 -1
- package/dist/policy-packs/builtin/solution-acceptance-runtime.d.ts +18 -0
- package/dist/policy-packs/builtin/solution-acceptance-runtime.js +32 -0
- package/dist/policy-packs/builtin/solution-acceptance-runtime.js.map +1 -1
- package/dist/policy-packs/builtin/understanding-before-execution-runtime.d.ts +82 -10
- package/dist/policy-packs/builtin/understanding-before-execution-runtime.js +88 -22
- package/dist/policy-packs/builtin/understanding-before-execution-runtime.js.map +1 -1
- package/dist/policy-packs/builtin/understanding-before-execution.d.ts +11 -0
- package/dist/policy-packs/builtin/understanding-before-execution.js +15 -0
- package/dist/policy-packs/builtin/understanding-before-execution.js.map +1 -1
- package/package.json +3 -3
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { PolicyPack } from "../../schema/index.js";
|
|
2
|
+
import { type ApprovalMarker, type CheckApprovalMarkerOptions, type MarkerCheck } from "./understanding-before-execution-runtime.js";
|
|
2
3
|
export declare const PACK_NAME = "branch-protection";
|
|
3
4
|
/**
|
|
4
5
|
* Ledger tag written by the producer when the current branch is NOT in
|
|
@@ -8,14 +9,54 @@ export declare const PACK_NAME = "branch-protection";
|
|
|
8
9
|
*/
|
|
9
10
|
export declare const NON_PROTECTED_TAG_PREFIX = "branch:non-protected";
|
|
10
11
|
/**
|
|
11
|
-
* Operator escape-hatch tag
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
12
|
+
* Operator escape-hatch tag, kept as a best-effort AUDIT echo only.
|
|
13
|
+
*
|
|
14
|
+
* SECURITY (audit finding #39): this tag is NO LONGER a trusted override
|
|
15
|
+
* signal. The agent has direct `mcp__agent-grounding__ledger_add` access,
|
|
16
|
+
* so it could self-write `branch-protection-ack:<anything>` and bless its
|
|
17
|
+
* own protected-branch edit — exactly the self-approval backdoor the
|
|
18
|
+
* understanding gate closed in agent-tasks/88ca4bb3 by moving the
|
|
19
|
+
* canonical approval to a filesystem marker. The blocker now consults the
|
|
20
|
+
* operator-only marker file (see `checkBranchProtectionMarker` below); the
|
|
21
|
+
* `harness approve branch-protection` verb still records this ledger tag
|
|
22
|
+
* so `harness audit` / forensics keep a trail, but its presence alone
|
|
23
|
+
* never satisfies the gate. The trailing `:<reason>` stays free-form.
|
|
17
24
|
*/
|
|
18
25
|
export declare const ACK_TAG_PREFIX = "branch-protection-ack";
|
|
26
|
+
/**
|
|
27
|
+
* Marker-name namespace for an operator-written branch-protection
|
|
28
|
+
* override. The marker lives in the shared `.approvals/` directory under
|
|
29
|
+
* `harness.generated/` (the same directory the understanding gate uses),
|
|
30
|
+
* prefixed so it can never be confused with an understanding-gate session
|
|
31
|
+
* marker (`.approvals/<sessionId>`) or a task marker (`.approvals/task-<id>`):
|
|
32
|
+
* Claude Code / Codex session ids are UUIDs and never start with this
|
|
33
|
+
* literal, so the three namespaces stay disjoint.
|
|
34
|
+
*
|
|
35
|
+
* Why a marker and not the `branch-protection-ack` ledger tag: only a
|
|
36
|
+
* process the operator launched (their `!`-shell or any un-hooked
|
|
37
|
+
* terminal) can write under `harness.generated/` — Edit / Write / Bash
|
|
38
|
+
* are all gated, and the configured MCP servers expose no filesystem
|
|
39
|
+
* write. So the marker is the canonical override signal; the ledger row
|
|
40
|
+
* is a best-effort audit echo only.
|
|
41
|
+
*/
|
|
42
|
+
export declare const BRANCH_PROTECTION_MARKER_PREFIX = "branch-protection-";
|
|
43
|
+
/** Marker filename (inside `.approvals/`) for a session's branch-protection override. */
|
|
44
|
+
export declare function branchProtectionMarkerName(sessionId: string): string;
|
|
45
|
+
/**
|
|
46
|
+
* Operator-side: write the canonical branch-protection override marker for
|
|
47
|
+
* `sessionId`. Atomic (delegates to `writeApprovalMarker`). Caller is
|
|
48
|
+
* `harness approve branch-protection`, run from the operator's un-hooked
|
|
49
|
+
* shell; if the agent could reach this path the gate's value would
|
|
50
|
+
* collapse, so it lives behind the approve CLI.
|
|
51
|
+
*/
|
|
52
|
+
export declare function writeBranchProtectionMarker(generatedDir: string, sessionId: string, marker: ApprovalMarker): string;
|
|
53
|
+
/**
|
|
54
|
+
* Gate-side: is the operator's branch-protection override marker present
|
|
55
|
+
* for `sessionId`? Inherits `checkApprovalMarker`'s contract
|
|
56
|
+
* (existence-is-enough, symlink rejection, optional freshness via
|
|
57
|
+
* `maxAgeMs`); only the namespaced filename differs.
|
|
58
|
+
*/
|
|
59
|
+
export declare function checkBranchProtectionMarker(generatedDir: string, sessionId: string, opts?: CheckApprovalMarkerOptions): MarkerCheck;
|
|
19
60
|
/**
|
|
20
61
|
* Freshness window for the producer tag. Five minutes lets a single
|
|
21
62
|
* branch-check satisfy a whole edit batch without re-running for every
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
// hook branch-protection` (blocker) — both under `src/cli/`. This module
|
|
7
7
|
// is the small shared surface they pull from: tag formats, default
|
|
8
8
|
// protected list, config parsing.
|
|
9
|
+
import { checkApprovalMarker, writeApprovalMarker, } from "./understanding-before-execution-runtime.js";
|
|
9
10
|
export const PACK_NAME = "branch-protection";
|
|
10
11
|
/**
|
|
11
12
|
* Ledger tag written by the producer when the current branch is NOT in
|
|
@@ -15,14 +16,60 @@ export const PACK_NAME = "branch-protection";
|
|
|
15
16
|
*/
|
|
16
17
|
export const NON_PROTECTED_TAG_PREFIX = "branch:non-protected";
|
|
17
18
|
/**
|
|
18
|
-
* Operator escape-hatch tag
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
19
|
+
* Operator escape-hatch tag, kept as a best-effort AUDIT echo only.
|
|
20
|
+
*
|
|
21
|
+
* SECURITY (audit finding #39): this tag is NO LONGER a trusted override
|
|
22
|
+
* signal. The agent has direct `mcp__agent-grounding__ledger_add` access,
|
|
23
|
+
* so it could self-write `branch-protection-ack:<anything>` and bless its
|
|
24
|
+
* own protected-branch edit — exactly the self-approval backdoor the
|
|
25
|
+
* understanding gate closed in agent-tasks/88ca4bb3 by moving the
|
|
26
|
+
* canonical approval to a filesystem marker. The blocker now consults the
|
|
27
|
+
* operator-only marker file (see `checkBranchProtectionMarker` below); the
|
|
28
|
+
* `harness approve branch-protection` verb still records this ledger tag
|
|
29
|
+
* so `harness audit` / forensics keep a trail, but its presence alone
|
|
30
|
+
* never satisfies the gate. The trailing `:<reason>` stays free-form.
|
|
24
31
|
*/
|
|
25
32
|
export const ACK_TAG_PREFIX = "branch-protection-ack";
|
|
33
|
+
/**
|
|
34
|
+
* Marker-name namespace for an operator-written branch-protection
|
|
35
|
+
* override. The marker lives in the shared `.approvals/` directory under
|
|
36
|
+
* `harness.generated/` (the same directory the understanding gate uses),
|
|
37
|
+
* prefixed so it can never be confused with an understanding-gate session
|
|
38
|
+
* marker (`.approvals/<sessionId>`) or a task marker (`.approvals/task-<id>`):
|
|
39
|
+
* Claude Code / Codex session ids are UUIDs and never start with this
|
|
40
|
+
* literal, so the three namespaces stay disjoint.
|
|
41
|
+
*
|
|
42
|
+
* Why a marker and not the `branch-protection-ack` ledger tag: only a
|
|
43
|
+
* process the operator launched (their `!`-shell or any un-hooked
|
|
44
|
+
* terminal) can write under `harness.generated/` — Edit / Write / Bash
|
|
45
|
+
* are all gated, and the configured MCP servers expose no filesystem
|
|
46
|
+
* write. So the marker is the canonical override signal; the ledger row
|
|
47
|
+
* is a best-effort audit echo only.
|
|
48
|
+
*/
|
|
49
|
+
export const BRANCH_PROTECTION_MARKER_PREFIX = "branch-protection-";
|
|
50
|
+
/** Marker filename (inside `.approvals/`) for a session's branch-protection override. */
|
|
51
|
+
export function branchProtectionMarkerName(sessionId) {
|
|
52
|
+
return `${BRANCH_PROTECTION_MARKER_PREFIX}${sessionId}`;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Operator-side: write the canonical branch-protection override marker for
|
|
56
|
+
* `sessionId`. Atomic (delegates to `writeApprovalMarker`). Caller is
|
|
57
|
+
* `harness approve branch-protection`, run from the operator's un-hooked
|
|
58
|
+
* shell; if the agent could reach this path the gate's value would
|
|
59
|
+
* collapse, so it lives behind the approve CLI.
|
|
60
|
+
*/
|
|
61
|
+
export function writeBranchProtectionMarker(generatedDir, sessionId, marker) {
|
|
62
|
+
return writeApprovalMarker(generatedDir, branchProtectionMarkerName(sessionId), marker);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Gate-side: is the operator's branch-protection override marker present
|
|
66
|
+
* for `sessionId`? Inherits `checkApprovalMarker`'s contract
|
|
67
|
+
* (existence-is-enough, symlink rejection, optional freshness via
|
|
68
|
+
* `maxAgeMs`); only the namespaced filename differs.
|
|
69
|
+
*/
|
|
70
|
+
export function checkBranchProtectionMarker(generatedDir, sessionId, opts = {}) {
|
|
71
|
+
return checkApprovalMarker(generatedDir, branchProtectionMarkerName(sessionId), opts);
|
|
72
|
+
}
|
|
26
73
|
/**
|
|
27
74
|
* Freshness window for the producer tag. Five minutes lets a single
|
|
28
75
|
* branch-check satisfy a whole edit batch without re-running for every
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"branch-protection-runtime.js","sourceRoot":"","sources":["../../../src/policy-packs/builtin/branch-protection-runtime.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,EAAE;AACF,kEAAkE;AAClE,yEAAyE;AACzE,sEAAsE;AACtE,yEAAyE;AACzE,mEAAmE;AACnE,kCAAkC;
|
|
1
|
+
{"version":3,"file":"branch-protection-runtime.js","sourceRoot":"","sources":["../../../src/policy-packs/builtin/branch-protection-runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -16,18 +16,23 @@
|
|
|
16
16
|
// consults the ledger on every Write/Edit (or `apply_patch`) and
|
|
17
17
|
// emits a Claude Code deny envelope unless either:
|
|
18
18
|
// - a fresh (<5m) `branch:non-protected` tag exists, OR
|
|
19
|
-
// -
|
|
20
|
-
//
|
|
21
|
-
//
|
|
19
|
+
// - the operator-only override marker exists at
|
|
20
|
+
// `harness.generated/.approvals/branch-protection-<sessionId>`,
|
|
21
|
+
// written by `harness approve branch-protection`. The legacy
|
|
22
|
+
// `branch-protection-ack:` ledger tag is no longer trusted as an
|
|
23
|
+
// override (audit finding #39): it is agent-writable via
|
|
24
|
+
// `mcp__agent-grounding__ledger_add`, so it could self-bless an
|
|
25
|
+
// edit. The marker lives under `harness.generated/`, which Edit /
|
|
26
|
+
// Write / Bash are all gated from writing.
|
|
22
27
|
//
|
|
23
28
|
// The producer is also runnable on-demand from the operator's `!` shell
|
|
24
29
|
// — same CLI verb, no SessionStart event piped on stdin — so an agent
|
|
25
30
|
// that just branched can refresh the gate without restarting the
|
|
26
31
|
// session.
|
|
27
32
|
//
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
//
|
|
33
|
+
// Enabled per-installation via `harness pack add branch-protection`.
|
|
34
|
+
// The `full` init template wires it with `enabled: true` (see
|
|
35
|
+
// src/cli/init/templates.ts); the `solo` / `team` templates do not.
|
|
31
36
|
import { z } from "zod";
|
|
32
37
|
import { PolicyUxSchema } from "../../schema/policies.js";
|
|
33
38
|
import { DEFAULT_RUNTIME } from "../runtime.js";
|
|
@@ -72,7 +77,7 @@ function buildHooks(runtime) {
|
|
|
72
77
|
command: BLOCKER_COMMAND,
|
|
73
78
|
blocking: "hard",
|
|
74
79
|
budget_ms: 5000,
|
|
75
|
-
description: `Blocker: deny ${blockerMatch} on protected branches unless a fresh branch:non-protected tag or
|
|
80
|
+
description: `Blocker: deny ${blockerMatch} on protected branches unless a fresh branch:non-protected tag exists in the ledger or the operator-only override marker (harness approve branch-protection) is present.`,
|
|
76
81
|
},
|
|
77
82
|
];
|
|
78
83
|
}
|
|
@@ -113,7 +118,8 @@ While this pack is enabled, hooks are wired into the ${settingsArtefact}:
|
|
|
113
118
|
\`${blockerMatch}\`: refuses the tool call unless EITHER
|
|
114
119
|
- a \`${NON_PROTECTED_TAG_PREFIX}\` tag exists in the ledger from
|
|
115
120
|
within the last ${minutes} minutes, OR
|
|
116
|
-
-
|
|
121
|
+
- the operator-only override marker exists at
|
|
122
|
+
\`harness.generated/.approvals/branch-protection-<sessionId>\`.
|
|
117
123
|
|
|
118
124
|
## Escape hatches
|
|
119
125
|
|
|
@@ -122,12 +128,16 @@ While this pack is enabled, hooks are wired into the ${settingsArtefact}:
|
|
|
122
128
|
is gated by the Understanding Gate but the producer command is itself
|
|
123
129
|
a \`harness ...\` invocation that the gate's allowlist accepts.
|
|
124
130
|
|
|
125
|
-
- **Explicit override** (
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
deliberate reason to edit a protected branch
|
|
129
|
-
workflow patches,
|
|
130
|
-
|
|
131
|
+
- **Explicit override** (operator only): from an un-hooked shell run
|
|
132
|
+
\`harness approve branch-protection --session <sessionId>\`. This writes
|
|
133
|
+
the canonical approval marker the blocker consults. Use it when you have
|
|
134
|
+
a deliberate reason to edit a protected branch (version bumps, CI
|
|
135
|
+
workflow patches, hotfixes). SECURITY (audit finding #39): a
|
|
136
|
+
\`${ACK_TAG_PREFIX}:<reason>\` ledger tag is NO LONGER sufficient on its
|
|
137
|
+
own — it is agent-writable via \`mcp__agent-grounding__ledger_add\`, so
|
|
138
|
+
the gate would otherwise be self-approvable. The approve verb still
|
|
139
|
+
records that ledger tag for audit, but only the marker file (which the
|
|
140
|
+
agent cannot write) opens the gate.
|
|
131
141
|
|
|
132
142
|
## Out of scope (v1)
|
|
133
143
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"branch-protection.js","sourceRoot":"","sources":["../../../src/policy-packs/builtin/branch-protection.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAC5C,EAAE;AACF,sEAAsE;AACtE,uEAAuE;AACvE,sEAAsE;AACtE,wEAAwE;AACxE,EAAE;AACF,yDAAyD;AACzD,EAAE;AACF,0EAA0E;AAC1E,oEAAoE;AACpE,qEAAqE;AACrE,uCAAuC;AACvC,EAAE;AACF,kEAAkE;AAClE,sEAAsE;AACtE,wDAAwD;AACxD,+DAA+D;AAC/D,
|
|
1
|
+
{"version":3,"file":"branch-protection.js","sourceRoot":"","sources":["../../../src/policy-packs/builtin/branch-protection.ts"],"names":[],"mappings":"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"}
|
|
@@ -36,6 +36,15 @@ export interface Verdict {
|
|
|
36
36
|
}
|
|
37
37
|
/** Env knob that overrides the verdict directory (mirrors the producer). */
|
|
38
38
|
export declare const VERDICT_DIR_ENV = "SOLUTION_VERDICT_DIR";
|
|
39
|
+
/**
|
|
40
|
+
* Env knob that supplies the verdict id for SOLO / non-agent-tasks sessions.
|
|
41
|
+
* The completion-gate consults it ONLY when no agent-tasks `active-claim` is
|
|
42
|
+
* recorded (resolution order: active-claim first, then this env, then
|
|
43
|
+
* fail-closed), so a claimed session's id stays authoritative and cannot be
|
|
44
|
+
* redirected by an env var. A sessionId fallback is intentionally still NOT a
|
|
45
|
+
* source (the wrong-scope bug class understanding-gate closed).
|
|
46
|
+
*/
|
|
47
|
+
export declare const VERDICT_ID_ENV = "SOLUTION_VERDICT_ID";
|
|
39
48
|
/**
|
|
40
49
|
* Stable tail of the default verdict dir. The write-guard's reference
|
|
41
50
|
* detection matches on this so ANY spelling of the home prefix is caught
|
|
@@ -61,6 +70,15 @@ export declare function verdictDir(env?: NodeJS.ProcessEnv, homedir?: () => stri
|
|
|
61
70
|
*/
|
|
62
71
|
export declare function sanitizeVerdictId(id: string): string;
|
|
63
72
|
export declare function verdictPathFor(dir: string, id: string): string;
|
|
73
|
+
/**
|
|
74
|
+
* Resolve the explicit verdict id from `SOLUTION_VERDICT_ID`, or null when it
|
|
75
|
+
* is unset, blank, or not a safe single path segment. Validated through
|
|
76
|
+
* `sanitizeVerdictId` so a traversal-y or empty value fails closed here
|
|
77
|
+
* (returns null -> the gate denies) rather than reaching the marker read. This
|
|
78
|
+
* is the solo / non-agent-tasks fallback the completion-gate uses only when no
|
|
79
|
+
* active-claim is present.
|
|
80
|
+
*/
|
|
81
|
+
export declare function resolveExplicitVerdictId(env?: NodeJS.ProcessEnv): string | null;
|
|
64
82
|
/**
|
|
65
83
|
* Read + validate the verdict marker for `id`, or null when it is absent,
|
|
66
84
|
* unparseable, a symlink, or not a regular file. The lstat + symlink reject
|
|
@@ -75,6 +75,15 @@ export function resolveProtectedCompletionTools(pack) {
|
|
|
75
75
|
}
|
|
76
76
|
/** Env knob that overrides the verdict directory (mirrors the producer). */
|
|
77
77
|
export const VERDICT_DIR_ENV = "SOLUTION_VERDICT_DIR";
|
|
78
|
+
/**
|
|
79
|
+
* Env knob that supplies the verdict id for SOLO / non-agent-tasks sessions.
|
|
80
|
+
* The completion-gate consults it ONLY when no agent-tasks `active-claim` is
|
|
81
|
+
* recorded (resolution order: active-claim first, then this env, then
|
|
82
|
+
* fail-closed), so a claimed session's id stays authoritative and cannot be
|
|
83
|
+
* redirected by an env var. A sessionId fallback is intentionally still NOT a
|
|
84
|
+
* source (the wrong-scope bug class understanding-gate closed).
|
|
85
|
+
*/
|
|
86
|
+
export const VERDICT_ID_ENV = "SOLUTION_VERDICT_ID";
|
|
78
87
|
/**
|
|
79
88
|
* Stable tail of the default verdict dir. The write-guard's reference
|
|
80
89
|
* detection matches on this so ANY spelling of the home prefix is caught
|
|
@@ -116,6 +125,29 @@ export function sanitizeVerdictId(id) {
|
|
|
116
125
|
export function verdictPathFor(dir, id) {
|
|
117
126
|
return path.join(dir, `${sanitizeVerdictId(id)}.json`);
|
|
118
127
|
}
|
|
128
|
+
/**
|
|
129
|
+
* Resolve the explicit verdict id from `SOLUTION_VERDICT_ID`, or null when it
|
|
130
|
+
* is unset, blank, or not a safe single path segment. Validated through
|
|
131
|
+
* `sanitizeVerdictId` so a traversal-y or empty value fails closed here
|
|
132
|
+
* (returns null -> the gate denies) rather than reaching the marker read. This
|
|
133
|
+
* is the solo / non-agent-tasks fallback the completion-gate uses only when no
|
|
134
|
+
* active-claim is present.
|
|
135
|
+
*/
|
|
136
|
+
export function resolveExplicitVerdictId(env = process.env) {
|
|
137
|
+
const raw = env[VERDICT_ID_ENV];
|
|
138
|
+
if (typeof raw !== "string")
|
|
139
|
+
return null;
|
|
140
|
+
const trimmed = raw.trim();
|
|
141
|
+
if (trimmed.length === 0)
|
|
142
|
+
return null;
|
|
143
|
+
try {
|
|
144
|
+
sanitizeVerdictId(trimmed);
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
return trimmed;
|
|
150
|
+
}
|
|
119
151
|
/**
|
|
120
152
|
* Read + validate the verdict marker for `id`, or null when it is absent,
|
|
121
153
|
* unparseable, a symlink, or not a regular file. The lstat + symlink reject
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"solution-acceptance-runtime.js","sourceRoot":"","sources":["../../../src/policy-packs/builtin/solution-acceptance-runtime.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"solution-acceptance-runtime.js","sourceRoot":"","sources":["../../../src/policy-packs/builtin/solution-acceptance-runtime.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,EAAE;AACF,8EAA8E;AAC9E,8EAA8E;AAC9E,sEAAsE;AACtE,8EAA8E;AAC9E,4EAA4E;AAC5E,sEAAsE;AACtE,wEAAwE;AACxE,4EAA4E;AAC5E,6CAA6C;AAC7C,EAAE;AACF,2EAA2E;AAC3E,oCAAoC;AACpC,EAAE;AACF,0EAA0E;AAC1E,6EAA6E;AAC7E,wEAAwE;AACxE,8DAA8D;AAC9D,EAAE;AACF,6EAA6E;AAC7E,+EAA+E;AAC/E,8EAA8E;AAC9E,+EAA+E;AAC/E,uEAAuE;AACvE,EAAE;AACF,sEAAsE;AACtE,4EAA4E;AAC5E,4EAA4E;AAC5E,6EAA6E;AAC7E,0EAA0E;AAC1E,2EAA2E;AAC3E,2EAA2E;AAC3E,gBAAgB;AAEhB,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAGlC,MAAM,CAAC,MAAM,SAAS,GAAG,qBAAqB,CAAC;AAE/C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kCAAkC,GAAG;IAChD,aAAa;IACb,gBAAgB;IAChB,YAAY;IACZ,qBAAqB;CACb,CAAC;AAEX;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAC/B,uFAAuF,CAAC;AAE1F;;;;;;GAMG;AACH,MAAM,UAAU,+BAA+B,CAAC,IAAgB;IAC9D,MAAM,GAAG,GAAI,IAAI,CAAC,MAAkC,CAAC,4BAA4B,CAAC,CAAC;IACnF,IACE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;QAClB,GAAG,CAAC,MAAM,GAAG,CAAC;QACd,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,EACvD,CAAC;QACD,OAAO,GAAe,CAAC;IACzB,CAAC;IACD,OAAO,CAAC,GAAG,kCAAkC,CAAC,CAAC;AACjD,CAAC;AAeD,4EAA4E;AAC5E,MAAM,CAAC,MAAM,eAAe,GAAG,sBAAsB,CAAC;AAEtD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,qBAAqB,CAAC;AAEpD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,mBAAmB,CAAC,CAAC;AAElF;;;;;;;;GAQG;AACH,MAAM,UAAU,UAAU,CACxB,MAAyB,OAAO,CAAC,GAAG,EACpC,UAAwB,EAAE,CAAC,OAAO;IAElC,MAAM,QAAQ,GAAG,GAAG,CAAC,eAAe,CAAC,CAAC;IACtC,IAAI,QAAQ,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC5D,MAAM,GAAG,GAAG,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAClC,MAAM,IAAI,GACR,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC/E,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,iBAAiB,EAAE,mBAAmB,CAAC,CAAC;AACjE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,EAAU;IAC1C,MAAM,OAAO,GAAG,EAAE,CAAC,OAAO,CAAC,kBAAkB,EAAE,GAAG,CAAC,CAAC;IACpD,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACpC,IAAI,IAAI,KAAK,EAAE,IAAI,IAAI,KAAK,GAAG,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QACjD,MAAM,IAAI,KAAK,CAAC,uBAAuB,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,GAAW,EAAE,EAAU;IACpD,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,iBAAiB,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC;AACzD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,wBAAwB,CACtC,MAAyB,OAAO,CAAC,GAAG;IAEpC,MAAM,GAAG,GAAG,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACzC,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IAC3B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACtC,IAAI,CAAC;QACH,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,GAAW,EAAE,EAAU;IACjD,IAAI,CAAS,CAAC;IACd,IAAI,CAAC;QACH,CAAC,GAAG,cAAc,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC,CAAC,aAAa;IAC5B,CAAC;IACD,IAAI,IAAc,CAAC;IACnB,IAAI,CAAC;QACH,IAAI,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE;QAAE,OAAO,IAAI,CAAC;IACzD,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAqB,CAAC;QACnD,IACE,OAAO,MAAM,CAAC,EAAE,KAAK,QAAQ;YAC7B,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ;YAC/B,OAAO,MAAM,CAAC,KAAK,KAAK,SAAS,EACjC,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO;YACL,EAAE,EAAE,MAAM,CAAC,EAAE;YACb,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,UAAU,EAAE,OAAO,MAAM,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;YACzE,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE;YAC/D,SAAS,EAAE,OAAO,MAAM,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE;YACvE,MAAM,EAAE,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE;SAC/D,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAQD;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAC1B,OAAuB,EACvB,WAA0B,EAC1B,EAAU;IAEV,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO;YACL,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,gDAAgD,EAAE,uDAAuD;YACjH,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACnB,MAAM,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAClF,OAAO;YACL,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,oCAAoC,EAAE,iBAAiB,GAAG,uCAAuC;YACzG,OAAO;SACR,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,OAAO;YACL,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,mEAAmE,EAAE,cAAc;YAC3F,OAAO;SACR,CAAC;IACJ,CAAC;IACD,IAAI,OAAO,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;QACjC,OAAO;YACL,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,0CAA0C,EAAE,kBAAkB,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,kBAAkB,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,8CAA8C;YACrL,OAAO;SACR,CAAC;IACJ,CAAC;IACD,OAAO;QACL,OAAO,EAAE,IAAI;QACb,MAAM,EAAE,oCAAoC,EAAE,sBAAsB,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,gBAAgB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,GAAG,GAAG,CAAC,IAAI;QACnJ,OAAO;KACR,CAAC;AACJ,CAAC;AAED,qCAAqC;AAErC;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,MAAc,EAAE,GAAW,EAAE,GAAY;IACnE,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACpE,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACjC,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;QACvC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;QACtB,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,CAAC;IAC/C,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAC7C,OAAO,GAAG,KAAK,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;AACxE,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,UAAU,wBAAwB,CAAC,OAAe,EAAE,GAAW;IACnE,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACtE,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IAChC,4DAA4D;IAC5D,IACE,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC;QACrB,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC;QACjC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC;QAClC,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,EAC5C,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,yEAAyE;IACzE,oEAAoE;IACpE,qEAAqE;IACrE,0EAA0E;IAC1E,uEAAuE;IACvE,2EAA2E;IAC3E,uEAAuE;IACvE,sEAAsE;IACtE,yEAAyE;IACzE,6EAA6E;IAC7E,4EAA4E;IAC5E,mEAAmE;IACnE,oBAAoB;IACpB,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAC1B,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC;QAC3E,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;IAC9D,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -12,6 +12,18 @@ export interface PersistedReport {
|
|
|
12
12
|
sessionId: string | null;
|
|
13
13
|
approvalStatus: string | null;
|
|
14
14
|
approvedAt: string | null;
|
|
15
|
+
/**
|
|
16
|
+
* ISO timestamp the producer stamped when it wrote the report; null
|
|
17
|
+
* for legacy reports without the field.
|
|
18
|
+
*/
|
|
19
|
+
createdAt: string | null;
|
|
20
|
+
/**
|
|
21
|
+
* Effective creation time in epoch ms, resolved `createdAt` →
|
|
22
|
+
* filename ISO prefix → file mtime. Unlike mtime alone this survives
|
|
23
|
+
* the approval rewrite (which bumps mtime and would otherwise make a
|
|
24
|
+
* weeks-old report sort as the freshest, harness-discovery C1).
|
|
25
|
+
*/
|
|
26
|
+
createdAtMs: number;
|
|
15
27
|
}
|
|
16
28
|
/**
|
|
17
29
|
* Env var the persisted-report directory can be set from. Honored by
|
|
@@ -44,14 +56,39 @@ export declare function reportsDirForManifest(manifestPath: string): string;
|
|
|
44
56
|
/** Build the per-session ledger tag the pack searches for. */
|
|
45
57
|
export declare function approvedLedgerTagFor(sessionId: string): string;
|
|
46
58
|
/**
|
|
47
|
-
* List persisted reports under `dir`, newest-first by
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
59
|
+
* List persisted reports under `dir`, newest-first by creation time
|
|
60
|
+
* (JSON `createdAt`, falling back to the filename ISO prefix, falling
|
|
61
|
+
* back to mtime). Missing directory returns []. Any I/O error on a
|
|
62
|
+
* single file is silently skipped; the caller falls through to the
|
|
63
|
+
* ledger result.
|
|
64
|
+
*
|
|
65
|
+
* Creation time, NOT mtime, is the sort key: `harness approve
|
|
66
|
+
* understanding` rewrites the report it flips, which bumps mtime and
|
|
67
|
+
* made an old just-approved report sort as the freshest
|
|
68
|
+
* (harness-discovery C1). mtime survives only as the last-resort
|
|
69
|
+
* fallback for files that carry neither timestamp.
|
|
53
70
|
*/
|
|
54
71
|
export declare function listPersistedReports(dir: string): PersistedReport[];
|
|
72
|
+
/**
|
|
73
|
+
* Maximum age a sessionId-null report may have for the tolerant
|
|
74
|
+
* fallback to adopt it on the `harness approve understanding` path.
|
|
75
|
+
* Sized for the real flow (Stop hook persists at turn end, operator
|
|
76
|
+
* approves within minutes) with slack for a slow read-through. Live
|
|
77
|
+
* repro that motivated it: a 17-day-old pending report got adopted,
|
|
78
|
+
* validated, and stamped for a fresh session because the producer had
|
|
79
|
+
* silently failed to persist the fresh report (harness-discovery C1,
|
|
80
|
+
* friction-log #67).
|
|
81
|
+
*/
|
|
82
|
+
export declare const TOLERANT_FALLBACK_MAX_AGE_MS: number;
|
|
83
|
+
/**
|
|
84
|
+
* Tolerance for a sessionId-less candidate whose `createdAt` lies in
|
|
85
|
+
* the FUTURE relative to the approve-time clock. A future creation
|
|
86
|
+
* time is suspect either way: a forged `createdAt` (the producer's
|
|
87
|
+
* Metadata block lets the agent author it) or serious clock skew.
|
|
88
|
+
* Beyond this skew the fallback rejects the candidate just like a
|
|
89
|
+
* stale one rather than trusting a timestamp that cannot be right.
|
|
90
|
+
*/
|
|
91
|
+
export declare const TOLERANT_FALLBACK_FUTURE_SKEW_MS: number;
|
|
55
92
|
export interface FindReportOptions {
|
|
56
93
|
/**
|
|
57
94
|
* Behaviour of the sessionId-null tolerant fallback (older Stop-hook
|
|
@@ -70,15 +107,50 @@ export interface FindReportOptions {
|
|
|
70
107
|
* report into the live session (harness/0dce3880 friction #1).
|
|
71
108
|
*/
|
|
72
109
|
tolerantFallback?: "any" | "uncompleted";
|
|
110
|
+
/**
|
|
111
|
+
* Maximum age (relative to `now`) of a sessionId-null candidate the
|
|
112
|
+
* tolerant fallback may adopt; older candidates are skipped and
|
|
113
|
+
* surfaced via `FindReportSelection.staleRejected`. Strict sessionId
|
|
114
|
+
* matches are never age-limited. Unset means no limit (the legacy
|
|
115
|
+
* gate-read / expiry contract).
|
|
116
|
+
*/
|
|
117
|
+
maxFallbackAgeMs?: number;
|
|
118
|
+
/** Clock anchor for the age computation; defaults to the wall clock. */
|
|
119
|
+
now?: Date;
|
|
120
|
+
}
|
|
121
|
+
export interface FindReportSelection {
|
|
122
|
+
report: PersistedReport | null;
|
|
123
|
+
/**
|
|
124
|
+
* True when `report` was adopted via the sessionId-null tolerant
|
|
125
|
+
* fallback rather than a strict sessionId match. Callers that bind
|
|
126
|
+
* the report to a session (the approve flow) surface this loudly so
|
|
127
|
+
* the operator can verify the adoption.
|
|
128
|
+
*/
|
|
129
|
+
fallbackAdopted: boolean;
|
|
130
|
+
/**
|
|
131
|
+
* sessionId-null candidates skipped for exceeding `maxFallbackAgeMs`,
|
|
132
|
+
* newest first. Lets the caller distinguish "no report at all" from
|
|
133
|
+
* "only stale candidates existed", which are different failures: the
|
|
134
|
+
* latter usually means the producer failed to persist the fresh
|
|
135
|
+
* report (harness-discovery C1).
|
|
136
|
+
*/
|
|
137
|
+
staleRejected: PersistedReport[];
|
|
73
138
|
}
|
|
74
139
|
/**
|
|
75
|
-
*
|
|
140
|
+
* Select the freshest report for a given session_id, or the freshest
|
|
76
141
|
* applicable report when the persisted file lacks a sessionId field
|
|
77
|
-
* (older package versions). null when nothing matches.
|
|
142
|
+
* (older package versions). `report: null` when nothing matches.
|
|
78
143
|
*
|
|
79
144
|
* The strict (sessionId-equals) match always wins. The tolerant
|
|
80
|
-
* fallback's appetite is controlled by `opts.tolerantFallback`
|
|
81
|
-
* `FindReportOptions`.
|
|
145
|
+
* fallback's appetite is controlled by `opts.tolerantFallback` and
|
|
146
|
+
* `opts.maxFallbackAgeMs` — see `FindReportOptions`. The selection
|
|
147
|
+
* result carries enough context (`fallbackAdopted`, `staleRejected`)
|
|
148
|
+
* for the caller to be loud about non-strict adoptions.
|
|
149
|
+
*/
|
|
150
|
+
export declare function selectReportForSession(reports: PersistedReport[], sessionId: string, opts?: FindReportOptions): FindReportSelection;
|
|
151
|
+
/**
|
|
152
|
+
* Back-compat wrapper around `selectReportForSession` for callers that
|
|
153
|
+
* only need the report (the gate read and expiry paths).
|
|
82
154
|
*/
|
|
83
155
|
export declare function findLatestReportForSession(reports: PersistedReport[], sessionId: string, opts?: FindReportOptions): PersistedReport | null;
|
|
84
156
|
export interface PersistedReportApprovalCheck {
|
|
@@ -85,7 +85,20 @@ function safeJsonParse(text) {
|
|
|
85
85
|
return null;
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
|
-
|
|
88
|
+
/**
|
|
89
|
+
* Parse the ISO prefix of a producer filename
|
|
90
|
+
* (`2026-05-24T06-16-39-409Z-<slug>-<hash>.json`) into epoch ms. The
|
|
91
|
+
* producer flattens `:` and `.` to `-` for filesystem safety; undo that
|
|
92
|
+
* before `Date.parse`. null when the name does not carry the prefix.
|
|
93
|
+
*/
|
|
94
|
+
function parseFilenameIsoMs(name) {
|
|
95
|
+
const m = /^(\d{4}-\d{2}-\d{2})T(\d{2})-(\d{2})-(\d{2})-(\d{3})Z/.exec(name);
|
|
96
|
+
if (!m)
|
|
97
|
+
return null;
|
|
98
|
+
const ms = Date.parse(`${m[1]}T${m[2]}:${m[3]}:${m[4]}.${m[5]}Z`);
|
|
99
|
+
return Number.isNaN(ms) ? null : ms;
|
|
100
|
+
}
|
|
101
|
+
function readPersistedReport(filePath, mtimeMs) {
|
|
89
102
|
let raw;
|
|
90
103
|
try {
|
|
91
104
|
raw = fs.readFileSync(filePath, "utf8");
|
|
@@ -97,20 +110,32 @@ function readPersistedReport(filePath) {
|
|
|
97
110
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
98
111
|
return null;
|
|
99
112
|
const obj = parsed;
|
|
113
|
+
const createdAt = typeof obj["createdAt"] === "string" ? obj["createdAt"] : null;
|
|
114
|
+
const createdAtJsonMs = createdAt !== null ? Date.parse(createdAt) : Number.NaN;
|
|
115
|
+
const createdAtMs = !Number.isNaN(createdAtJsonMs)
|
|
116
|
+
? createdAtJsonMs
|
|
117
|
+
: (parseFilenameIsoMs(path.basename(filePath)) ?? mtimeMs);
|
|
100
118
|
return {
|
|
101
119
|
filePath,
|
|
102
120
|
sessionId: typeof obj["sessionId"] === "string" ? obj["sessionId"] : null,
|
|
103
121
|
approvalStatus: typeof obj["approvalStatus"] === "string" ? obj["approvalStatus"] : null,
|
|
104
122
|
approvedAt: typeof obj["approvedAt"] === "string" ? obj["approvedAt"] : null,
|
|
123
|
+
createdAt,
|
|
124
|
+
createdAtMs,
|
|
105
125
|
};
|
|
106
126
|
}
|
|
107
127
|
/**
|
|
108
|
-
* List persisted reports under `dir`, newest-first by
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
128
|
+
* List persisted reports under `dir`, newest-first by creation time
|
|
129
|
+
* (JSON `createdAt`, falling back to the filename ISO prefix, falling
|
|
130
|
+
* back to mtime). Missing directory returns []. Any I/O error on a
|
|
131
|
+
* single file is silently skipped; the caller falls through to the
|
|
132
|
+
* ledger result.
|
|
133
|
+
*
|
|
134
|
+
* Creation time, NOT mtime, is the sort key: `harness approve
|
|
135
|
+
* understanding` rewrites the report it flips, which bumps mtime and
|
|
136
|
+
* made an old just-approved report sort as the freshest
|
|
137
|
+
* (harness-discovery C1). mtime survives only as the last-resort
|
|
138
|
+
* fallback for files that carry neither timestamp.
|
|
114
139
|
*/
|
|
115
140
|
export function listPersistedReports(dir) {
|
|
116
141
|
let names;
|
|
@@ -134,34 +159,61 @@ export function listPersistedReports(dir) {
|
|
|
134
159
|
}
|
|
135
160
|
if (!stat.isFile())
|
|
136
161
|
continue;
|
|
137
|
-
const report = readPersistedReport(full);
|
|
162
|
+
const report = readPersistedReport(full, stat.mtimeMs);
|
|
138
163
|
if (!report)
|
|
139
164
|
continue;
|
|
140
|
-
reports.push(
|
|
165
|
+
reports.push(report);
|
|
141
166
|
}
|
|
142
|
-
reports.sort((a, b) => b.
|
|
143
|
-
return reports
|
|
167
|
+
reports.sort((a, b) => b.createdAtMs - a.createdAtMs);
|
|
168
|
+
return reports;
|
|
144
169
|
}
|
|
145
170
|
/**
|
|
146
|
-
*
|
|
171
|
+
* Maximum age a sessionId-null report may have for the tolerant
|
|
172
|
+
* fallback to adopt it on the `harness approve understanding` path.
|
|
173
|
+
* Sized for the real flow (Stop hook persists at turn end, operator
|
|
174
|
+
* approves within minutes) with slack for a slow read-through. Live
|
|
175
|
+
* repro that motivated it: a 17-day-old pending report got adopted,
|
|
176
|
+
* validated, and stamped for a fresh session because the producer had
|
|
177
|
+
* silently failed to persist the fresh report (harness-discovery C1,
|
|
178
|
+
* friction-log #67).
|
|
179
|
+
*/
|
|
180
|
+
export const TOLERANT_FALLBACK_MAX_AGE_MS = 15 * 60_000;
|
|
181
|
+
/**
|
|
182
|
+
* Tolerance for a sessionId-less candidate whose `createdAt` lies in
|
|
183
|
+
* the FUTURE relative to the approve-time clock. A future creation
|
|
184
|
+
* time is suspect either way: a forged `createdAt` (the producer's
|
|
185
|
+
* Metadata block lets the agent author it) or serious clock skew.
|
|
186
|
+
* Beyond this skew the fallback rejects the candidate just like a
|
|
187
|
+
* stale one rather than trusting a timestamp that cannot be right.
|
|
188
|
+
*/
|
|
189
|
+
export const TOLERANT_FALLBACK_FUTURE_SKEW_MS = 5 * 60_000;
|
|
190
|
+
/**
|
|
191
|
+
* Select the freshest report for a given session_id, or the freshest
|
|
147
192
|
* applicable report when the persisted file lacks a sessionId field
|
|
148
|
-
* (older package versions). null when nothing matches.
|
|
193
|
+
* (older package versions). `report: null` when nothing matches.
|
|
149
194
|
*
|
|
150
195
|
* The strict (sessionId-equals) match always wins. The tolerant
|
|
151
|
-
* fallback's appetite is controlled by `opts.tolerantFallback`
|
|
152
|
-
* `FindReportOptions`.
|
|
196
|
+
* fallback's appetite is controlled by `opts.tolerantFallback` and
|
|
197
|
+
* `opts.maxFallbackAgeMs` — see `FindReportOptions`. The selection
|
|
198
|
+
* result carries enough context (`fallbackAdopted`, `staleRejected`)
|
|
199
|
+
* for the caller to be loud about non-strict adoptions.
|
|
153
200
|
*/
|
|
154
|
-
export function
|
|
201
|
+
export function selectReportForSession(reports, sessionId, opts = {}) {
|
|
155
202
|
// Strict match first.
|
|
156
203
|
for (const r of reports) {
|
|
157
|
-
if (r.sessionId === sessionId)
|
|
158
|
-
return r;
|
|
204
|
+
if (r.sessionId === sessionId) {
|
|
205
|
+
return { report: r, fallbackAdopted: false, staleRejected: [] };
|
|
206
|
+
}
|
|
159
207
|
}
|
|
160
208
|
// Tolerant fallback: a report without sessionId is treated as
|
|
161
209
|
// applicable to whichever session is asking. Only kicks in when no
|
|
162
|
-
// sessionId-tagged report exists
|
|
163
|
-
//
|
|
210
|
+
// matching sessionId-tagged report exists — which includes the case
|
|
211
|
+
// where the producer Stop hook silently failed to persist the live
|
|
212
|
+
// session's report, so the candidates here may be entirely unrelated
|
|
213
|
+
// leftovers. `maxFallbackAgeMs` is the guard against adopting those.
|
|
164
214
|
const mode = opts.tolerantFallback ?? "any";
|
|
215
|
+
const nowMs = (opts.now ?? new Date()).getTime();
|
|
216
|
+
const staleRejected = [];
|
|
165
217
|
for (const r of reports) {
|
|
166
218
|
if (r.sessionId !== null)
|
|
167
219
|
continue;
|
|
@@ -172,9 +224,23 @@ export function findLatestReportForSession(reports, sessionId, opts = {}) {
|
|
|
172
224
|
// the live session to a stale, unrelated report.
|
|
173
225
|
continue;
|
|
174
226
|
}
|
|
175
|
-
|
|
227
|
+
if (opts.maxFallbackAgeMs !== undefined) {
|
|
228
|
+
const ageMs = nowMs - r.createdAtMs;
|
|
229
|
+
if (ageMs > opts.maxFallbackAgeMs || ageMs < -TOLERANT_FALLBACK_FUTURE_SKEW_MS) {
|
|
230
|
+
staleRejected.push(r);
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return { report: r, fallbackAdopted: true, staleRejected };
|
|
176
235
|
}
|
|
177
|
-
return null;
|
|
236
|
+
return { report: null, fallbackAdopted: false, staleRejected };
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Back-compat wrapper around `selectReportForSession` for callers that
|
|
240
|
+
* only need the report (the gate read and expiry paths).
|
|
241
|
+
*/
|
|
242
|
+
export function findLatestReportForSession(reports, sessionId, opts = {}) {
|
|
243
|
+
return selectReportForSession(reports, sessionId, opts).report;
|
|
178
244
|
}
|
|
179
245
|
/**
|
|
180
246
|
* Phase 6 #6 — substring-pollution defence shared by every PreToolUse
|