@lannguyensi/harness 0.50.0 → 0.52.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 +247 -0
- package/dist/cli/approve/understanding.d.ts +97 -0
- package/dist/cli/approve/understanding.js +67 -24
- package/dist/cli/approve/understanding.js.map +1 -1
- package/dist/cli/audit.d.ts +29 -0
- package/dist/cli/audit.js +130 -17
- package/dist/cli/audit.js.map +1 -1
- package/dist/cli/delegate/index.d.ts +63 -0
- package/dist/cli/delegate/index.js +313 -0
- package/dist/cli/delegate/index.js.map +1 -0
- package/dist/cli/doctor/auto-approve-mode.d.ts +37 -0
- package/dist/cli/doctor/auto-approve-mode.js +80 -0
- package/dist/cli/doctor/auto-approve-mode.js.map +1 -0
- package/dist/cli/doctor/claude-mcp.d.ts +14 -0
- package/dist/cli/doctor/claude-mcp.js +7 -1
- package/dist/cli/doctor/claude-mcp.js.map +1 -1
- package/dist/cli/doctor/codex-config-drift.d.ts +28 -0
- package/dist/cli/doctor/codex-config-drift.js +240 -0
- package/dist/cli/doctor/codex-config-drift.js.map +1 -0
- package/dist/cli/doctor/format.js +67 -1
- package/dist/cli/doctor/format.js.map +1 -1
- package/dist/cli/doctor/index.d.ts +20 -0
- package/dist/cli/doctor/index.js +84 -1
- package/dist/cli/doctor/index.js.map +1 -1
- package/dist/cli/doctor/settings-drift.d.ts +25 -0
- package/dist/cli/doctor/settings-drift.js +203 -0
- package/dist/cli/doctor/settings-drift.js.map +1 -0
- package/dist/cli/doctor/types.d.ts +69 -0
- package/dist/cli/doctor/types.js.map +1 -1
- package/dist/cli/doctor/ug-auto-approvals.d.ts +40 -0
- package/dist/cli/doctor/ug-auto-approvals.js +116 -0
- package/dist/cli/doctor/ug-auto-approvals.js.map +1 -0
- package/dist/cli/doctor/ug-delegations.d.ts +26 -0
- package/dist/cli/doctor/ug-delegations.js +112 -0
- package/dist/cli/doctor/ug-delegations.js.map +1 -0
- package/dist/cli/doctor/understanding-mode-env.d.ts +12 -0
- package/dist/cli/doctor/understanding-mode-env.js +16 -1
- package/dist/cli/doctor/understanding-mode-env.js.map +1 -1
- package/dist/cli/index.js +100 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/pack/approve-escape.js +16 -7
- package/dist/cli/pack/approve-escape.js.map +1 -1
- package/dist/cli/pack/auto-approve-path.d.ts +135 -0
- package/dist/cli/pack/auto-approve-path.js +400 -0
- package/dist/cli/pack/auto-approve-path.js.map +1 -0
- package/dist/cli/pack/hook-codex-pre-tool-use.d.ts +9 -0
- package/dist/cli/pack/hook-codex-pre-tool-use.js +118 -4
- package/dist/cli/pack/hook-codex-pre-tool-use.js.map +1 -1
- package/dist/cli/pack/hook-pre-tool-use.d.ts +36 -0
- package/dist/cli/pack/hook-pre-tool-use.js +402 -3
- package/dist/cli/pack/hook-pre-tool-use.js.map +1 -1
- package/dist/cli/pack/transcript-report-scan.d.ts +71 -0
- package/dist/cli/pack/transcript-report-scan.js +359 -0
- package/dist/cli/pack/transcript-report-scan.js.map +1 -0
- package/dist/cli/smoke/index.d.ts +18 -1
- package/dist/cli/smoke/index.js +54 -0
- package/dist/cli/smoke/index.js.map +1 -1
- package/dist/cli/validate/checks.js +81 -0
- package/dist/cli/validate/checks.js.map +1 -1
- package/dist/policy-packs/builtin/understanding-before-execution/auto-approve.d.ts +210 -0
- package/dist/policy-packs/builtin/understanding-before-execution/auto-approve.js +361 -0
- package/dist/policy-packs/builtin/understanding-before-execution/auto-approve.js.map +1 -0
- package/dist/policy-packs/builtin/understanding-before-execution/delegation-markers.d.ts +223 -0
- package/dist/policy-packs/builtin/understanding-before-execution/delegation-markers.js +633 -0
- package/dist/policy-packs/builtin/understanding-before-execution/delegation-markers.js.map +1 -0
- package/dist/policy-packs/builtin/understanding-before-execution/index.d.ts +3 -1
- package/dist/policy-packs/builtin/understanding-before-execution/index.js +10 -1
- package/dist/policy-packs/builtin/understanding-before-execution/index.js.map +1 -1
- package/dist/policy-packs/builtin/understanding-before-execution/measured-permission-modes.d.ts +29 -0
- package/dist/policy-packs/builtin/understanding-before-execution/measured-permission-modes.js +82 -0
- package/dist/policy-packs/builtin/understanding-before-execution/measured-permission-modes.js.map +1 -0
- package/dist/policy-packs/builtin/understanding-before-execution/persisted-reports.d.ts +23 -0
- package/dist/policy-packs/builtin/understanding-before-execution/persisted-reports.js +29 -0
- package/dist/policy-packs/builtin/understanding-before-execution/persisted-reports.js.map +1 -1
- package/dist/policy-packs/builtin/understanding-before-execution.d.ts +42 -0
- package/dist/policy-packs/builtin/understanding-before-execution.js +56 -1
- package/dist/policy-packs/builtin/understanding-before-execution.js.map +1 -1
- package/dist/runtime/approval-signing.d.ts +22 -0
- package/dist/runtime/approval-signing.js +83 -1
- package/dist/runtime/approval-signing.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings-drift.js","sourceRoot":"","sources":["../../../src/cli/doctor/settings-drift.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,6 +9,11 @@ import type { NpmBinReport } from "./npm-bin-path.js";
|
|
|
9
9
|
import type { RogueLedgerDb } from "./rogue-ledger.js";
|
|
10
10
|
import type { UnderstandingModeEnvDivergence } from "./understanding-mode-env.js";
|
|
11
11
|
import type { ToolchainParitySection } from "./toolchain-parity.js";
|
|
12
|
+
import type { UgAutoApprovalsSection } from "./ug-auto-approvals.js";
|
|
13
|
+
import type { UgDelegationsSection } from "./ug-delegations.js";
|
|
14
|
+
import type { SettingsDriftSection } from "./settings-drift.js";
|
|
15
|
+
import type { AutoApproveModeWarning } from "./auto-approve-mode.js";
|
|
16
|
+
import type { CodexConfigDriftSection } from "./codex-config-drift.js";
|
|
12
17
|
/**
|
|
13
18
|
* Phase 6 #6 follow-up — doctor target identifier. Distinct from
|
|
14
19
|
* `Runtime` (which gates `harness apply --runtime`): doctor only adds
|
|
@@ -379,6 +384,65 @@ export interface DoctorReport {
|
|
|
379
384
|
* `understanding-mode-env.ts` for the full rationale.
|
|
380
385
|
*/
|
|
381
386
|
understandingModeEnv?: UnderstandingModeEnvDivergence;
|
|
387
|
+
/**
|
|
388
|
+
* Auto-approval listing + last-N metric (ADR
|
|
389
|
+
* docs/decisions/2026-08-27-ug-auto-mode-approval.md slice 1,
|
|
390
|
+
* agent-tasks 74b4b17d, "Audit and doctor"). Present only when the
|
|
391
|
+
* `understanding-before-execution` pack is declared and enabled
|
|
392
|
+
* (mirrors `understandingModeEnv`'s gate) — a manifest that never
|
|
393
|
+
* uses the pack has no `.approvals/` markers this listing owns an
|
|
394
|
+
* opinion about. Purely informational (`ℹ`); never rolls into
|
|
395
|
+
* `warningCount`.
|
|
396
|
+
*/
|
|
397
|
+
ugAutoApprovals?: UgAutoApprovalsSection;
|
|
398
|
+
/**
|
|
399
|
+
* Delegations-on-disk metric (same ADR, slice 3, "Audit and doctor",
|
|
400
|
+
* agent-tasks 37ad0b05): `total`/`expired`/`unreadable` counts
|
|
401
|
+
* from `harness.generated/.delegations/`, distinct from
|
|
402
|
+
* `ugAutoApprovals` (which reads `.approvals/` only). Present only
|
|
403
|
+
* when the `understanding-before-execution` pack is declared and
|
|
404
|
+
* enabled (mirrors `ugAutoApprovals`'s gate). `delegationsDirPresent`
|
|
405
|
+
* mirrors `ugAutoApprovals`'s `approvalsDirPresent`: the rendered line
|
|
406
|
+
* stays silent when `.delegations/` does not exist at all, and shows
|
|
407
|
+
* the zero-count line when it exists but is empty (both cases resolve
|
|
408
|
+
* `total`/`expired`/`unreadable` to `0`). Informational (`ℹ`) unless
|
|
409
|
+
* `unreadable > 0`, in which case it rolls one warning into
|
|
410
|
+
* `warningCount`.
|
|
411
|
+
*/
|
|
412
|
+
ugDelegations?: UgDelegationsSection;
|
|
413
|
+
/**
|
|
414
|
+
* `auto_approve` configured without `mode: grill_me` (agent-tasks
|
|
415
|
+
* abfad738, follow-up of ADR
|
|
416
|
+
* docs/decisions/2026-08-27-ug-auto-mode-approval.md slice 1).
|
|
417
|
+
* Present only when the pack is declared and enabled AND
|
|
418
|
+
* `config.auto_approve` parses as a valid opt-in block. Always
|
|
419
|
+
* advisory (rolls into `warningCount`, never `errorCount`). See
|
|
420
|
+
* `auto-approve-mode.ts` for the full rationale.
|
|
421
|
+
*/
|
|
422
|
+
ugAutoApproveMode?: AutoApproveModeWarning;
|
|
423
|
+
/**
|
|
424
|
+
* Settings-drift compensating control (same ADR, threat model (c)): a
|
|
425
|
+
* `permissions.defaultMode` or hook entry present in a live Claude
|
|
426
|
+
* Code settings file but absent from harness's own last-apply
|
|
427
|
+
* snapshot. Present only when the understanding-gate pack is enabled
|
|
428
|
+
* AND `harness apply` has run at least once for this manifest
|
|
429
|
+
* (`harness.generated/` exists) — otherwise there is no baseline to
|
|
430
|
+
* compare against and nothing to say. `warnings` roll into
|
|
431
|
+
* `warningCount`; `notes` never do.
|
|
432
|
+
*/
|
|
433
|
+
settingsDrift?: SettingsDriftSection;
|
|
434
|
+
/**
|
|
435
|
+
* Codex counterpart of `settingsDrift` (follow-up of slice 2 of the
|
|
436
|
+
* same ADR, agent-tasks f59ea0eb): a live `approval_policy = "never"`
|
|
437
|
+
* or full-access `default_permissions` selection in `$CODEX_HOME
|
|
438
|
+
* /config.toml` or `<repo>/.codex/config.toml`. Present only when the
|
|
439
|
+
* understanding-gate pack's `auto_approve.harnesses` lists `codex`;
|
|
440
|
+
* a repo that never opted Codex into the auto path has no live signal
|
|
441
|
+
* for this key to pre-set. Unlike `settingsDrift`, this always warns
|
|
442
|
+
* while the key is present and the repo is opted in (not gated on
|
|
443
|
+
* drift since the last apply); `warnings` roll into `warningCount`.
|
|
444
|
+
*/
|
|
445
|
+
codexConfigDrift?: CodexConfigDriftSection;
|
|
382
446
|
memory: MemoryReport;
|
|
383
447
|
hooks: HookEntryReport[];
|
|
384
448
|
policies: PolicyEntryReport[];
|
|
@@ -478,5 +542,10 @@ export interface DoctorReport {
|
|
|
478
542
|
export type { NpmBinReport } from "./npm-bin-path.js";
|
|
479
543
|
export type { ClaudeMcpRegistrationSection, ClaudeMcpEntryReport } from "./claude-mcp.js";
|
|
480
544
|
export type { UnderstandingModeEnvDivergence } from "./understanding-mode-env.js";
|
|
545
|
+
export type { UgAutoApprovalsSection, AutoApprovalListingEntry } from "./ug-auto-approvals.js";
|
|
546
|
+
export type { UgDelegationsSection } from "./ug-delegations.js";
|
|
547
|
+
export type { AutoApproveModeWarning } from "./auto-approve-mode.js";
|
|
548
|
+
export type { SettingsDriftSection } from "./settings-drift.js";
|
|
549
|
+
export type { CodexConfigDriftSection } from "./codex-config-drift.js";
|
|
481
550
|
export type { ToolchainParitySection, ToolchainParityPeerReport } from "./toolchain-parity.js";
|
|
482
551
|
export type { Manifest, McpProbeResult, MemoryReport, StaleMemory, CodexTargetReport, RogueLedgerDb, };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/cli/doctor/types.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/cli/doctor/types.ts"],"names":[],"mappings":"AAiBA;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,OAAO,EAAE,UAAU,CAAU,CAAC;AAGnE,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAK,oBAA0C,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAClG,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/** Default doctor window when `--recent-sessions` is not passed. */
|
|
2
|
+
export declare const DEFAULT_RECENT_SESSIONS = 20;
|
|
3
|
+
export interface AutoApprovalListingEntry {
|
|
4
|
+
sessionId: string;
|
|
5
|
+
mode: string;
|
|
6
|
+
harness: string;
|
|
7
|
+
approvedAt: string;
|
|
8
|
+
}
|
|
9
|
+
export interface UgAutoApprovalsSection {
|
|
10
|
+
/** Whether `<generatedDir>/.approvals/` exists at all. */
|
|
11
|
+
approvalsDirPresent: boolean;
|
|
12
|
+
/** The `recentSessions` window that was applied. */
|
|
13
|
+
windowSize: number;
|
|
14
|
+
/** Auto approvals among the newest `windowSize` session markers. */
|
|
15
|
+
autoApprovedCount: number;
|
|
16
|
+
/** Per-mode breakdown of `autoApprovedCount` (e.g. `{bypassPermissions: 3}`). */
|
|
17
|
+
byMode: Record<string, number>;
|
|
18
|
+
/** Per-harness breakdown of `autoApprovedCount` (e.g. `{"claude-code": 3}`). */
|
|
19
|
+
byHarness: Record<string, number>;
|
|
20
|
+
/** `(sessionId, mode, approvedAt)` for each auto approval in the window, newest first. */
|
|
21
|
+
entries: AutoApprovalListingEntry[];
|
|
22
|
+
/**
|
|
23
|
+
* Session markers whose body could not be read as JSON with a
|
|
24
|
+
* parseable `approvedAt` (corrupt file, non-JSON content, missing
|
|
25
|
+
* `approvedAt`). Excluded from the window (there is no timestamp to
|
|
26
|
+
* sort them by) and never counted as auto approvals; surfaced as a
|
|
27
|
+
* single summary count instead of per-file noise.
|
|
28
|
+
*/
|
|
29
|
+
unreadableCount: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Build the auto-approval listing + last-N metric. Pure filesystem read;
|
|
33
|
+
* never throws for ordinary "nothing to see" states (missing
|
|
34
|
+
* `.approvals/`, empty directory, every marker unreadable) — those all
|
|
35
|
+
* resolve to an empty-ish section rather than an exception, mirroring
|
|
36
|
+
* the rest of doctor's read-only checks.
|
|
37
|
+
*/
|
|
38
|
+
export declare function buildUgAutoApprovals(generatedDir: string, opts: {
|
|
39
|
+
recentSessions: number;
|
|
40
|
+
}): UgAutoApprovalsSection;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// Doctor visibility half of ADR docs/decisions/2026-08-27-ug-auto-mode-approval.md
|
|
2
|
+
// slice 1 (agent-tasks 74b4b17d): the "auto approvals in the last N
|
|
3
|
+
// sessions" listing named in the ADR's "Audit and doctor" section and
|
|
4
|
+
// slice 1 acceptance criterion 7.
|
|
5
|
+
//
|
|
6
|
+
// Scans `<generatedDir>/.approvals/` for SESSION markers (skips the
|
|
7
|
+
// per-task markers named `task-<id>` and the branch-protection markers
|
|
8
|
+
// named `branch-protection-<sid>`, neither of which is a session
|
|
9
|
+
// approval), sorts the readable ones newest-first by `approvedAt`,
|
|
10
|
+
// takes the newest `recentSessions` of them, and reports how many of
|
|
11
|
+
// THOSE were minted by the auto path (`approvedBy` starting with
|
|
12
|
+
// `auto-mode:`, see auto-approve.ts's `parseAutoApprovedBy`).
|
|
13
|
+
//
|
|
14
|
+
// This is an AUDIT SURFACE, not a security check: it never verifies a
|
|
15
|
+
// marker's HMAC signature (that is `checkApprovalMarker`'s job at gate
|
|
16
|
+
// time; a forged marker still gets rejected there regardless of what
|
|
17
|
+
// this listing shows) and it never blocks or writes anything.
|
|
18
|
+
import * as fs from "node:fs";
|
|
19
|
+
import * as path from "node:path";
|
|
20
|
+
import { readRegularFileRejectingSymlink } from "../../io/read-regular-file.js";
|
|
21
|
+
import { safeJsonParse } from "../../io/safe-json-parse.js";
|
|
22
|
+
import { BRANCH_PROTECTION_MARKER_PREFIX } from "../../policy-packs/builtin/branch-protection-runtime.js";
|
|
23
|
+
import { APPROVAL_MARKER_DIRNAME, APPROVAL_MARKER_TASK_PREFIX, parseAutoApprovedBy, } from "../../policy-packs/builtin/understanding-before-execution/index.js";
|
|
24
|
+
/** Default doctor window when `--recent-sessions` is not passed. */
|
|
25
|
+
export const DEFAULT_RECENT_SESSIONS = 20;
|
|
26
|
+
function emptySection(windowSize) {
|
|
27
|
+
return {
|
|
28
|
+
approvalsDirPresent: false,
|
|
29
|
+
windowSize,
|
|
30
|
+
autoApprovedCount: 0,
|
|
31
|
+
byMode: {},
|
|
32
|
+
byHarness: {},
|
|
33
|
+
entries: [],
|
|
34
|
+
unreadableCount: 0,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Build the auto-approval listing + last-N metric. Pure filesystem read;
|
|
39
|
+
* never throws for ordinary "nothing to see" states (missing
|
|
40
|
+
* `.approvals/`, empty directory, every marker unreadable) — those all
|
|
41
|
+
* resolve to an empty-ish section rather than an exception, mirroring
|
|
42
|
+
* the rest of doctor's read-only checks.
|
|
43
|
+
*/
|
|
44
|
+
export function buildUgAutoApprovals(generatedDir, opts) {
|
|
45
|
+
const windowSize = opts.recentSessions;
|
|
46
|
+
const dir = path.join(generatedDir, APPROVAL_MARKER_DIRNAME);
|
|
47
|
+
let dirents;
|
|
48
|
+
try {
|
|
49
|
+
dirents = fs.readdirSync(dir, { withFileTypes: true });
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
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
|
+
readable.sort((a, b) => Date.parse(b.approvedAt) - Date.parse(a.approvedAt));
|
|
89
|
+
const windowed = readable.slice(0, windowSize);
|
|
90
|
+
const byMode = {};
|
|
91
|
+
const byHarness = {};
|
|
92
|
+
const entries = [];
|
|
93
|
+
for (const m of windowed) {
|
|
94
|
+
const parsedAuto = parseAutoApprovedBy(m.approvedBy);
|
|
95
|
+
if (parsedAuto === null)
|
|
96
|
+
continue;
|
|
97
|
+
byMode[parsedAuto.mode] = (byMode[parsedAuto.mode] ?? 0) + 1;
|
|
98
|
+
byHarness[parsedAuto.harness] = (byHarness[parsedAuto.harness] ?? 0) + 1;
|
|
99
|
+
entries.push({
|
|
100
|
+
sessionId: m.sessionId,
|
|
101
|
+
mode: parsedAuto.mode,
|
|
102
|
+
harness: parsedAuto.harness,
|
|
103
|
+
approvedAt: m.approvedAt,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
approvalsDirPresent: true,
|
|
108
|
+
windowSize,
|
|
109
|
+
autoApprovedCount: entries.length,
|
|
110
|
+
byMode,
|
|
111
|
+
byHarness,
|
|
112
|
+
entries,
|
|
113
|
+
unreadableCount,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=ug-auto-approvals.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ug-auto-approvals.js","sourceRoot":"","sources":["../../../src/cli/doctor/ug-auto-approvals.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface UgDelegationsSection {
|
|
2
|
+
/** Whether `<generatedDir>/.delegations/` exists at all. */
|
|
3
|
+
delegationsDirPresent: boolean;
|
|
4
|
+
/** Delegation files found in `.delegations/`, readable or not. */
|
|
5
|
+
total: number;
|
|
6
|
+
/** Of `total`, how many parsed with an `expires` segment in the past. */
|
|
7
|
+
expired: number;
|
|
8
|
+
/** Of `total`, how many were not valid JSON, or failed `parseDelegationApprovedBy`. */
|
|
9
|
+
unreadable: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Build the delegations-on-disk doctor metric. Pure filesystem read;
|
|
13
|
+
* never throws for "nothing to see" states (missing `.delegations/`,
|
|
14
|
+
* empty directory, every file unreadable), those all resolve to a
|
|
15
|
+
* zero-ish section, mirroring `buildUgAutoApprovals`. `total`/`expired`/
|
|
16
|
+
* `unreadable` read `0` for both an EMPTY and a MISSING directory; only
|
|
17
|
+
* `delegationsDirPresent` tells them apart, the same way
|
|
18
|
+
* `approvalsDirPresent` does for `buildUgAutoApprovals`, doctor's
|
|
19
|
+
* render layer uses it to stay silent when there is truly nothing on
|
|
20
|
+
* disk (`format.ts`'s own "no line for a check that found nothing"
|
|
21
|
+
* convention) while still rendering the zero-count line for a
|
|
22
|
+
* `harness delegate`-touched, now-empty directory.
|
|
23
|
+
*/
|
|
24
|
+
export declare function buildUgDelegations(generatedDir: string, opts?: {
|
|
25
|
+
now?: Date;
|
|
26
|
+
}): UgDelegationsSection;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// Doctor visibility for slice 3 delegations (ADR
|
|
2
|
+
// docs/decisions/2026-08-27-ug-auto-mode-approval.md, "Audit and
|
|
3
|
+
// doctor", agent-tasks 37ad0b05): "delegations on disk: <count>
|
|
4
|
+
// (<count> expired)" from `<generatedDir>/.delegations/`, distinct from
|
|
5
|
+
// the `.approvals/` auto-approval listing in `ug-auto-approvals.ts`.
|
|
6
|
+
//
|
|
7
|
+
// SEPARATE DIRECTORY, DELIBERATELY: a delegation never lands in
|
|
8
|
+
// `.approvals/` (delegation-markers.ts's module header), so this scan
|
|
9
|
+
// reads `DELEGATION_MARKER_DIRNAME` (".delegations") only and never
|
|
10
|
+
// touches the approvals directory `ug-auto-approvals.ts` owns. Mirrors
|
|
11
|
+
// that module's read-only-audit shape (no signature verification here
|
|
12
|
+
// either; a forged delegation still gets rejected by the dedicated
|
|
13
|
+
// verifier at gate time, `delegation-markers.ts`'s `verifyDelegation`)
|
|
14
|
+
// but does not share its code: the two scans read different
|
|
15
|
+
// directories, filter differently (no task-marker / branch-protection
|
|
16
|
+
// prefix skip here), and count differently (an approval count vs. an
|
|
17
|
+
// expiry count), so a shared helper would mostly be parameters threading
|
|
18
|
+
// through an otherwise-empty shell. `npm run check:duplication` is the
|
|
19
|
+
// backstop if that stops being true.
|
|
20
|
+
import * as fs from "node:fs";
|
|
21
|
+
import * as path from "node:path";
|
|
22
|
+
import { readRegularFileRejectingSymlink } from "../../io/read-regular-file.js";
|
|
23
|
+
import { safeJsonParse } from "../../io/safe-json-parse.js";
|
|
24
|
+
import { DELEGATION_MARKER_DIRNAME, parseDelegationApprovedBy, } from "../../policy-packs/builtin/understanding-before-execution/index.js";
|
|
25
|
+
function emptySection() {
|
|
26
|
+
return { delegationsDirPresent: false, total: 0, expired: 0, unreadable: 0 };
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* A delegation filename is always a session id: a UUID handed out by
|
|
30
|
+
* Claude Code or Codex, or (in this repo's own test fixtures) a short
|
|
31
|
+
* alnum/hyphen placeholder. Neither ever contains a literal `.` or
|
|
32
|
+
* starts with one. Filtering on that BEFORE the read is what keeps
|
|
33
|
+
* filesystem debris that happens to sit next to `.delegations/` files
|
|
34
|
+
* (macOS's `.DS_Store`, a stray `foo.txt`) from counting toward `total`
|
|
35
|
+
* at all: without it, such a file reads as valid-but-not-JSON and rolls
|
|
36
|
+
* into `unreadable`, which flips this metric's doctor line from
|
|
37
|
+
* informational (ℹ) straight to a WARNING (⚠) for debris that was never
|
|
38
|
+
* a delegation to begin with. Mirrors the same non-empty/no-traversal
|
|
39
|
+
* shape family `rejectMalformedSessionId` pins for a delegation's own
|
|
40
|
+
* filename on the write side (`delegationMarkerPathFor`), tightened to
|
|
41
|
+
* additionally exclude a dot, which is the part that actually
|
|
42
|
+
* discriminates a stray dotfile from a real session id.
|
|
43
|
+
*/
|
|
44
|
+
const SESSION_ID_BASENAME_RE = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;
|
|
45
|
+
/**
|
|
46
|
+
* Build the delegations-on-disk doctor metric. Pure filesystem read;
|
|
47
|
+
* never throws for "nothing to see" states (missing `.delegations/`,
|
|
48
|
+
* empty directory, every file unreadable), those all resolve to a
|
|
49
|
+
* zero-ish section, mirroring `buildUgAutoApprovals`. `total`/`expired`/
|
|
50
|
+
* `unreadable` read `0` for both an EMPTY and a MISSING directory; only
|
|
51
|
+
* `delegationsDirPresent` tells them apart, the same way
|
|
52
|
+
* `approvalsDirPresent` does for `buildUgAutoApprovals`, doctor's
|
|
53
|
+
* render layer uses it to stay silent when there is truly nothing on
|
|
54
|
+
* disk (`format.ts`'s own "no line for a check that found nothing"
|
|
55
|
+
* convention) while still rendering the zero-count line for a
|
|
56
|
+
* `harness delegate`-touched, now-empty directory.
|
|
57
|
+
*/
|
|
58
|
+
export function buildUgDelegations(generatedDir, opts = {}) {
|
|
59
|
+
const now = opts.now ?? new Date();
|
|
60
|
+
const dir = path.join(generatedDir, DELEGATION_MARKER_DIRNAME);
|
|
61
|
+
let dirents;
|
|
62
|
+
try {
|
|
63
|
+
dirents = fs.readdirSync(dir, { withFileTypes: true });
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return emptySection();
|
|
67
|
+
}
|
|
68
|
+
let total = 0;
|
|
69
|
+
let expired = 0;
|
|
70
|
+
let unreadable = 0;
|
|
71
|
+
for (const d of dirents) {
|
|
72
|
+
// Not a session-id-shaped basename: never a delegation file (a
|
|
73
|
+
// stray dotfile, an extensioned file, a directory, ...), skip
|
|
74
|
+
// before even reading it. The once-per-session adoption ledger is
|
|
75
|
+
// never nested under here: `.delegation-adoptions/` is a SIBLING of
|
|
76
|
+
// `.delegations/` (see delegation-markers.ts's module header), so
|
|
77
|
+
// this filter has no ledger-shaped entry to reason about; an
|
|
78
|
+
// ordinary session-id-named directory would still fall through to
|
|
79
|
+
// the `not-regular` skip below, same as any other directory would.
|
|
80
|
+
if (!SESSION_ID_BASENAME_RE.test(d.name))
|
|
81
|
+
continue;
|
|
82
|
+
const full = path.join(dir, d.name);
|
|
83
|
+
const read = readRegularFileRejectingSymlink(full);
|
|
84
|
+
// A symlink / non-regular / raced-away entry is not a delegation
|
|
85
|
+
// file this metric owns an opinion about; skip silently, same
|
|
86
|
+
// reasoning as `buildUgAutoApprovals`.
|
|
87
|
+
if (read.kind === "symlink" || read.kind === "not-regular" || read.kind === "missing") {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
total++;
|
|
91
|
+
if (read.kind === "unreadable") {
|
|
92
|
+
unreadable++;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
const parsed = safeJsonParse(read.content);
|
|
96
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
97
|
+
unreadable++;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
const obj = parsed;
|
|
101
|
+
const segments = parseDelegationApprovedBy(obj["approvedBy"]);
|
|
102
|
+
if (!segments.ok) {
|
|
103
|
+
unreadable++;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (Date.parse(segments.value.expiresAt) <= now.getTime()) {
|
|
107
|
+
expired++;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return { delegationsDirPresent: true, total, expired, unreadable };
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=ug-delegations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ug-delegations.js","sourceRoot":"","sources":["../../../src/cli/doctor/ug-delegations.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Mode } from "../../policy-packs/builtin/understanding-before-execution.js";
|
|
2
2
|
import type { Manifest } from "../../schema/index.js";
|
|
3
|
+
import type { PolicyPack } from "../../schema/policy-packs.js";
|
|
3
4
|
export interface UnderstandingModeEnvDivergence {
|
|
4
5
|
/** `UNDERSTANDING_GATE_MODE`, normalised (trim + lowercase, mirrors `resolveMode`'s own normalisation). */
|
|
5
6
|
envMode: Mode;
|
|
@@ -36,4 +37,15 @@ export interface UnderstandingModeEnvDivergence {
|
|
|
36
37
|
* warn about.
|
|
37
38
|
* - the normalised env mode differs from the resolved `config.mode`.
|
|
38
39
|
*/
|
|
40
|
+
/**
|
|
41
|
+
* Shared gate for the understanding-gate doctor sub-checks that only
|
|
42
|
+
* make sense when the pack is actually live: this divergence advisory,
|
|
43
|
+
* and (ADR docs/decisions/2026-08-27-ug-auto-mode-approval.md slice 1,
|
|
44
|
+
* agent-tasks 74b4b17d) the auto-approval listing and settings-drift
|
|
45
|
+
* checks. Declared-but-disabled counts as not enabled, mirroring the
|
|
46
|
+
* `p.enabled !== false` gate `checkUnderstandingModeEnvDivergence` uses
|
|
47
|
+
* inline below.
|
|
48
|
+
*/
|
|
49
|
+
export declare function findEnabledUnderstandingPack(manifest: Manifest): PolicyPack | undefined;
|
|
50
|
+
export declare function isUnderstandingPackEnabled(manifest: Manifest): boolean;
|
|
39
51
|
export declare function checkUnderstandingModeEnvDivergence(manifest: Manifest, env: NodeJS.ProcessEnv): UnderstandingModeEnvDivergence | undefined;
|
|
@@ -48,8 +48,23 @@ import { isMode, MODE_ENV, PACK_NAME as UNDERSTANDING_PACK_NAME, resolveModeFrom
|
|
|
48
48
|
* warn about.
|
|
49
49
|
* - the normalised env mode differs from the resolved `config.mode`.
|
|
50
50
|
*/
|
|
51
|
+
/**
|
|
52
|
+
* Shared gate for the understanding-gate doctor sub-checks that only
|
|
53
|
+
* make sense when the pack is actually live: this divergence advisory,
|
|
54
|
+
* and (ADR docs/decisions/2026-08-27-ug-auto-mode-approval.md slice 1,
|
|
55
|
+
* agent-tasks 74b4b17d) the auto-approval listing and settings-drift
|
|
56
|
+
* checks. Declared-but-disabled counts as not enabled, mirroring the
|
|
57
|
+
* `p.enabled !== false` gate `checkUnderstandingModeEnvDivergence` uses
|
|
58
|
+
* inline below.
|
|
59
|
+
*/
|
|
60
|
+
export function findEnabledUnderstandingPack(manifest) {
|
|
61
|
+
return manifest.policy_packs.find((p) => p.name === UNDERSTANDING_PACK_NAME && p.enabled !== false);
|
|
62
|
+
}
|
|
63
|
+
export function isUnderstandingPackEnabled(manifest) {
|
|
64
|
+
return findEnabledUnderstandingPack(manifest) !== undefined;
|
|
65
|
+
}
|
|
51
66
|
export function checkUnderstandingModeEnvDivergence(manifest, env) {
|
|
52
|
-
const pack = manifest
|
|
67
|
+
const pack = findEnabledUnderstandingPack(manifest);
|
|
53
68
|
if (!pack)
|
|
54
69
|
return undefined;
|
|
55
70
|
const envRaw = env[MODE_ENV];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"understanding-mode-env.js","sourceRoot":"","sources":["../../../src/cli/doctor/understanding-mode-env.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,wEAAwE;AACxE,mEAAmE;AACnE,EAAE;AACF,iEAAiE;AACjE,0EAA0E;AAC1E,kEAAkE;AAClE,wEAAwE;AACxE,kEAAkE;AAClE,sEAAsE;AACtE,oEAAoE;AACpE,sEAAsE;AACtE,yEAAyE;AACzE,wEAAwE;AACxE,yEAAyE;AACzE,uDAAuD;AACvD,EAAE;AACF,qEAAqE;AACrE,uEAAuE;AACvE,wEAAwE;AACxE,sEAAsE;AACtE,4DAA4D;AAC5D,wEAAwE;AACxE,yEAAyE;AACzE,wEAAwE;AACxE,qEAAqE;AACrE,yEAAyE;AACzE,+DAA+D;AAC/D,EAAE;AACF,+DAA+D;AAC/D,0EAA0E;AAC1E,kEAAkE;AAClE,2BAA2B;AAE3B,OAAO,EACL,MAAM,EACN,QAAQ,EACR,SAAS,IAAI,uBAAuB,EACpC,qBAAqB,GAEtB,MAAM,8DAA8D,CAAC;
|
|
1
|
+
{"version":3,"file":"understanding-mode-env.js","sourceRoot":"","sources":["../../../src/cli/doctor/understanding-mode-env.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,wEAAwE;AACxE,mEAAmE;AACnE,EAAE;AACF,iEAAiE;AACjE,0EAA0E;AAC1E,kEAAkE;AAClE,wEAAwE;AACxE,kEAAkE;AAClE,sEAAsE;AACtE,oEAAoE;AACpE,sEAAsE;AACtE,yEAAyE;AACzE,wEAAwE;AACxE,yEAAyE;AACzE,uDAAuD;AACvD,EAAE;AACF,qEAAqE;AACrE,uEAAuE;AACvE,wEAAwE;AACxE,sEAAsE;AACtE,4DAA4D;AAC5D,wEAAwE;AACxE,yEAAyE;AACzE,wEAAwE;AACxE,qEAAqE;AACrE,yEAAyE;AACzE,+DAA+D;AAC/D,EAAE;AACF,+DAA+D;AAC/D,0EAA0E;AAC1E,kEAAkE;AAClE,2BAA2B;AAE3B,OAAO,EACL,MAAM,EACN,QAAQ,EACR,SAAS,IAAI,uBAAuB,EACpC,qBAAqB,GAEtB,MAAM,8DAA8D,CAAC;AAyBtE;;;;;;;;;;;;;;;GAeG;AACH;;;;;;;;GAQG;AACH,MAAM,UAAU,4BAA4B,CAAC,QAAkB;IAC7D,OAAO,QAAQ,CAAC,YAAY,CAAC,IAAI,CAC/B,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,uBAAuB,IAAI,CAAC,CAAC,OAAO,KAAK,KAAK,CACjE,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,QAAkB;IAC3D,OAAO,4BAA4B,CAAC,QAAQ,CAAC,KAAK,SAAS,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,mCAAmC,CACjD,QAAkB,EAClB,GAAsB;IAEtB,MAAM,IAAI,GAAG,4BAA4B,CAAC,QAAQ,CAAC,CAAC;IACpD,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC;IAE5B,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC7B,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,EAAE;QAAE,OAAO,SAAS,CAAC;IAC5D,MAAM,aAAa,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAClD,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC;QAAE,OAAO,SAAS,CAAC;IAE7C,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,aAAa,EAAE,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;IACjF,IAAI,aAAa,KAAK,UAAU;QAAE,OAAO,SAAS,CAAC;IAEnD,yEAAyE;IACzE,yEAAyE;IACzE,yEAAyE;IACzE,uEAAuE;IACvE,wEAAwE;IACxE,wEAAwE;IACxE,MAAM,WAAW,GAAG,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,sBAAsB,CAAC;IAEhE,OAAO;QACL,OAAO,EAAE,aAAa;QACtB,UAAU;QACV,OAAO,EAAE,GAAG,QAAQ,IAAI,aAAa,8BAA8B,UAAU,EAAE;QAC/E,MAAM,EAAE;YACN,uFAAuF;gBACrF,iFAAiF,aAAa,GAAG;YACnG,yBAAyB,UAAU,GAAG,WAAW,EAAE;YACnD,uDAAuD,QAAQ,wBAAwB;SACxF;KACF,CAAC;AACJ,CAAC"}
|
package/dist/cli/index.js
CHANGED
|
@@ -41,6 +41,8 @@ import { approveBranchProtection } from "./approve/branch-protection.js";
|
|
|
41
41
|
import { approveRisk } from "./approve/risk.js";
|
|
42
42
|
import { approveUnderstanding } from "./approve/understanding.js";
|
|
43
43
|
import { readPipedStdin } from "./approve/stdin-report.js";
|
|
44
|
+
import { issueDelegation } from "./delegate/index.js";
|
|
45
|
+
import { InvalidDurationError, parseDurationSeconds } from "../policies/index.js";
|
|
44
46
|
import { runRecordDogfood, runRecordReview, runRecordReviewSubagent, } from "./record/index.js";
|
|
45
47
|
import { describe, isPillar } from "./describe.js";
|
|
46
48
|
import { diff as diffRun } from "./diff/index.js";
|
|
@@ -175,6 +177,7 @@ export function buildProgram(opts = {}) {
|
|
|
175
177
|
.option("--project <name>", "apply per-project overrides for this project name")
|
|
176
178
|
.option("--shallow", "skip MCP probes (CLI --version probes still run); report manifest-reference state only")
|
|
177
179
|
.option("--target <runtime>", `additionally evaluate the harness-side adapter health for a runtime (allowed: ${KNOWN_DOCTOR_TARGETS.join(", ")})`)
|
|
180
|
+
.option("--recent-sessions <n>", "understanding-gate auto-approval doctor window: how many of the newest .approvals/ session markers to scan (default: 20)")
|
|
178
181
|
.option("--json", "emit a structured JSON DoctorReport instead of prose (--rm-rogue-ledgers is ignored with --json)")
|
|
179
182
|
.option("--rm-rogue-ledgers", "after reporting, delete each rogue evidence-ledger directory found (prompts per hit; combine with --yes to skip prompts)")
|
|
180
183
|
.option("--yes", "with --rm-rogue-ledgers, skip per-hit confirmation prompts")
|
|
@@ -187,12 +190,22 @@ export function buildProgram(opts = {}) {
|
|
|
187
190
|
}
|
|
188
191
|
target = options.target;
|
|
189
192
|
}
|
|
193
|
+
let recentSessions;
|
|
194
|
+
if (options.recentSessions !== undefined) {
|
|
195
|
+
const parsed = Number(options.recentSessions);
|
|
196
|
+
if (!Number.isInteger(parsed) || parsed < 1) {
|
|
197
|
+
stderr(`--recent-sessions must be an integer >= 1, got ${JSON.stringify(options.recentSessions)}\n`);
|
|
198
|
+
throw new HarnessExitError("", EX_USAGE);
|
|
199
|
+
}
|
|
200
|
+
recentSessions = parsed;
|
|
201
|
+
}
|
|
190
202
|
const report = await doctor({
|
|
191
203
|
configPath: options.config,
|
|
192
204
|
project: options.project,
|
|
193
205
|
shallow: options.shallow,
|
|
194
206
|
versionProbe: defaultVersionProbe,
|
|
195
207
|
...(target !== undefined ? { target } : {}),
|
|
208
|
+
...(recentSessions !== undefined ? { recentSessions } : {}),
|
|
196
209
|
...(opts.rogueLedgerScanOptions !== undefined
|
|
197
210
|
? { rogueLedgerScanOptions: opts.rogueLedgerScanOptions }
|
|
198
211
|
: {}),
|
|
@@ -1505,6 +1518,86 @@ export function buildProgram(opts = {}) {
|
|
|
1505
1518
|
}
|
|
1506
1519
|
stdout(`${lines.join("\n")}\n`);
|
|
1507
1520
|
});
|
|
1521
|
+
// `harness delegate` (Slice 3 of docs/decisions/2026-08-27-ug-auto-mode-approval.md,
|
|
1522
|
+
// agent-tasks 37ad0b05): issue a signed pre-authorization for a
|
|
1523
|
+
// headless `claude -p` child session, bound to an already-approved
|
|
1524
|
+
// PARENT session. NOT an approval: the child still writes and gets its
|
|
1525
|
+
// own Understanding Report checked by its own PreToolUse hook before
|
|
1526
|
+
// its auto-marker is minted. Writes harness.generated/.delegations/,
|
|
1527
|
+
// never .approvals/.
|
|
1528
|
+
program
|
|
1529
|
+
.command("delegate")
|
|
1530
|
+
.description("Issue a signed delegation for a headless `claude -p` child session, bound to " +
|
|
1531
|
+
"an already-approved parent session (pre-authorization, not an approval: the " +
|
|
1532
|
+
"child still writes and gets its own Understanding Report checked). Writes " +
|
|
1533
|
+
"harness.generated/.delegations/<child-sid>, never .approvals/. See " +
|
|
1534
|
+
"docs/decisions/2026-08-27-ug-auto-mode-approval.md.")
|
|
1535
|
+
.requiredOption("--child-session <uuid>", "session id of the claude -p child this delegation authorizes (UUID)")
|
|
1536
|
+
.option("--cwd <path>", "bind the delegation to this working directory; at least one of --cwd/--task is required")
|
|
1537
|
+
.option("--task <id>", "bind the delegation to this agent-tasks task id; at least one of --cwd/--task is required")
|
|
1538
|
+
.option("--ttl <duration>", "delegation lifetime (e.g. 30m, 1h); default: the pack's approval_lifecycle.max_age when set, else 1h")
|
|
1539
|
+
.option("--report <path>", "fallback shape: bind the child's Understanding Report by content + path hash at spawn time; not yet consumed by the child hook, see the pack doc")
|
|
1540
|
+
.option("--session-id <id>", "explicit parent session id (default: $CLAUDE_CODE_SESSION_ID, then $CLAUDE_SESSION_ID, then $CODEX_SESSION_ID, then staged .pending-approval)")
|
|
1541
|
+
.option("--config <path>", "manifest path (default: ~/.harness/harness.yaml; legacy fallback ~/.claude/harness.yaml)")
|
|
1542
|
+
.option("--project <name>", "apply per-project overrides")
|
|
1543
|
+
.action(async (options) => {
|
|
1544
|
+
// Usage-level check ahead of every deeper resolution, with the
|
|
1545
|
+
// exact message the ADR slice 3 acceptance criterion pins.
|
|
1546
|
+
// `issueDelegation` carries the identical check (reason
|
|
1547
|
+
// "no-binding") for callers that invoke it directly (the smoke
|
|
1548
|
+
// runner, tests) without going through commander's flag parsing.
|
|
1549
|
+
if (options.cwd === undefined && options.task === undefined) {
|
|
1550
|
+
throw new HarnessExitError("a delegation must bind a cwd or a task", EX_FAIL);
|
|
1551
|
+
}
|
|
1552
|
+
let ttlSeconds;
|
|
1553
|
+
if (options.ttl) {
|
|
1554
|
+
try {
|
|
1555
|
+
ttlSeconds = parseDurationSeconds(options.ttl);
|
|
1556
|
+
}
|
|
1557
|
+
catch (err) {
|
|
1558
|
+
if (err instanceof InvalidDurationError) {
|
|
1559
|
+
throw new HarnessExitError(`--ttl: ${err.message}`, EX_USAGE);
|
|
1560
|
+
}
|
|
1561
|
+
throw err;
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
const cliOpts = {
|
|
1565
|
+
childSessionId: options.childSession,
|
|
1566
|
+
};
|
|
1567
|
+
// `!== undefined` (not truthy), agreeing with the usage check
|
|
1568
|
+
// above: a truthy check would silently drop an explicit `--cwd
|
|
1569
|
+
// ''` / `--task ''` instead of letting `issueDelegation` refuse
|
|
1570
|
+
// it with `invalid-cwd` / `invalid-task` (L3 fix, agent-tasks
|
|
1571
|
+
// 37ad0b05).
|
|
1572
|
+
if (options.cwd !== undefined)
|
|
1573
|
+
cliOpts.cwd = options.cwd;
|
|
1574
|
+
if (options.task !== undefined)
|
|
1575
|
+
cliOpts.taskId = options.task;
|
|
1576
|
+
if (ttlSeconds !== undefined)
|
|
1577
|
+
cliOpts.ttlSeconds = ttlSeconds;
|
|
1578
|
+
if (options.report)
|
|
1579
|
+
cliOpts.reportPath = options.report;
|
|
1580
|
+
if (options.sessionId)
|
|
1581
|
+
cliOpts.parentSessionId = options.sessionId;
|
|
1582
|
+
if (options.config)
|
|
1583
|
+
cliOpts.configPath = options.config;
|
|
1584
|
+
if (options.project)
|
|
1585
|
+
cliOpts.project = options.project;
|
|
1586
|
+
const result = await issueDelegation(cliOpts);
|
|
1587
|
+
if (!result.ok) {
|
|
1588
|
+
throw new HarnessExitError(`delegate refused (${result.reason}): ${result.detail}`, EX_FAIL);
|
|
1589
|
+
}
|
|
1590
|
+
const lines = [
|
|
1591
|
+
`delegation: ✓ ${result.filePath} (child ${result.childSessionId}, parent ${result.parentSessionId}, expires ${result.expiresAt})`,
|
|
1592
|
+
];
|
|
1593
|
+
if (result.ledgerFact.written) {
|
|
1594
|
+
lines.push(`ledger: ✓ wrote understanding-delegated:${result.childSessionId}:${result.parentSessionId} (audit only)`);
|
|
1595
|
+
}
|
|
1596
|
+
else {
|
|
1597
|
+
lines.push(`ledger: ⚠ skipped (${result.ledgerFact.reason ?? "unknown"}) (audit only)`);
|
|
1598
|
+
}
|
|
1599
|
+
stdout(`${lines.join("\n")}\n`);
|
|
1600
|
+
});
|
|
1508
1601
|
const VALID_DECISION_FILTERS = [
|
|
1509
1602
|
"allow",
|
|
1510
1603
|
"warn",
|
|
@@ -1630,7 +1723,7 @@ export function buildProgram(opts = {}) {
|
|
|
1630
1723
|
});
|
|
1631
1724
|
program
|
|
1632
1725
|
.command("audit")
|
|
1633
|
-
.description("Replay policy decisions from the evidence ledger for a time window")
|
|
1726
|
+
.description("Replay policy decisions from the evidence ledger for a time window, plus an approvals section listing raw understanding-gate approval facts")
|
|
1634
1727
|
.option("--since <duration>", "time window (default: 24h)")
|
|
1635
1728
|
.option("--policy <name>", "filter to a single policy by name")
|
|
1636
1729
|
.option("--outcome <outcome>", "filter by decision outcome (allow / warn / require_approval / deny / warn-degraded / deny-degraded)")
|
|
@@ -1727,6 +1820,8 @@ export function buildProgram(opts = {}) {
|
|
|
1727
1820
|
.option("--expect-no-hook <names>", "comma-separated list of hook names / events that MUST NOT fire (repeatable)", (value, prev = []) => prev.concat(splitCommaList(value)), [])
|
|
1728
1821
|
.option("--expect-exit <n>", "expected result.is_error: 0 ⇒ false, !=0 ⇒ true")
|
|
1729
1822
|
.option("--expect-decision <kind>", "policy decision must be one of allow|deny|warn")
|
|
1823
|
+
.option("--no-delegate", "do not pre-authorize the spawned child (docs/decisions/2026-08-27-ug-auto-mode-approval.md); " +
|
|
1824
|
+
"it then has no delegation binding it to this approved session and falls back to the plain opt-in path")
|
|
1730
1825
|
.action(async (options) => {
|
|
1731
1826
|
const expectations = {};
|
|
1732
1827
|
if (options.expectHook && options.expectHook.length > 0) {
|
|
@@ -1750,6 +1845,10 @@ export function buildProgram(opts = {}) {
|
|
|
1750
1845
|
outputDir: options.outputDir,
|
|
1751
1846
|
expectations,
|
|
1752
1847
|
};
|
|
1848
|
+
// commander's `--no-delegate` negates `options.delegate` (default
|
|
1849
|
+
// true); only an explicit `--no-delegate` flips it to `false`.
|
|
1850
|
+
if (options.delegate === false)
|
|
1851
|
+
smokeOpts.noDelegate = true;
|
|
1753
1852
|
if (options.config)
|
|
1754
1853
|
smokeOpts.configPath = options.config;
|
|
1755
1854
|
if (options.project)
|