@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.
Files changed (51) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/dist/cli/doctor/bypass-without-auto-approve.d.ts +38 -0
  3. package/dist/cli/doctor/bypass-without-auto-approve.js +89 -0
  4. package/dist/cli/doctor/bypass-without-auto-approve.js.map +1 -0
  5. package/dist/cli/doctor/format.js +7 -0
  6. package/dist/cli/doctor/format.js.map +1 -1
  7. package/dist/cli/doctor/index.js +17 -0
  8. package/dist/cli/doctor/index.js.map +1 -1
  9. package/dist/cli/doctor/types.d.ts +11 -0
  10. package/dist/cli/doctor/types.js.map +1 -1
  11. package/dist/cli/doctor/ug-auto-approvals.js +18 -43
  12. package/dist/cli/doctor/ug-auto-approvals.js.map +1 -1
  13. package/dist/cli/gc/index.d.ts +13 -2
  14. package/dist/cli/gc/index.js +203 -1
  15. package/dist/cli/gc/index.js.map +1 -1
  16. package/dist/cli/index.js +44 -6
  17. package/dist/cli/index.js.map +1 -1
  18. package/dist/cli/init/composer.js +9 -0
  19. package/dist/cli/init/composer.js.map +1 -1
  20. package/dist/cli/init/profiles.d.ts +2 -2
  21. package/dist/cli/init/profiles.js +11 -0
  22. package/dist/cli/init/profiles.js.map +1 -1
  23. package/dist/cli/init/templates.d.ts +1 -1
  24. package/dist/cli/init/templates.js +11 -0
  25. package/dist/cli/init/templates.js.map +1 -1
  26. package/dist/cli/pack/hook-pre-tool-use.js +20 -21
  27. package/dist/cli/pack/hook-pre-tool-use.js.map +1 -1
  28. package/dist/cli/pack/index.d.ts +1 -0
  29. package/dist/cli/pack/index.js +1 -0
  30. package/dist/cli/pack/index.js.map +1 -1
  31. package/dist/cli/pack/upgrade.d.ts +32 -0
  32. package/dist/cli/pack/upgrade.js +273 -0
  33. package/dist/cli/pack/upgrade.js.map +1 -0
  34. package/dist/io/read-json-dir-entries.d.ts +36 -0
  35. package/dist/io/read-json-dir-entries.js +38 -0
  36. package/dist/io/read-json-dir-entries.js.map +1 -0
  37. package/dist/policy-packs/builtin/understanding-before-execution/auto-approve-default.d.ts +35 -0
  38. package/dist/policy-packs/builtin/understanding-before-execution/auto-approve-default.js +86 -0
  39. package/dist/policy-packs/builtin/understanding-before-execution/auto-approve-default.js.map +1 -0
  40. package/dist/policy-packs/builtin/understanding-before-execution/delegation-markers.d.ts +14 -0
  41. package/dist/policy-packs/builtin/understanding-before-execution/delegation-markers.js +14 -0
  42. package/dist/policy-packs/builtin/understanding-before-execution/delegation-markers.js.map +1 -1
  43. package/dist/policy-packs/builtin/understanding-before-execution/index.d.ts +3 -1
  44. package/dist/policy-packs/builtin/understanding-before-execution/index.js +18 -1
  45. package/dist/policy-packs/builtin/understanding-before-execution/index.js.map +1 -1
  46. package/dist/policy-packs/builtin/understanding-before-execution/permission-mode-observations.d.ts +71 -0
  47. package/dist/policy-packs/builtin/understanding-before-execution/permission-mode-observations.js +169 -0
  48. package/dist/policy-packs/builtin/understanding-before-execution/permission-mode-observations.js.map +1 -0
  49. package/dist/runtime/git-context.js +7 -1
  50. package/dist/runtime/git-context.js.map +1 -1
  51. 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 { readRegularFileRejectingSymlink } from "../../io/read-regular-file.js";
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
- let dirents;
48
- try {
49
- dirents = fs.readdirSync(dir, { withFileTypes: true });
50
- }
51
- catch {
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,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,+BAA+B,EAAE,MAAM,+BAA+B,CAAC;AAChF,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,+BAA+B,EAAE,MAAM,yDAAyD,CAAC;AAC1G,OAAO,EACL,uBAAuB,EACvB,2BAA2B,EAC3B,mBAAmB,GACpB,MAAM,oEAAoE,CAAC;AAE5E,oEAAoE;AACpE,MAAM,CAAC,MAAM,uBAAuB,GAAG,EAAE,CAAC;AAgC1C,SAAS,YAAY,CAAC,UAAkB;IACtC,OAAO;QACL,mBAAmB,EAAE,KAAK;QAC1B,UAAU;QACV,iBAAiB,EAAE,CAAC;QACpB,MAAM,EAAE,EAAE;QACV,SAAS,EAAE,EAAE;QACb,OAAO,EAAE,EAAE;QACX,eAAe,EAAE,CAAC;KACnB,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAClC,YAAoB,EACpB,IAAgC;IAEhC,MAAM,UAAU,GAAG,IAAI,CAAC,cAAc,CAAC;IACvC,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,uBAAuB,CAAC,CAAC;IAE7D,IAAI,OAAoB,CAAC;IACzB,IAAI,CAAC;QACH,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IACzD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,YAAY,CAAC,UAAU,CAAC,CAAC;IAClC,CAAC;IAED,MAAM,QAAQ,GAAqE,EAAE,CAAC;IACtF,IAAI,eAAe,GAAG,CAAC,CAAC;IAExB,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC;QACpB,IAAI,IAAI,CAAC,UAAU,CAAC,2BAA2B,CAAC;YAAE,SAAS;QAC3D,IAAI,IAAI,CAAC,UAAU,CAAC,+BAA+B,CAAC;YAAE,SAAS;QAE/D,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAClC,MAAM,IAAI,GAAG,+BAA+B,CAAC,IAAI,CAAC,CAAC;QACnD,qEAAqE;QACrE,gEAAgE;QAChE,oEAAoE;QACpE,uCAAuC;QACvC,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,IAAI,KAAK,aAAa,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YACtF,SAAS;QACX,CAAC;QACD,IAAI,IAAI,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;YAC/B,eAAe,EAAE,CAAC;YAClB,SAAS;QACX,CAAC;QACD,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC3C,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3E,eAAe,EAAE,CAAC;YAClB,SAAS;QACX,CAAC;QACD,MAAM,GAAG,GAAG,MAAiC,CAAC;QAC9C,MAAM,UAAU,GAAG,GAAG,CAAC,YAAY,CAAC,CAAC;QACrC,IAAI,OAAO,UAAU,KAAK,QAAQ,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC;YAC3E,eAAe,EAAE,CAAC;YAClB,SAAS;QACX,CAAC;QACD,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,EAAE,UAAU,EAAE,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;IAChF,CAAC;IAED,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;IAC7E,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;IAE/C,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,MAAM,SAAS,GAA2B,EAAE,CAAC;IAC7C,MAAM,OAAO,GAA+B,EAAE,CAAC;IAC/C,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,MAAM,UAAU,GAAG,mBAAmB,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;QACrD,IAAI,UAAU,KAAK,IAAI;YAAE,SAAS;QAClC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QAC7D,SAAS,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QACzE,OAAO,CAAC,IAAI,CAAC;YACX,SAAS,EAAE,CAAC,CAAC,SAAS;YACtB,IAAI,EAAE,UAAU,CAAC,IAAI;YACrB,OAAO,EAAE,UAAU,CAAC,OAAO;YAC3B,UAAU,EAAE,CAAC,CAAC,UAAU;SACzB,CAAC,CAAC;IACL,CAAC;IAED,OAAO;QACL,mBAAmB,EAAE,IAAI;QACzB,UAAU;QACV,iBAAiB,EAAE,OAAO,CAAC,MAAM;QACjC,MAAM;QACN,SAAS;QACT,OAAO;QACP,eAAe;KAChB,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"ug-auto-approvals.js","sourceRoot":"","sources":["../../../src/cli/doctor/ug-auto-approvals.ts"],"names":[],"mappings":"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"}
@@ -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 or non-terminal). */
54
+ /** Count of artifacts inspected but kept (fresh, non-terminal, or unparseable). */
44
55
  keptCount: number;
45
56
  applied: boolean;
46
57
  }
@@ -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 { APPROVAL_MARKER_DIRNAME, defaultReportsDir, listPersistedReports, } from "../../policy-packs/builtin/understanding-before-execution-runtime.js";
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,
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/cli/gc/index.ts"],"names":[],"mappings":"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"}
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, and " +
2260
- "approval markers older than the retention window. Pending reports and " +
2261
- "anything outside the enumerated harness-owned dirs are never touched " +
2262
- "(the evidence ledger and solution-acceptance verdict dirs are owned by " +
2263
- "their producers). Dry-run by default; pass --apply to delete.")
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("") +