@lannguyensi/harness 0.52.0 → 0.53.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 +43 -0
- package/dist/cli/doctor/bypass-without-auto-approve.d.ts +38 -0
- package/dist/cli/doctor/bypass-without-auto-approve.js +89 -0
- package/dist/cli/doctor/bypass-without-auto-approve.js.map +1 -0
- package/dist/cli/doctor/format.js +7 -0
- package/dist/cli/doctor/format.js.map +1 -1
- package/dist/cli/doctor/index.js +17 -0
- package/dist/cli/doctor/index.js.map +1 -1
- package/dist/cli/doctor/types.d.ts +11 -0
- package/dist/cli/doctor/types.js.map +1 -1
- package/dist/cli/doctor/ug-auto-approvals.js +18 -43
- package/dist/cli/doctor/ug-auto-approvals.js.map +1 -1
- package/dist/cli/gc/index.d.ts +13 -2
- package/dist/cli/gc/index.js +203 -1
- package/dist/cli/gc/index.js.map +1 -1
- package/dist/cli/index.js +44 -6
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init/composer.js +9 -0
- package/dist/cli/init/composer.js.map +1 -1
- package/dist/cli/init/profiles.d.ts +2 -2
- package/dist/cli/init/profiles.js +11 -0
- package/dist/cli/init/profiles.js.map +1 -1
- package/dist/cli/init/templates.d.ts +1 -1
- package/dist/cli/init/templates.js +11 -0
- package/dist/cli/init/templates.js.map +1 -1
- package/dist/cli/pack/hook-pre-tool-use.js +20 -21
- package/dist/cli/pack/hook-pre-tool-use.js.map +1 -1
- package/dist/cli/pack/index.d.ts +1 -0
- package/dist/cli/pack/index.js +1 -0
- package/dist/cli/pack/index.js.map +1 -1
- package/dist/cli/pack/upgrade.d.ts +32 -0
- package/dist/cli/pack/upgrade.js +273 -0
- package/dist/cli/pack/upgrade.js.map +1 -0
- package/dist/io/read-json-dir-entries.d.ts +36 -0
- package/dist/io/read-json-dir-entries.js +38 -0
- package/dist/io/read-json-dir-entries.js.map +1 -0
- package/dist/policy-packs/builtin/understanding-before-execution/auto-approve-default.d.ts +35 -0
- package/dist/policy-packs/builtin/understanding-before-execution/auto-approve-default.js +86 -0
- package/dist/policy-packs/builtin/understanding-before-execution/auto-approve-default.js.map +1 -0
- package/dist/policy-packs/builtin/understanding-before-execution/delegation-markers.d.ts +14 -0
- package/dist/policy-packs/builtin/understanding-before-execution/delegation-markers.js +14 -0
- package/dist/policy-packs/builtin/understanding-before-execution/delegation-markers.js.map +1 -1
- package/dist/policy-packs/builtin/understanding-before-execution/index.d.ts +3 -1
- package/dist/policy-packs/builtin/understanding-before-execution/index.js +18 -1
- package/dist/policy-packs/builtin/understanding-before-execution/index.js.map +1 -1
- package/dist/policy-packs/builtin/understanding-before-execution/permission-mode-observations.d.ts +71 -0
- package/dist/policy-packs/builtin/understanding-before-execution/permission-mode-observations.js +169 -0
- package/dist/policy-packs/builtin/understanding-before-execution/permission-mode-observations.js.map +1 -0
- package/dist/runtime/git-context.js +7 -1
- package/dist/runtime/git-context.js.map +1 -1
- package/package.json +1 -1
|
@@ -15,10 +15,8 @@
|
|
|
15
15
|
// marker's HMAC signature (that is `checkApprovalMarker`'s job at gate
|
|
16
16
|
// time; a forged marker still gets rejected there regardless of what
|
|
17
17
|
// this listing shows) and it never blocks or writes anything.
|
|
18
|
-
import * as fs from "node:fs";
|
|
19
18
|
import * as path from "node:path";
|
|
20
|
-
import {
|
|
21
|
-
import { safeJsonParse } from "../../io/safe-json-parse.js";
|
|
19
|
+
import { readJsonDirEntriesRejectingSymlinks } from "../../io/read-json-dir-entries.js";
|
|
22
20
|
import { BRANCH_PROTECTION_MARKER_PREFIX } from "../../policy-packs/builtin/branch-protection-runtime.js";
|
|
23
21
|
import { APPROVAL_MARKER_DIRNAME, APPROVAL_MARKER_TASK_PREFIX, parseAutoApprovedBy, } from "../../policy-packs/builtin/understanding-before-execution/index.js";
|
|
24
22
|
/** Default doctor window when `--recent-sessions` is not passed. */
|
|
@@ -44,47 +42,24 @@ function emptySection(windowSize) {
|
|
|
44
42
|
export function buildUgAutoApprovals(generatedDir, opts) {
|
|
45
43
|
const windowSize = opts.recentSessions;
|
|
46
44
|
const dir = path.join(generatedDir, APPROVAL_MARKER_DIRNAME);
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
45
|
+
// A symlink / non-regular / raced-away entry is not a session marker this
|
|
46
|
+
// listing owns an opinion about; the shared reader skips it silently
|
|
47
|
+
// rather than counting it as "unreadable" (that count is for a FILE we
|
|
48
|
+
// expected to be a marker but could not parse).
|
|
49
|
+
const { dirPresent, entries: readable, unreadableCount } = readJsonDirEntriesRejectingSymlinks(dir, {
|
|
50
|
+
skip: (name) => name.startsWith(APPROVAL_MARKER_TASK_PREFIX) || name.startsWith(BRANCH_PROTECTION_MARKER_PREFIX),
|
|
51
|
+
parse: (raw, name) => {
|
|
52
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw))
|
|
53
|
+
return null;
|
|
54
|
+
const obj = raw;
|
|
55
|
+
const approvedAt = obj["approvedAt"];
|
|
56
|
+
if (typeof approvedAt !== "string" || Number.isNaN(Date.parse(approvedAt)))
|
|
57
|
+
return null;
|
|
58
|
+
return { sessionId: name, approvedAt, approvedBy: obj["approvedBy"] };
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
if (!dirPresent)
|
|
52
62
|
return emptySection(windowSize);
|
|
53
|
-
}
|
|
54
|
-
const readable = [];
|
|
55
|
-
let unreadableCount = 0;
|
|
56
|
-
for (const d of dirents) {
|
|
57
|
-
const name = d.name;
|
|
58
|
-
if (name.startsWith(APPROVAL_MARKER_TASK_PREFIX))
|
|
59
|
-
continue;
|
|
60
|
-
if (name.startsWith(BRANCH_PROTECTION_MARKER_PREFIX))
|
|
61
|
-
continue;
|
|
62
|
-
const full = path.join(dir, name);
|
|
63
|
-
const read = readRegularFileRejectingSymlink(full);
|
|
64
|
-
// A symlink / non-regular / raced-away entry is not a session marker
|
|
65
|
-
// this listing owns an opinion about; skip silently rather than
|
|
66
|
-
// counting it as "unreadable" (that count is for a FILE we expected
|
|
67
|
-
// to be a marker but could not parse).
|
|
68
|
-
if (read.kind === "symlink" || read.kind === "not-regular" || read.kind === "missing") {
|
|
69
|
-
continue;
|
|
70
|
-
}
|
|
71
|
-
if (read.kind === "unreadable") {
|
|
72
|
-
unreadableCount++;
|
|
73
|
-
continue;
|
|
74
|
-
}
|
|
75
|
-
const parsed = safeJsonParse(read.content);
|
|
76
|
-
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
77
|
-
unreadableCount++;
|
|
78
|
-
continue;
|
|
79
|
-
}
|
|
80
|
-
const obj = parsed;
|
|
81
|
-
const approvedAt = obj["approvedAt"];
|
|
82
|
-
if (typeof approvedAt !== "string" || Number.isNaN(Date.parse(approvedAt))) {
|
|
83
|
-
unreadableCount++;
|
|
84
|
-
continue;
|
|
85
|
-
}
|
|
86
|
-
readable.push({ sessionId: name, approvedAt, approvedBy: obj["approvedBy"] });
|
|
87
|
-
}
|
|
88
63
|
readable.sort((a, b) => Date.parse(b.approvedAt) - Date.parse(a.approvedAt));
|
|
89
64
|
const windowed = readable.slice(0, windowSize);
|
|
90
65
|
const byMode = {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ug-auto-approvals.js","sourceRoot":"","sources":["../../../src/cli/doctor/ug-auto-approvals.ts"],"names":[],"mappings":"AAAA,mFAAmF;AACnF,oEAAoE;AACpE,sEAAsE;AACtE,kCAAkC;AAClC,EAAE;AACF,oEAAoE;AACpE,uEAAuE;AACvE,iEAAiE;AACjE,mEAAmE;AACnE,qEAAqE;AACrE,iEAAiE;AACjE,8DAA8D;AAC9D,EAAE;AACF,sEAAsE;AACtE,uEAAuE;AACvE,qEAAqE;AACrE,8DAA8D;AAE9D,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"ug-auto-approvals.js","sourceRoot":"","sources":["../../../src/cli/doctor/ug-auto-approvals.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/gc/index.d.ts
CHANGED
|
@@ -12,13 +12,19 @@ export interface GcOptions extends LoaderOptions {
|
|
|
12
12
|
/** Test seam: clock anchor. */
|
|
13
13
|
now?: Date;
|
|
14
14
|
}
|
|
15
|
-
export type GcCategory = "report" | "parse-error" | "approval-marker";
|
|
15
|
+
export type GcCategory = "report" | "parse-error" | "approval-marker" | "delegation" | "permission-mode-observation";
|
|
16
16
|
export interface GcCandidate {
|
|
17
17
|
filePath: string;
|
|
18
18
|
category: GcCategory;
|
|
19
19
|
/** Why this file aged out (status + age), for the listing UI. */
|
|
20
20
|
reason: string;
|
|
21
21
|
}
|
|
22
|
+
/** A delegation-sweep file gc could not parse: reported, never a deletion candidate. */
|
|
23
|
+
export interface GcUnparseable {
|
|
24
|
+
filePath: string;
|
|
25
|
+
category: GcCategory;
|
|
26
|
+
reason: string;
|
|
27
|
+
}
|
|
22
28
|
export interface GcResult {
|
|
23
29
|
retentionDays: number;
|
|
24
30
|
cutoffIso: string;
|
|
@@ -32,7 +38,12 @@ export interface GcResult {
|
|
|
32
38
|
*/
|
|
33
39
|
parseErrorsDir: string | null;
|
|
34
40
|
approvalsDir: string;
|
|
41
|
+
delegationsDir: string;
|
|
42
|
+
adoptionLedgerDir: string;
|
|
43
|
+
permissionModeObservationsDir: string;
|
|
35
44
|
candidates: GcCandidate[];
|
|
45
|
+
/** Delegation-sweep files inspected but left in place because they could not be parsed. */
|
|
46
|
+
unparseable: GcUnparseable[];
|
|
36
47
|
/** Files actually deleted (apply mode only). */
|
|
37
48
|
removed: string[];
|
|
38
49
|
/** Per-file deletion failures (apply mode only); never silent. */
|
|
@@ -40,7 +51,7 @@ export interface GcResult {
|
|
|
40
51
|
filePath: string;
|
|
41
52
|
reason: string;
|
|
42
53
|
}>;
|
|
43
|
-
/** Count of artifacts inspected but kept (fresh
|
|
54
|
+
/** Count of artifacts inspected but kept (fresh, non-terminal, or unparseable). */
|
|
44
55
|
keptCount: number;
|
|
45
56
|
applied: boolean;
|
|
46
57
|
}
|
package/dist/cli/gc/index.js
CHANGED
|
@@ -14,12 +14,47 @@
|
|
|
14
14
|
// <reportsDir> terminal-status reports (approved / expired)
|
|
15
15
|
// <reportsDir>/../parse-errors parse-error logs
|
|
16
16
|
// <generatedDir>/.approvals session / task / branch-protection markers
|
|
17
|
+
// <generatedDir>/.delegations signed delegation markers (slice 3)
|
|
18
|
+
// <generatedDir>/.delegation-adoptions once-per-session adoption ledgers
|
|
19
|
+
// <generatedDir>/.permission-mode-observations per-session PreToolUse
|
|
20
|
+
// permission_mode observations (task 8f637efd)
|
|
17
21
|
// The evidence ledger (grounding-mcp) and solution-acceptance
|
|
18
22
|
// verdict dirs (producer-owned) are out of scope by design.
|
|
19
23
|
// - Deletion failures are surfaced loudly per file, never swallowed.
|
|
24
|
+
//
|
|
25
|
+
// DELEGATIONS SWEEP (task 3ece079d, follow-up from UG auto-mode slice 3,
|
|
26
|
+
// agent-tasks 37ad0b05): a delegation marker is refused once expired
|
|
27
|
+
// (`verifyDelegation`'s "expired" reason), but nothing ever removed the
|
|
28
|
+
// FILE, so `.delegations/` grows the same way `.approvals/` used to. The
|
|
29
|
+
// adoption ledger at `.delegation-adoptions/<sid>` is worse: it is
|
|
30
|
+
// appended to once per captured transcript entry and never cleared at
|
|
31
|
+
// all, delegation expiry or not. Both sweep under one `"delegation"`
|
|
32
|
+
// category, on the SAME retention/grace window as everything else in this
|
|
33
|
+
// file (`retentionDays` / `cutoffMs`): a delegation marker is a candidate
|
|
34
|
+
// once its own `expires` segment (read via `parseDelegationApprovedBy`,
|
|
35
|
+
// no signature check needed for a retention decision) is older than
|
|
36
|
+
// `cutoffMs`, and its ledger sibling is a candidate once the marker for
|
|
37
|
+
// that session id is gone or itself a candidate. A marker whose content
|
|
38
|
+
// cannot be parsed is never a deletion candidate (fail closed, same as a
|
|
39
|
+
// pending report): it is surfaced separately in `GcResult.unparseable` so
|
|
40
|
+
// an operator can see it, but `--apply` never touches it, and its ledger
|
|
41
|
+
// sibling (if any) stays too, since "this delegation is dead" cannot be
|
|
42
|
+
// established for one gc cannot read.
|
|
43
|
+
//
|
|
44
|
+
// PERMISSION-MODE OBSERVATIONS SWEEP (task 8f637efd review round 2 F5):
|
|
45
|
+
// `.permission-mode-observations/` (one small per-session file, see
|
|
46
|
+
// permission-mode-observations.ts) grows the same way `.approvals/` and
|
|
47
|
+
// `.delegations/` do (nothing else ever removes an entry), and gc had
|
|
48
|
+
// not been taught about it. Swept under its own `"permission-mode-
|
|
49
|
+
// observation"` category by mtime, same `staleFilesByMtime` helper and
|
|
50
|
+
// `retentionDays` window every mtime-aged category here already uses (no
|
|
51
|
+
// signed `expires` to key off, unlike the delegation sweep: this is a
|
|
52
|
+
// plain observation record, not an approval artifact).
|
|
20
53
|
import * as fs from "node:fs";
|
|
21
54
|
import * as path from "node:path";
|
|
22
|
-
import {
|
|
55
|
+
import { readRegularFileRejectingSymlink } from "../../io/read-regular-file.js";
|
|
56
|
+
import { safeJsonParse } from "../../io/safe-json-parse.js";
|
|
57
|
+
import { ADOPTION_LEDGER_DIRNAME, APPROVAL_MARKER_DIRNAME, DELEGATION_MARKER_DIRNAME, PERMISSION_MODE_OBSERVATION_DIRNAME, defaultReportsDir, listPersistedReports, parseDelegationApprovedBy, } from "../../policy-packs/builtin/understanding-before-execution-runtime.js";
|
|
23
58
|
import { resolveGeneratedDir } from "../../runtime/pending-approval.js";
|
|
24
59
|
import { resolvePaths } from "../loader.js";
|
|
25
60
|
export const DEFAULT_RETENTION_DAYS = 30;
|
|
@@ -66,6 +101,158 @@ function staleFilesByMtime(dir, cutoffMs, nowMs, category) {
|
|
|
66
101
|
}
|
|
67
102
|
return { candidates, kept };
|
|
68
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* A delegation filename is always a session id (mirrors
|
|
106
|
+
* `doctor/ug-delegations.ts`'s own filter and rationale): filtering on
|
|
107
|
+
* this shape before reading keeps filesystem debris that happens to sit
|
|
108
|
+
* next to `.delegations/` or `.delegation-adoptions/` (macOS's
|
|
109
|
+
* `.DS_Store`, a stray dotfile) from ever becoming an "unparseable"
|
|
110
|
+
* candidate gc reports on. Not shared code with that module (see its own
|
|
111
|
+
* header for why); `check:duplication` is the backstop if that stops
|
|
112
|
+
* being a coincidence.
|
|
113
|
+
*/
|
|
114
|
+
const SESSION_ID_BASENAME_RE = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;
|
|
115
|
+
/**
|
|
116
|
+
* Read a `.delegations/<sid>` marker and classify it against `cutoffMs`,
|
|
117
|
+
* WITHOUT verifying its signature: gc is deciding whether to delete a
|
|
118
|
+
* file, not whether to trust it as an approval, and `parseDelegationApprovedBy`
|
|
119
|
+
* is the same reader `harness doctor`'s delegations metric uses read-only.
|
|
120
|
+
* Any failure to read the file, parse it as JSON, or parse its
|
|
121
|
+
* `approvedBy`/`expires` segment is `unparseable`, never `expired`: a
|
|
122
|
+
* marker gc cannot understand is never deleted (mutation probe M2).
|
|
123
|
+
*/
|
|
124
|
+
function readDelegationStatus(filePath, cutoffMs, nowMs) {
|
|
125
|
+
const read = readRegularFileRejectingSymlink(filePath);
|
|
126
|
+
if (read.kind !== "ok") {
|
|
127
|
+
return { kind: "unparseable", reason: `could not read ${filePath} (${read.kind})` };
|
|
128
|
+
}
|
|
129
|
+
const parsed = safeJsonParse(read.content);
|
|
130
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
131
|
+
return { kind: "unparseable", reason: "not a JSON object" };
|
|
132
|
+
}
|
|
133
|
+
const segments = parseDelegationApprovedBy(parsed["approvedBy"]);
|
|
134
|
+
if (!segments.ok) {
|
|
135
|
+
return { kind: "unparseable", reason: segments.reason };
|
|
136
|
+
}
|
|
137
|
+
const expiresMs = Date.parse(segments.value.expiresAt);
|
|
138
|
+
if (!Number.isFinite(expiresMs)) {
|
|
139
|
+
return {
|
|
140
|
+
kind: "unparseable",
|
|
141
|
+
reason: `expires segment is not a valid instant: ${JSON.stringify(segments.value.expiresAt)}`,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
// The expiry comparison itself: mutation probe M1 skips this and treats
|
|
145
|
+
// every parseable marker as expired, which flips a "valid delegation is
|
|
146
|
+
// kept" test red.
|
|
147
|
+
if (expiresMs < cutoffMs) {
|
|
148
|
+
return {
|
|
149
|
+
kind: "expired",
|
|
150
|
+
reason: `expired ${ageDays(nowMs, expiresMs)}d ago (past the ${ageDays(nowMs, cutoffMs)}d grace)`,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
return { kind: "valid" };
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Sweep `.delegations/` (expired markers) and `.delegation-adoptions/`
|
|
157
|
+
* (ledgers orphaned once their marker is gone or expired). One shared
|
|
158
|
+
* category (`"delegation"`), mirroring the approval-marker sweep's
|
|
159
|
+
* dry-run/candidate/kept shape but keyed off the marker's OWN `expires`
|
|
160
|
+
* segment instead of file mtime, since a delegation's age on disk and its
|
|
161
|
+
* validity window are two different things.
|
|
162
|
+
*/
|
|
163
|
+
function sweepDelegations(generatedDir, cutoffMs, nowMs) {
|
|
164
|
+
const delegationsDir = path.join(generatedDir, DELEGATION_MARKER_DIRNAME);
|
|
165
|
+
const ledgerDir = path.join(generatedDir, ADOPTION_LEDGER_DIRNAME);
|
|
166
|
+
const candidates = [];
|
|
167
|
+
const unparseable = [];
|
|
168
|
+
let kept = 0;
|
|
169
|
+
// Session id -> status, for every delegation marker actually present
|
|
170
|
+
// and session-id-shaped, so the ledger pass below can tell "expired"
|
|
171
|
+
// from "absent" from "still valid" without re-reading the file.
|
|
172
|
+
const statusBySessionId = new Map();
|
|
173
|
+
let delegationNames;
|
|
174
|
+
try {
|
|
175
|
+
delegationNames = fs.readdirSync(delegationsDir);
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
delegationNames = [];
|
|
179
|
+
}
|
|
180
|
+
for (const name of delegationNames) {
|
|
181
|
+
if (!SESSION_ID_BASENAME_RE.test(name))
|
|
182
|
+
continue;
|
|
183
|
+
const full = path.join(delegationsDir, name);
|
|
184
|
+
const status = readDelegationStatus(full, cutoffMs, nowMs);
|
|
185
|
+
statusBySessionId.set(name, status);
|
|
186
|
+
if (status.kind === "expired") {
|
|
187
|
+
candidates.push({ filePath: full, category: "delegation", reason: status.reason });
|
|
188
|
+
}
|
|
189
|
+
else if (status.kind === "unparseable") {
|
|
190
|
+
unparseable.push({ filePath: full, category: "delegation", reason: status.reason });
|
|
191
|
+
kept += 1;
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
kept += 1;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
let ledgerNames;
|
|
198
|
+
try {
|
|
199
|
+
ledgerNames = fs.readdirSync(ledgerDir);
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
ledgerNames = [];
|
|
203
|
+
}
|
|
204
|
+
for (const name of ledgerNames) {
|
|
205
|
+
if (!SESSION_ID_BASENAME_RE.test(name))
|
|
206
|
+
continue;
|
|
207
|
+
const full = path.join(ledgerDir, name);
|
|
208
|
+
let stat;
|
|
209
|
+
try {
|
|
210
|
+
stat = fs.statSync(full);
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
if (!stat.isFile()) {
|
|
216
|
+
kept += 1;
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
const status = statusBySessionId.get(name);
|
|
220
|
+
if (status === undefined) {
|
|
221
|
+
// No marker for this session id at all: only a candidate once the
|
|
222
|
+
// LEDGER's own age clears the cutoff. Without this check a ledger
|
|
223
|
+
// written seconds ago (its marker legitimately not written yet, or
|
|
224
|
+
// already cleaned up by something else) would be swept regardless
|
|
225
|
+
// of age, contradicting the "older than the retention window"
|
|
226
|
+
// posture documented on the command and the listing header; it
|
|
227
|
+
// also closes the TOCTOU window between the two readdir calls
|
|
228
|
+
// above, since a ledger written between them is newer than cutoff.
|
|
229
|
+
if (stat.mtimeMs < cutoffMs) {
|
|
230
|
+
candidates.push({
|
|
231
|
+
filePath: full,
|
|
232
|
+
category: "delegation",
|
|
233
|
+
reason: "orphaned adoption ledger (no delegation marker for this session)",
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
kept += 1;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
else if (status.kind === "expired") {
|
|
241
|
+
candidates.push({
|
|
242
|
+
filePath: full,
|
|
243
|
+
category: "delegation",
|
|
244
|
+
reason: `orphaned adoption ledger (delegation ${status.reason})`,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
// Marker still valid, or unparseable (gc cannot prove it is dead):
|
|
249
|
+
// keep the ledger either way, same fail-closed posture as the
|
|
250
|
+
// marker itself.
|
|
251
|
+
kept += 1;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return { candidates, unparseable, kept };
|
|
255
|
+
}
|
|
69
256
|
export function gc(opts = {}) {
|
|
70
257
|
const retentionDays = opts.retentionDays ?? DEFAULT_RETENTION_DAYS;
|
|
71
258
|
if (!Number.isFinite(retentionDays) || retentionDays < 1) {
|
|
@@ -92,7 +279,11 @@ export function gc(opts = {}) {
|
|
|
92
279
|
manifestPath: manifestBase(),
|
|
93
280
|
});
|
|
94
281
|
const approvalsDir = path.join(generatedDir, APPROVAL_MARKER_DIRNAME);
|
|
282
|
+
const delegationsDir = path.join(generatedDir, DELEGATION_MARKER_DIRNAME);
|
|
283
|
+
const adoptionLedgerDir = path.join(generatedDir, ADOPTION_LEDGER_DIRNAME);
|
|
284
|
+
const permissionModeObservationsDir = path.join(generatedDir, PERMISSION_MODE_OBSERVATION_DIRNAME);
|
|
95
285
|
const candidates = [];
|
|
286
|
+
const unparseable = [];
|
|
96
287
|
let keptCount = 0;
|
|
97
288
|
// Reports: only terminal statuses age out. A pending report is never
|
|
98
289
|
// deleted regardless of age; since the C1 fix, stale pending leftovers
|
|
@@ -119,6 +310,13 @@ export function gc(opts = {}) {
|
|
|
119
310
|
const markers = staleFilesByMtime(approvalsDir, cutoffMs, nowMs, "approval-marker");
|
|
120
311
|
candidates.push(...markers.candidates);
|
|
121
312
|
keptCount += markers.kept;
|
|
313
|
+
const delegations = sweepDelegations(generatedDir, cutoffMs, nowMs);
|
|
314
|
+
candidates.push(...delegations.candidates);
|
|
315
|
+
unparseable.push(...delegations.unparseable);
|
|
316
|
+
keptCount += delegations.kept;
|
|
317
|
+
const permissionModeObservations = staleFilesByMtime(permissionModeObservationsDir, cutoffMs, nowMs, "permission-mode-observation");
|
|
318
|
+
candidates.push(...permissionModeObservations.candidates);
|
|
319
|
+
keptCount += permissionModeObservations.kept;
|
|
122
320
|
const removed = [];
|
|
123
321
|
const failures = [];
|
|
124
322
|
if (opts.apply === true) {
|
|
@@ -138,7 +336,11 @@ export function gc(opts = {}) {
|
|
|
138
336
|
reportsDir,
|
|
139
337
|
parseErrorsDir,
|
|
140
338
|
approvalsDir,
|
|
339
|
+
delegationsDir,
|
|
340
|
+
adoptionLedgerDir,
|
|
341
|
+
permissionModeObservationsDir,
|
|
141
342
|
candidates,
|
|
343
|
+
unparseable,
|
|
142
344
|
removed,
|
|
143
345
|
failures,
|
|
144
346
|
keptCount,
|
package/dist/cli/gc/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/cli/gc/index.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,EAAE;AACF,iEAAiE;AACjE,sEAAsE;AACtE,oEAAoE;AACpE,kEAAkE;AAClE,sEAAsE;AACtE,gEAAgE;AAChE,0CAA0C;AAC1C,EAAE;AACF,yDAAyD;AACzD,6CAA6C;AAC7C,+DAA+D;AAC/D,6EAA6E;AAC7E,wDAAwD;AACxD,kFAAkF;AAClF,kEAAkE;AAClE,gEAAgE;AAChE,uEAAuE;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/cli/gc/index.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/index.js
CHANGED
|
@@ -27,7 +27,7 @@ import { add } from "./add/index.js";
|
|
|
27
27
|
import { adopt } from "./adopt/index.js";
|
|
28
28
|
import { apply, CODEX_CONFIG_BASENAME, DRIFT_HINT_MESSAGE, formatNextSteps, OPENCODE_CONFIG_BASENAME, SETTINGS_BASENAME, } from "./apply/index.js";
|
|
29
29
|
import { isRemoveType, KNOWN_REMOVE_TYPES, remove } from "./remove/index.js";
|
|
30
|
-
import { packAdd, packList, packRemove, packReseed } from "./pack/index.js";
|
|
30
|
+
import { packAdd, packList, packRemove, packReseed, packUpgrade } from "./pack/index.js";
|
|
31
31
|
import { runPackHookPreToolUseCli } from "./pack/hook-pre-tool-use.js";
|
|
32
32
|
import { runPackHookPostToolUseCli } from "./pack/hook-post-tool-use.js";
|
|
33
33
|
import { runPackHookTrackActiveClaimCli } from "./pack/hook-track-active-claim.js";
|
|
@@ -862,6 +862,35 @@ export function buildProgram(opts = {}) {
|
|
|
862
862
|
}
|
|
863
863
|
stdout(`reseeded ${result.fieldsChanged.map((f) => `config.${f}`).join(", ")} for policy_packs entry ${JSON.stringify(result.name)} in ${result.path}\n`);
|
|
864
864
|
});
|
|
865
|
+
// `harness pack upgrade <name>` (task 8f637efd, D-004): text-level
|
|
866
|
+
// insertion of a pack's missing default config block into an existing
|
|
867
|
+
// manifest, idempotent, never rewrites content outside the inserted
|
|
868
|
+
// block. Today's only wired upgrade is `understanding-before-
|
|
869
|
+
// execution`'s `auto_approve` default; see upgrade.ts's module header
|
|
870
|
+
// for why this is text-level rather than a Document-API mutation like
|
|
871
|
+
// `add` / `reseed`.
|
|
872
|
+
packCmd
|
|
873
|
+
.command("upgrade <name>")
|
|
874
|
+
.description("Insert a pack's missing default config block into an existing manifest (today: " +
|
|
875
|
+
"understanding-before-execution's auto_approve default). Idempotent; " +
|
|
876
|
+
"refuses on ambiguity rather than guessing where to insert.")
|
|
877
|
+
.option("--config <path>", "manifest path (default: ~/.harness/harness.yaml; legacy fallback ~/.claude/harness.yaml)")
|
|
878
|
+
.option("--dry-run", "print the unified diff and exit without writing")
|
|
879
|
+
.action(async (name, options) => {
|
|
880
|
+
const result = await packUpgrade(name, {
|
|
881
|
+
configPath: options.config,
|
|
882
|
+
dryRun: options.dryRun,
|
|
883
|
+
});
|
|
884
|
+
if (result.alreadyPresent) {
|
|
885
|
+
stdout(`policy_packs entry ${JSON.stringify(result.name)} already carries this upgrade; nothing to insert (${result.path} unchanged).\n`);
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
888
|
+
if (options.dryRun) {
|
|
889
|
+
stdout(result.diff);
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
stdout(`upgraded policy_packs entry ${JSON.stringify(result.name)} in ${result.path}\n`);
|
|
893
|
+
});
|
|
865
894
|
// `harness pack hook` runtime sub-tree (Phase 6 #4): wired by the
|
|
866
895
|
// pack's PreToolUse hook contribution; reads PreToolUse JSON from
|
|
867
896
|
// stdin, consults the signed approval marker (the persisted report and
|
|
@@ -2256,11 +2285,13 @@ export function buildProgram(opts = {}) {
|
|
|
2256
2285
|
program
|
|
2257
2286
|
.command("gc")
|
|
2258
2287
|
.description("Retention-based cleanup of harness-owned gate state: terminal " +
|
|
2259
|
-
"(approved/expired) understanding-gate reports, parse-error logs,
|
|
2260
|
-
"approval markers
|
|
2261
|
-
"
|
|
2262
|
-
"
|
|
2263
|
-
"
|
|
2288
|
+
"(approved/expired) understanding-gate reports, parse-error logs, " +
|
|
2289
|
+
"approval markers, expired delegation markers, orphaned delegation " +
|
|
2290
|
+
"adoption ledgers, and stale permission-mode observations older than " +
|
|
2291
|
+
"the retention window. Pending reports and anything outside the " +
|
|
2292
|
+
"enumerated harness-owned dirs are never touched (the evidence " +
|
|
2293
|
+
"ledger and solution-acceptance verdict dirs are owned by their " +
|
|
2294
|
+
"producers). Dry-run by default; pass --apply to delete.")
|
|
2264
2295
|
.option("--config <path>", "manifest path (default: ~/.harness/harness.yaml; legacy fallback ~/.claude/harness.yaml)")
|
|
2265
2296
|
.option("--retention-days <n>", `delete artifacts older than this many days (default: ${DEFAULT_RETENTION_DAYS})`)
|
|
2266
2297
|
.option("--apply", "delete the listed artifacts (default: dry-run listing only)")
|
|
@@ -2283,10 +2314,17 @@ export function buildProgram(opts = {}) {
|
|
|
2283
2314
|
result.reportsDir,
|
|
2284
2315
|
...(result.parseErrorsDir !== null ? [result.parseErrorsDir] : []),
|
|
2285
2316
|
result.approvalsDir,
|
|
2317
|
+
result.delegationsDir,
|
|
2318
|
+
result.adoptionLedgerDir,
|
|
2319
|
+
result.permissionModeObservationsDir,
|
|
2286
2320
|
];
|
|
2287
2321
|
if (result.parseErrorsDir === null) {
|
|
2288
2322
|
stderr("gc: skipping the parse-errors sweep (reports dir does not have the conventional .understanding-gate/reports shape)\n");
|
|
2289
2323
|
}
|
|
2324
|
+
if (result.unparseable.length > 0) {
|
|
2325
|
+
stderr(`gc: ${result.unparseable.length} delegation file(s) could not be parsed and were left in place:\n` +
|
|
2326
|
+
result.unparseable.map((u) => ` [${u.category}] ${u.filePath} (${u.reason})\n`).join(""));
|
|
2327
|
+
}
|
|
2290
2328
|
if (result.candidates.length === 0) {
|
|
2291
2329
|
stdout(`gc: nothing older than ${result.retentionDays}d (cutoff ${result.cutoffIso}) under\n` +
|
|
2292
2330
|
sweptDirs.map((d) => ` ${d}\n`).join("") +
|