@lannguyensi/harness 0.52.0 → 0.54.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 +82 -0
- package/dist/cli/delegate/index.d.ts +15 -2
- package/dist/cli/delegate/index.js +128 -4
- package/dist/cli/delegate/index.js.map +1 -1
- package/dist/cli/doctor/bypass-without-auto-approve.d.ts +38 -0
- package/dist/cli/doctor/bypass-without-auto-approve.js +95 -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/dry-run.js +32 -1
- package/dist/cli/dry-run.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 +57 -11
- 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 +100 -8
- package/dist/cli/init/profiles.js.map +1 -1
- package/dist/cli/init/templates.d.ts +1 -1
- package/dist/cli/init/templates.js +174 -3
- package/dist/cli/init/templates.js.map +1 -1
- package/dist/cli/pack/hook-pre-tool-use.js +219 -115
- 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/cli/record/index.d.ts +8 -0
- package/dist/cli/record/index.js +55 -7
- package/dist/cli/record/index.js.map +1 -1
- package/dist/cli/validate/checks.js +61 -9
- package/dist/cli/validate/checks.js.map +1 -1
- package/dist/io/extract.d.ts +29 -0
- package/dist/io/extract.js +36 -0
- package/dist/io/extract.js.map +1 -1
- 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/policies/index.d.ts +1 -1
- package/dist/policies/index.js +1 -1
- package/dist/policies/index.js.map +1 -1
- 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 +51 -0
- package/dist/policy-packs/builtin/understanding-before-execution/delegation-markers.js +55 -1
- 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 +175 -0
- package/dist/policy-packs/builtin/understanding-before-execution/permission-mode-observations.js.map +1 -0
- package/dist/runtime/destructive-shell-floor.d.ts +19 -0
- package/dist/runtime/destructive-shell-floor.js +597 -0
- package/dist/runtime/destructive-shell-floor.js.map +1 -0
- package/dist/runtime/git-context.js +7 -1
- package/dist/runtime/git-context.js.map +1 -1
- package/dist/runtime/intercept.d.ts +10 -7
- package/dist/runtime/intercept.js +108 -9
- package/dist/runtime/intercept.js.map +1 -1
- package/dist/runtime/read-only-bash.d.ts +21 -0
- package/dist/runtime/read-only-bash.js +484 -0
- package/dist/runtime/read-only-bash.js.map +1 -1
- package/dist/runtime/risk-classifier.js +68 -13
- package/dist/runtime/risk-classifier.js.map +1 -1
- package/dist/runtime/when-eval.d.ts +18 -5
- package/dist/runtime/when-eval.js +64 -11
- package/dist/runtime/when-eval.js.map +1 -1
- package/dist/runtime/workflow-policies.d.ts +43 -1
- package/dist/runtime/workflow-policies.js +158 -1
- package/dist/runtime/workflow-policies.js.map +1 -1
- package/dist/schema/extract.d.ts +4 -0
- package/dist/schema/extract.js +64 -1
- package/dist/schema/extract.js.map +1 -1
- package/dist/schema/index.d.ts +13 -0
- package/dist/schema/policies.d.ts +19 -0
- package/dist/schema/policies.js +17 -1
- package/dist/schema/policies.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,88 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.54.0] - 2026-09-01
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **The child's PreToolUse hook now consumes the launcher-supplied Understanding Report the `--report` fallback shape of `harness delegate` mints** (agent-tasks `49d1ee41`, follow-up to UG auto-mode slice 3, agent-tasks `37ad0b05`). Previously `harness delegate --report <path>` minted a fully signed, verifiable delegation, but the child's hook never passed `verifyDelegation` a `launcherReportPath`, so every report-bound delegation was refused (`report_path_mismatch`) unconditionally and the transcript scan was the only report channel this release actually acted on, documented in the pack doc as "issued and verifiable, not yet consumed".
|
|
15
|
+
- **Conventional path channel.** `delegationReportPathFor` (`src/policy-packs/builtin/understanding-before-execution/delegation-markers.ts`) derives `harness.generated/.delegation-reports/<child-sid>.md` from nothing but the child session id, which `harness delegate` already takes as a required argument at delegation time. `harness delegate --report <path>` copies the operator's file there (mode 0600), refusing (new reason `report-conflict`, distinct from `report-unreadable`) rather than silently overwriting a DIFFERENT file already staged for the same child session; an identical restage is a no-op. The `report=` segment of the signed `approvedBy` string, and `reportContentHash`, now bind THIS conventional copy (its realpath and its bytes), not the operator's original `--report` argument, since the child's hook has no channel to learn an arbitrary operator-chosen path.
|
|
16
|
+
- **Hook side.** `src/cli/pack/hook-pre-tool-use.ts` offers the conventional path to `verifyDelegation` unconditionally on every delegation check (a no-op for a delegation that binds no `report=` segment). On success, a report-bound delegation (`verified.reportPathHash !== undefined`) skips the transcript scan entirely and instead reads the conventional file, adopts it once via the SAME once-per-session `.delegation-adoptions/<child-sid>` ledger the transcript scan uses, keyed by the report's CONTENT hash, namespaced `report:` so the id shapes can never collide with the scan's `uuid:`/`digest:` ids, and persists it `pending` through the same `persistStdinReport` path, for the auto path below to consume exactly as it consumes a transcript-captured report. A missing, moved, or content-modified file at the conventional path blocks through the verifier's existing distinct reasons (`report_path_mismatch` / `report_content_mismatch`); no new refusal reason was added on the read side, and the special-cased "not yet consumed" stderr wording was removed.
|
|
17
|
+
- **Adoption-once, verified for real.** Mutation probe 1: disabling the once-per-session adoption check (`adopted.ids.has(entryId)`) turns the new "(r2) ... cannot mint a second marker from the SAME launcher report once the first expired" test red; restoring the check turns it green again. Mutation probe 2: disabling the verifier's content-hash comparison (`actualContentHash !== boundContentHash` inside `verifyDelegation`) turns the new "(w2) ... whose CONTENT was modified" test red, since nothing then stops a modified conventional file's stale hash from minting; restoring the comparison turns it green again. A round-2 fix added a third probe: `verifyDelegation` now returns the exact bytes it verified (`reportContent`) and the hook persists THOSE bytes instead of re-reading the file, closing a verify-then-persist race. Restated in round 3 to name what actually discriminates: appending trailing text to the persisted markdown is dropped by the parser and leaves the probe green, so mutation probe A2 instead alters a PARSED section (`currentUnderstanding`) before persist, which turns the "(w3) ... report-bound delegation" test red; restoring the unmodified bytes turns it green again. The "reintroduces a second, unchecked read" half is pinned by its own dedicated test added in round 3: "(w4) pins the single-read persist path" (`tests/cli/pack-hook-pre-tool-use-delegate.test.ts`) mocks the conventional file's reader to hand back the bound bytes on the first read and tampered bytes on any read after that, so a regression that re-reads the file lands the tampered content in the persisted report and turns (w4) red; restoring the single-read path (`reportContent = verified.reportContent`, no second read) turns it green again.
|
|
18
|
+
- **Tests** (`tests/cli/delegate.test.ts`, `tests/cli/pack-hook-pre-tool-use-delegate.test.ts`): the conventional copy's mode/content/path binding, the `report-conflict` refusal and its idempotent-restage counterpart, hook cases for happy path (adopted, persisted pending, marker minted, transcript never scanned), a moved (absent) conventional file, a content-modified conventional file, second-call-after-marker-expiry adoption (the once-per-report mint), and confirmation that an unbound/cwd-task-only delegation is unaffected (the existing suite's negative controls, unchanged).
|
|
19
|
+
- Docs: `docs/decisions/2026-08-27-ug-auto-mode-approval.md` ("Amendment: the launcher-report channel is consumed by the child hook"), `docs/policy-packs/understanding-before-execution.md` (the `--report` fallback-shape paragraph and a "Cleanup" residue sentence for `.delegation-reports/`, not yet gc'd, folded into gc follow-up task `3ece079d`), `docs/CLI.md` (the `delegate` row), `docs/okf/understanding-gate-auto-mode-signals.md` ("Verdict (iv), interactive": the fallback channel is now live).
|
|
20
|
+
|
|
21
|
+
- **The review-before-merge gate now covers `mcp__agent-tasks__task_merge` and `mcp__agent-tasks__task_finish` (`autoMerge`), via a new `trigger.input_match` predicate** (task `2699b476`). Closes the residual the 99f47307 Slice 1 entry named twice below (round-2 F4 and the round-3 residual list, entry (c)): only `pull_requests_merge` and `gh pr merge` were gated, so the two task-scoped verbs that also land a PR passed with "no policy matched event".
|
|
22
|
+
- **New trigger field `trigger.input_match`** (`src/schema/policies.ts`, `src/schema/extract.ts`): a map from a `toolArgs.`-namespaced extract expression (same grammar and parser as `trigger.extract`) to a `string`/`number`/`boolean` literal, ANDed onto the other trigger fields and compared by STRICT equality (same JSON type and value). A missing path never matches, an empty map is a validation error, and a non-`toolArgs` namespace is rejected at parse time with a message naming the namespace, so a typo like `event.autoMerge` is a loud error rather than a predicate that quietly never fires. The check lives in `PolicyTriggerSchema`, the same layer `bash_match` / `path_match` are declared at, so `validate`, `apply`, `doctor`, `loadManifest` and the intercept entrypoint all refuse the bad shape. This is what separates the merging mode of `task_finish` (`autoMerge: true`, both the soloMode work claim and the review claim + approve) from the ordinary one, which merges nothing and stays uninterceptable.
|
|
23
|
+
- **Evaluated once, read twice.** `firstInputMatchMismatch` (`src/io/extract.ts`) is called by `policyMatchesEvent` (`src/runtime/intercept.ts`) and by dry-run's `policyMatchesTool` (`src/cli/dry-run.ts`), so `harness policy dry-run` predicts exactly what `policy intercept` decides, keeping the parity contract `docs/okf/debug-verb-selection.md` records. A non-matching payload comes back in the "could match" bucket naming the failing entry and both values. Fail direction on an unparseable expression (only reachable from a hand-built `Manifest`): the entry is treated as HOLDING, so the gate stays armed rather than silently excusing the call.
|
|
24
|
+
- **Two new shipped policies plus their hooks**, in `FULL_TEMPLATE`, `TEAM_TEMPLATE` and `docs/examples/full-manifest.yaml`: `review-before-task-merge` (`match: mcp__agent-tasks__task_merge`) and `review-before-task-finish-automerge` (`match: mcp__agent-tasks__task_finish`, `input_match: { toolArgs.autoMerge: true }`), both extracting `TASK_ID: toolArgs.taskId` and requiring `review:${TASK_ID}`. Each gets its OWN hook (`require-review-evidence-task-merge` / `require-review-evidence-task-finish`) because a hook's `match` is what `harness apply` projects into settings.json's tool-name matcher: a matcher for `pull_requests_merge` never spawns `harness policy intercept` for either verb. An existing install picks these up only by re-running `harness init` / `harness apply`.
|
|
25
|
+
- **Derivation parity.** `src/runtime/workflow-policies.ts` derives the same two policies from a qualifying `workflows:` entry, structurally identical to the shipped ones apart from name and description (pinned by the parity assertion in `tests/runtime/workflow-policies.test.ts`, now covering all four gates, and for the two new ones also their `producers:` / `ux:`). Each is derived ONLY when its own hook is declared: a derived gate whose hook is not wired into settings.json would never be invoked and would read as enforced while doing nothing, the same "No-Op that LOOKS protective" shape `checkWorkflowGateWiring` exists to prevent. A manifest predating the two hooks therefore derives exactly the pair it always did, and `hasWiredMergeGateHooks` is unchanged. `trigger.input_match` also joined `triggerSurfaceKey`, so a hand-authored policy that gates a DIFFERENT `autoMerge` value no longer dedupes the derived gate away.
|
|
26
|
+
- **Producer.** `harness record review` grew an optional `--task <id>` that appends `review:<task-id>` to the same single fact it already writes (`review:<pr>` + `review:<branch>` + `review:<base>`), so one `harness record review --pr <n> --task <id> "<summary>"` satisfies all four merge gates. The id is validated loosely (non-empty, no whitespace); a value with internal whitespace is rejected because it would split into two tags in the fact content and satisfy a gate keyed on the first half.
|
|
27
|
+
- **Verification.** Intercept-level allow/deny coverage runs through the real `runInterceptCli` entrypoint (`tests/runtime/intercept-cli-workflow-gate.test.ts`): deny with an empty ledger and allow with the recorded fact for both verbs; a plain `task_finish`, `autoMerge: false`, and `autoMerge: "true"` (string) each pass with NO decision recorded at all; a `task_merge` with no `taskId` in the payload denies as `deny-degraded` rather than allowing; a manifest carrying both the hand-authored gate and the derivation evaluates exactly one policy per call. Mutation probes (each applied, run, and reverted): making the `input_match` evaluation unconditionally true turns the three not-intercepted tests red; dropping the task_merge policy from the derivation turns the parity test red; dropping the `input_match` arm from dry-run's `policyMatchesTool` turns the dry-run parity fixture red; dropping `input_match` from `triggerSurfaceKey` turns the two surface-key dedupe tests red.
|
|
28
|
+
- Docs: `docs/for-agents.md` (the residual paragraph now describes the coverage and the `--task` producer), `docs/writing-custom-policies.md` (new "One tool, two modes" recipe plus the field-reference row), `docs/CLI.md` (`record review --task` and its tag-semantics note).
|
|
29
|
+
- **Round-2 hardening** (review round 1 on the same task): `input_match` now evaluates against BOTH `tool_input` and `raw_input` when a payload carries them as separate non-null objects, so a benign value in one field can no longer shadow a merging value in the other and leave the gate unarmed; and `harness validate` now warns when a qualifying workflow wires the original `pull_requests_merge` / `gh pr merge` evidence pair but not the two task-scoped hooks, naming the uncovered verbs instead of staying silent.
|
|
30
|
+
|
|
31
|
+
- **`tests/decisions-citations-resolve.test.ts` guards every backtick-anchored source citation in `docs/decisions/*.md`** (agent-tasks `6f719bb4`). Each citation must be a repo-relative, anchored `` `path:N-M#"text on line M"` `` (or `:N` for a single line); the guard fails on a stale path, an out-of-range or blank-start range, an anchor absent from the range's last line, or an anchor that recurs elsewhere in the range. That pins the range's end line: a citation cannot silently drift onto different code without also changing what its anchor matches. It does not pin the start line on its own, and a bare, non-backtick line reference is outside the grammar and stays invisible to the guard.
|
|
32
|
+
|
|
33
|
+
### Changed
|
|
34
|
+
|
|
35
|
+
- **Permission-mode observation stays unsigned; the decision and its threat-model reasoning are now recorded in the ADR** (agent-tasks `a4ceb6be`), not just a review-round changelog note. `docs/decisions/2026-08-27-ug-auto-mode-approval.md` gained the "Amendment: permission-mode observation stays unsigned" section: signing the hook-written observation would only prove the hook signed the record, not that a session actually ran under the claimed permission mode, and it would open a second, unconditional signing surface for the marker-signing key (not a path to mint an approval marker, which stays namespaced by markerId), the exact widening the ADR's threat model already names as a residue. The `harness doctor` finding's message now names the evidence honestly ("hook-written, unsigned self-report; confirm from your own launch settings before enabling auto_approve"), and `permission-mode-observations.ts`'s and `bypass-without-auto-approve.ts`'s module headers, plus `docs/policy-packs/understanding-before-execution.md`, point at the ADR paragraph instead of leaving the older "possible future hardening" framing standing. No behavior change: the observation was already unsigned and the finding was already advisory-only.
|
|
36
|
+
- **`docs/decisions/*.md` source citations re-pointed and anchored** (agent-tasks `6f719bb4`). Every citation in the ADRs now names a repo-relative path with an anchor verified against the current tree; several had drifted onto unrelated lines after later changes (an unrelated constant move, new slice-1/slice-3 code) moved the code they described. A "Citation convention" note at the top of `docs/decisions/2026-08-27-ug-auto-mode-approval.md` records the grammar and the guard test that enforces it going forward.
|
|
37
|
+
- **`claude -p` delegation (slice 3) is decided Claude Code only on the consuming side, and the platform boundary is now documented rather than left implicit** (agent-tasks `be9faf70`). Consumption only: the Codex adapter has no delegation consumer, so a Codex session cannot be the delegated child; a Codex session holding a validly signed marker can still issue a delegation for a Claude child today, an accepted consequence of the shared marker contract (see the ADR's Platform scope amendment). The Codex adapter already shares slice 2's `permission_mode`-based auto-approval, but carries no delegation-marker consumer; a Codex-shaped port would need its own measured child-report capture channel and delegation verifier, not a code port of the three Claude-side pieces. Recorded as a "Platform scope" amendment to `docs/decisions/2026-08-27-ug-auto-mode-approval.md` with a reopen criterion, gap 14 in `docs/okf/codex-adapter-parity-gaps.md`, and the boundary named in the pack doc, `docs/CLI.md`, and the `harness delegate` `--help` text.
|
|
38
|
+
|
|
39
|
+
### Fixed
|
|
40
|
+
|
|
41
|
+
- **An unclassified action no longer satisfies `risk.severity_at_least: critical` on its own** (agent-tasks `2929c5b7`). It still satisfies `low`/`medium`/`high` (`risk.category_in` and `action.reversible` keep the full "unknown is not safe" fallback unchanged), but the top tier now requires a real classification: `evaluateWhen` (`src/runtime/when-eval.ts`) treats an unclassified profile as sitting one rung below the top of the severity scale ("high" on the shipped four-value scale) for `severity_at_least` comparisons only. No manifest opt-in restores the old blanket behavior; see `docs/risk-gate.md`'s "Unclassified actions and the fail-close rule". Motivation, measured on a production-resolved session: six consecutive read-only investigation commands (`cat`, `sed -n`, `curl`, `sshpass ssh`, `node -e`, `git -C ... rev-list`) were hard-denied by the shipped `gate-prod-destructive` policy, because none of them were classified and the old blanket rule let each one satisfy `severity_at_least: critical` exactly as `rm -rf /` would.
|
|
42
|
+
- **A built-in DESTRUCTIVE floor ships in the binary, so existing installs keep the hard block.** `src/runtime/destructive-shell-floor.ts`, composed in `classifyRisk` under the same highest-severity-wins rule as the existing floors. `harness init` writes a manifest once and never rewrites it, so shipping the compensating classification only as new `dangerous-shell` template patterns would have left every EXISTING manifest with the loosened fallback and no replacement. The floor classifies `critical` for `dd` with an `of=` target, `truncate -s` (glued `-s0` included), `shred`, `mkfs`/`mkfs.*`, `find ... -delete`, `find ... -exec(dir) rm`; `high` for `git reset --hard`, `git push --force`/`--force-with-lease`/`-f`/`+refspec`, `git clean -f` (cluster-aware), `git checkout .`, `git checkout -- .`, `git restore .`, `chmod`/`chown -R` (cluster-aware), `sed -i`, and `curl` carrying a local-write flag (`-o`, `-O`, `-D`, `-c`, `-K`, `--config`, `--output`, `--trace*`, `--create-dirs`, ...) or a body/method flag (`-d`/`--data*`/`--json`/`-F`/`--form*`/`-T`, or `-X`/`--request` naming anything but GET/HEAD). Since curl has no read-only floor to fall back on, its write-capable surface is recognised here as far as this argv-text scan can see it: `-w`/`--write-out` whose format string carries curl 8.3.0's `%output{FILE}` directive also counts as a local write (a format string itself read from a FILE, `-w @fmt.txt`, is a documented gap this scan does not close), and a local file read into the outgoing request classifies `high` with `network_exfiltration` alone, which keeps that profile `reversible: true`: `-H`/`--header` with a value starting `@`, or `-b`/`--cookie` with a value containing no `=` (curl's own rule for `-b`, `@`-prefixed or not — fix round 5 corrected this scan's earlier `-b @FILE`-only model to match). `shred`/`mkfs` are matched on their head token alone and so gained one exemption: an invocation whose ONLY argument is `--help`/`--version`/`-h`/`-V`/`-v`, which cannot destroy anything (`shred -v secret.txt` keeps its `critical`). It resolves the real head through path prefixes (`/bin/dd`), `VAR=`/`env`/`command`/`sudo`/`nice`/`timeout`/`exec`/`nohup` wrappers (reusing `peelWrapperPrefixes`), `busybox`, and `sh -c "..."` / `bash -lc '...'`, none of which a raw-string regex can follow. An operator pattern can raise above it; nothing sinks below it.
|
|
43
|
+
- **The `dangerous-shell` template patterns are that floor's operator-editable mirror**, kept in `FULL_TEMPLATE` and `docs/examples/full-manifest.yaml` (lockstep guard: `tests/cli/init-full-template-parity.test.ts`), and tightened this round: `truncate` now matches the glued `-s0` form, `chmod`/`chown` is short-cluster-aware like `git clean`'s pattern, the `curl` body pattern covers `--json`/`-F`/`--form`/`--form-string` and any `-X`/`--request` method other than GET/HEAD in any letter case, and a third `curl` pattern covers the local-write flags (`-o`/`-O`/`-D`/`-c`/`-K` in a cluster, the long spellings, and `-w`/`--write-out` with `%output`). The mirror stays narrower than the floor for the `@FILE` value on `-H`/`-b`, whose `@` a raw-string pattern cannot tell from an `@` anywhere else in the line. `tests/runtime/destructive-shell-floor.test.ts` pins that every template pattern's canonical spelling is also caught by the built-in floor, at the same severity or higher.
|
|
44
|
+
- **The `sed` read-only floor is ALLOWLIST-based and Risk-Classifier-only; `curl` deliberately gets NO floor.** `isReadOnlySedCommand` sits beside `isReadOnlyKubectlCommand` in `src/runtime/read-only-bash.ts` and is wired only into `classifyRisk`, deliberately NOT into the shared `isReadOnlyBashCommand`: the understanding-gate PreToolUse blocker and the solution-acceptance write-guard consume that predicate directly and return "allowed" for anything it accepts before their own checks run, so a `sed` recognition there widens both gates. `sed` floors to `low` only when every flag is on the read-only allowlist (so `-i` in every spelling and `-f SCRIPTFILE` forfeit) and every script parses as a read-only sed program: sed's `w` command, the `s///w` flag and GNU's `e` command/flag all forfeit, correcting this task's earlier claim that "without `-i`, sed only writes to stdout". **`curl` has no read-only floor at all** (decision D-013): two shipped and both leaked, first a write-flag denylist that missed `-o`/`-O`/`-D`/`-c`/`-K`, then a flag allowlist that still admitted `-w`/`--write-out`, whose `%output{FILE}` directive writes a local file from curl 8.3.0 onward (verified on curl 8.7.1). The recurring class is the premise rather than the list: deciding a curl invocation is inert needs every curl flag's write capability across curl versions, and the value side has the same problem (`-H @FILE`). `curl` therefore stays UNCLASSIFIED like `ssh` and `node -e` (approval-gated by the fallback, never hard-blocked, never floored), and only its write-CAPABLE spellings are named, by the destructive floor. Operators who want a specific curl shape exempt add an explicit `low` classifier pattern for it, or a network egress control. A bare truncating redirection (`> file`) stays a documented, NOT-closed gap: the classifier is regex-over-raw-string with no redirection model, and a heuristic for it would over-match `2>&1`, heredocs and comparison operators.
|
|
45
|
+
- **`ssh <host> <cmd>`, `node -e`/`--eval` and `curl` are NOT given a `low` floor.** For `ssh` and `node -e` the payload (the remote command, the evaluated code) is opaque to the classifier; for `curl` it is the flag surface that cannot be allowlisted robustly (see the floor bullet above), so a `low` floor there would drop Risk Gate coverage entirely for those shapes: neither `severity_at_least: critical` nor `severity_at_least: high` fires on a `low` action. All three stay genuinely unclassified instead, so the paired `evaluateWhen` fallback above already covers them: not hard-blocked by a production `severity_at_least: critical` gate, but still approval-gated by a production `severity_at_least: high` gate.
|
|
46
|
+
- **The `ux:`-declared deny envelope now names a fallback-caused deny**, not just the neutral non-`ux:` one, and interpolates the resolved environment name, the fallback rung, and the policy's own declared threshold, so an unscoped `severity_at_least: high` policy on a feature branch no longer reports a production context that was never resolved or a critical-severity comparison that never ran. The operator's own `cannot:` text is appended byte-for-byte, and a genuine classification hit still renders it with no prefix.
|
|
47
|
+
- Docs: `docs/risk-gate.md` ("Unclassified actions and the fail-close rule", "Built-in read-only commands", the `sed` read-only floor, the new "No `curl` read-only floor, by design" section, and the built-in destructive floor section, which now also records the head-resolution spellings the floor does not reach and the fact that a floor classifies — retiring the unclassified fallback for `risk.category_in` and `action.reversible`, not just `risk.severity_at_least` — whether the floor's own category list is empty (the benign floors) or non-empty but narrower than a policy might assume (the destructive floor: `chmod -R` classifies `mass_update` only, a curl request body classifies `production_mutation`+`network_exfiltration` but never `destructive`).
|
|
48
|
+
|
|
49
|
+
## [0.53.0] - 2026-08-30
|
|
50
|
+
|
|
51
|
+
### Added
|
|
52
|
+
|
|
53
|
+
- **`harness gc` sweeps expired delegation markers and their orphaned adoption ledgers** (task `3ece079d`, follow-up from UG auto-mode slice 3, agent-tasks `37ad0b05`). `harness gc` grew a new `delegation` category alongside `report` / `parse-error` / `approval-marker`: a `harness.generated/.delegations/<sid>` marker is a candidate once its own signed `expires` binding (read via `parseDelegationApprovedBy`, no signature check needed for a retention decision) is older than the same retention window every other category uses, and its `harness.generated/.delegation-adoptions/<sid>` once-per-session adoption ledger is a candidate once that session's delegation has expired, or once the ledger itself (by its own mtime) is older than the retention window with no marker at all for its session; the ledger age gate came out of the pre-merge review (a brand-new orphan ledger had been listed under "older than 30d") and also closes the window between the two directory reads; a marker gc cannot parse is reported in `GcResult.unparseable` and never deleted, dry-run or `--apply`, and its ledger sibling is kept alongside it. `ADOPTION_LEDGER_DIRNAME` moved from `hook-pre-tool-use.ts` (its only prior writer) to `delegation-markers.ts`, next to `DELEGATION_MARKER_DIRNAME`, so the new sweep reads it without a cli-to-cli import; re-exported through the same `understanding-before-execution` shim every other delegation symbol already travels through. Docs: `docs/CLI.md`'s `gc` row and a new "Cleanup" paragraph in `docs/policy-packs/understanding-before-execution.md`'s delegation section.
|
|
54
|
+
|
|
55
|
+
### Fixed
|
|
56
|
+
|
|
57
|
+
- **`git-context.ts`: resolve branch refs from the common dir in a linked worktree**
|
|
58
|
+
(agent-tasks `498e86d3`). `resolveGitContext` read `<gitDir>/refs/heads/<branch>`
|
|
59
|
+
and `<gitDir>/packed-refs` straight from the per-worktree gitdir it found via
|
|
60
|
+
`findGitEntry`, but a `git worktree add` checkout's per-worktree gitdir does
|
|
61
|
+
not store `refs/heads/*` or `packed-refs` at all (its `refs/` tree is empty
|
|
62
|
+
apart from the per-worktree `refs/bisect` and `refs/worktree` namespaces);
|
|
63
|
+
the branch refs live in the shared common dir (named by that gitdir's own
|
|
64
|
+
`commondir` file). Every gate that shells out to the solution-acceptance push
|
|
65
|
+
or finish/merge check from a linked worktree with an attached branch therefore
|
|
66
|
+
reported "cannot resolve the current git HEAD" (a detached HEAD, whose sha
|
|
67
|
+
sits directly in `HEAD`, was unaffected). `resolveGitContext` now routes the
|
|
68
|
+
branch-ref lookup through the existing `resolveCommonDir` helper (already used
|
|
69
|
+
by `resolveOriginHeadBase`'s callers for the same reason), a no-op for the
|
|
70
|
+
main checkout since `resolveCommonDir` returns the gitdir unchanged when no
|
|
71
|
+
`commondir` file exists. `resolveOriginHeadBase` (the default-branch resolver)
|
|
72
|
+
already routed through `resolveCommonDir` at every call site; that path is
|
|
73
|
+
pinned with a new linked-worktree test rather than changed. Two other
|
|
74
|
+
consumers of the same unresolved branch/sha were equally broken in a linked
|
|
75
|
+
worktree and now work there too: the `head:<sha>` preflight token
|
|
76
|
+
(`src/cli/session-start/index.ts`) and the `at_head:true` requires-flag
|
|
77
|
+
(`src/runtime/intercept.ts`).
|
|
78
|
+
|
|
79
|
+
- **Understanding gate: `auto_approve` now ships active in every `harness init` template, and existing installs get an upgrade path and a doctor warning** (task `8f637efd`, D-004, `docs/decisions/2026-08-27-ug-auto-mode-approval.md`, "Amendment: install default"). Origin: observed 2026-08-29 on the Mac mini install (harness 0.51.0 global, `understanding-before-execution` pack active, session running under `bypassPermissions`): after the 4h `approval_lifecycle` expired, the gate required a fresh Understanding Report, `harness approve understanding` resolved via the manual `permissionDecision: ask` path, and the operator had to confirm a prompt by hand; `harness doctor` reported "auto approvals in the last 20 sessions: 0". Root cause: `~/.harness/harness.yaml` had never carried an `auto_approve` block, because no init template shipped one (`grep -rn auto_approve src/cli/init` returned nothing before this change), and there was no upgrade path for an install that predated the block.
|
|
80
|
+
- `FULL_TEMPLATE`, `SOLO_TEMPLATE`, and `TEAM_TEMPLATE` (`minimal` carries no `policy_packs` at all) now ship `auto_approve: { when: [bypassPermissions], harnesses: [claude-code], require_report: true }` active, rendered from one canonical source (`src/policy-packs/builtin/understanding-before-execution/auto-approve-default.ts`) so `harness init`, the new upgrade verb, and the new doctor finding cannot drift on the snippet.
|
|
81
|
+
- New verb `harness pack upgrade understanding-before-execution [--dry-run]` (`src/cli/pack/upgrade.ts`) inserts the same block into an existing manifest's `config:` mapping by text-level line insertion (never a full YAML parse-and-reserialize), so hand edits elsewhere in the file are untouched. Idempotent (a no-op, byte-identical, when `auto_approve:` already exists), and refuses rather than guesses when the pack block or its `config:` key cannot be located unambiguously.
|
|
82
|
+
- New `harness doctor` advisory (`src/cli/doctor/bypass-without-auto-approve.ts`): warns when `bypassPermissions` was observed for a session and `auto_approve` does not cover it. The observation is written by the hook process from the PreToolUse payload, not from a ledger row or report field the agent writes through its own API, under `harness.generated/.permission-mode-observations/` (`src/policy-packs/builtin/understanding-before-execution/permission-mode-observations.ts`), independent of whether `auto_approve` is configured. It is NOT signed (review round 3, F2): it is evidence the hook was invoked with a given payload, not proof a session actually ran under that mode, so an already-approved session (or anything else able to invoke the hook binary directly) can write, or forge, one; the finding stays advisory-only for exactly that reason and never gates or mints an approval.
|
|
83
|
+
- `docs/policy-packs/understanding-before-execution.md` and `docs/CLI.md` document the new default, verb, and finding as rules; this entry is the evidence record for the change.
|
|
84
|
+
- **Review round 2 fixes.** A shared `readJsonDirEntriesRejectingSymlinks` helper (`src/io/read-json-dir-entries.ts`) replaces the verbatim readdir-classify-parse loop `ug-auto-approvals.ts` and `permission-mode-observations.ts` had each grown independently. `harness init --interactive`'s Custom composer now also ships `auto_approve` (it had been the one template-shaped surface this default missed, since it builds a config object rather than YAML text). `harness pack upgrade understanding-before-execution` now accepts a double- or single-quoted `- name: "understanding-before-execution"` pack entry and preserves a CRLF manifest's line endings end to end instead of inserting bare-LF lines. The doctor finding's `harnesses` coverage leg (previously untested) now has a discriminating test. `harness gc` grew a fourth mtime-aged category, `permission-mode-observation`, sweeping `harness.generated/.permission-mode-observations/` the same way it already sweeps `.approvals/`.
|
|
85
|
+
- **Review round 3 fixes.** F1 (HIGH): `permissionModeObservationPathFor` had joined `session_id` into the write path unvalidated (trusting the caller's own empty-string check), so a crafted `session_id` reaching `harness pack hook pre-tool-use` on stdin (e.g. `../../escape-target/victim`) could write a file outside `harness.generated/.permission-mode-observations/`; it now calls the same `rejectMalformedSessionId` the approval marker path uses, and a rejected id is treated as another best-effort write failure (one stderr warning, fail-open, never a throw out of the hook). The read side (`listPermissionModeObservations`) now runs every returned `sessionId` through a `sanitizeForDisplay` step (mirrors `persisted-reports.ts`'s `sanitizeDetailValue`: strips C0/DEL control characters including ESC, caps length) before it can reach `harness doctor`'s finding `message`, since a hostile on-disk entry name or JSON `sessionId` field is not otherwise constrained to be display-safe. Mutation probe M1: dropping the `rejectMalformedSessionId` call from `permissionModeObservationPathFor` turns 5 tests red (`tests/policy-packs/understanding-before-execution-permission-mode-observations.test.ts`, `tests/cli/pack-hook-pre-tool-use-permission-mode-observation.test.ts`); restored, all green.
|
|
86
|
+
- **Review round 4 (verification) fixes.** The ADR amendment's one remaining "non-agent-writable" phrase was aligned with the unsigned/advisory wording used everywhere else; `observedAt` is display-sanitised alongside `sessionId` (in the reader and again at the finding's render boundary, mirroring `persisted-reports.ts`), because V8's date parser accepts parenthesised trailing text so a crafted timestamp passed validation and reached `harness doctor` raw; the hook's own stderr warning sanitises the echoed session id; the hook-level traversal test now pins the concrete verdict (`blocked === true`) instead of a type check; the duplication pin follows the measured 112; and the broadened `auto_approve:` presence match documents its block-scalar limit (fail-safe no-op).
|
|
87
|
+
- F2 (MEDIUM): corrected the "hook-written, not agent-writable" framing above and in `permission-mode-observations.ts`'s and `bypass-without-auto-approve.ts`'s module headers and `docs/policy-packs/understanding-before-execution.md`: the observation is unsigned, so it proves the hook was invoked with a payload, not that a session ran under a given mode; the finding stays advisory-only and never gates. Not signing the observation in this task was an orchestrator decision (D-013): a second signed artifact would widen the marker-signing surface the ADR already lists as a residue, and the finding is advisory. Signing the observation was subsequently decided against; see the unsigned-observation entry in this section and the ADR amendment.
|
|
88
|
+
- F3 (MEDIUM): `harness pack upgrade`'s `auto_approve:` presence check used strict line equality, so a hand-applied flow-style value (`auto_approve: {when: [...], ...}`) or a bare key with a trailing comment was not recognized as present; the verb inserted a second key and `parseYaml` threw "Map keys must be unique" uncaught (exit 70, a raw YAML message). The check now matches any line whose trimmed form starts with `auto_approve:`, and both `parseYaml` call sites (pre-lock and under the lock) are wrapped so a still-unparseable proposal surfaces as this verb's own `HarnessExitError` at `EX_FAIL` naming the pack, instead of an uncaught exception. Mutation probe M3: reverting the presence check to strict equality turns the two new flow-style/trailing-comment tests in `tests/cli/pack-upgrade.test.ts` red; restored, all green (22/22).
|
|
89
|
+
- F4 (LOW): `packUpgrade` reported `applied: true` unconditionally after the locked callback ran, even when the callback found the block already present (a concurrent writer had raced it) and returned without writing. The callback now sets a `wroteUnderLock` flag only when it actually calls `atomicWriteFile`, and the result reports `applied: false, alreadyPresent: true` on the raced path. Covered by a real (unmocked) concurrency test in `tests/cli/pack-upgrade.test.ts`: two `packUpgrade` calls on the same fresh manifest via `Promise.all`, asserting exactly one reports `applied: true` and the manifest ends up with exactly one `auto_approve:` key.
|
|
90
|
+
- F5 (LOW): dropped "task 8f637efd" from the `harness pack upgrade --description` help string and the pack doc's section heading; kept in source comments and here.
|
|
91
|
+
|
|
10
92
|
## [0.52.0] - 2026-08-28
|
|
11
93
|
|
|
12
94
|
### Added
|
|
@@ -17,7 +17,7 @@ export interface IssueDelegationOptions extends LoaderOptions {
|
|
|
17
17
|
taskId?: string;
|
|
18
18
|
/** Delegation lifetime in seconds (already parsed, e.g. via `parseDurationSeconds`). Default: the applied pack's `approval_lifecycle.max_age` when set, else {@link DEFAULT_DELEGATION_TTL_SECONDS}. */
|
|
19
19
|
ttlSeconds?: number;
|
|
20
|
-
/** Fallback shape: path to the launcher-supplied Understanding Report file
|
|
20
|
+
/** Fallback shape: path to the launcher-supplied Understanding Report file. Copied to the conventional `harness.generated/.delegation-reports/<child-sid>.md` location (mode 0600) and bound by content AND that conventional path's hash; the child's PreToolUse hook reads it back from there. */
|
|
21
21
|
reportPath?: string;
|
|
22
22
|
/** Explicit parent session id. Resolved exactly like `harness approve understanding` (flag > env > `.pending-approval`; no newest-report fallback) when omitted. */
|
|
23
23
|
parentSessionId?: string;
|
|
@@ -35,7 +35,7 @@ export interface IssueDelegationOptions extends LoaderOptions {
|
|
|
35
35
|
reason: string;
|
|
36
36
|
}>;
|
|
37
37
|
}
|
|
38
|
-
export type IssueDelegationRefusalReason = "invalid-child-session" | "no-binding" | "invalid-cwd" | "invalid-task" | "invalid-ttl" | "ttl-above-max-age" | "parent-session-unresolved" | "parent-marker-missing" | "parent-marker-forged" | "parent-marker-expired" | "signing-key-absent" | "report-unreadable" | "invalid-input" | "write-failed";
|
|
38
|
+
export type IssueDelegationRefusalReason = "invalid-child-session" | "no-binding" | "invalid-cwd" | "invalid-task" | "invalid-ttl" | "ttl-above-max-age" | "parent-session-unresolved" | "parent-marker-missing" | "parent-marker-forged" | "parent-marker-expired" | "signing-key-absent" | "report-unreadable" | "report-unparseable" | "report-conflict" | "invalid-input" | "write-failed";
|
|
39
39
|
export type IssueDelegationResult = {
|
|
40
40
|
ok: true;
|
|
41
41
|
filePath: string;
|
|
@@ -59,5 +59,18 @@ export declare function delegationLedgerFactFor(childSessionId: string, parentSe
|
|
|
59
59
|
* calls `process.exit`; every failure mode is a `{ ok: false }` result
|
|
60
60
|
* with a distinct `reason` (ADR slice plan format rule: every refusal is
|
|
61
61
|
* a block with a distinct diagnostic, never a fall-through to allow).
|
|
62
|
+
*
|
|
63
|
+
* Consumption is Claude Code only: the Codex adapter has no delegation
|
|
64
|
+
* consumer (`hook-codex-pre-tool-use.ts` reads neither `.delegations/` nor
|
|
65
|
+
* a child report through this mechanism), so a Codex session can never be
|
|
66
|
+
* the delegated CHILD of this verb. Issuing is a separate question and is
|
|
67
|
+
* NOT restricted the same way: the parent-session resolution below reads
|
|
68
|
+
* `$CODEX_SESSION_ID` like any other tier, and the parent-marker check
|
|
69
|
+
* that follows accepts any validly signed marker regardless of which
|
|
70
|
+
* hook minted it, so a Codex session holding one can be the delegating
|
|
71
|
+
* PARENT of a Claude `-p` child today. Both are a decided platform
|
|
72
|
+
* boundary, not an oversight, see the "Platform scope" amendment in
|
|
73
|
+
* `docs/decisions/2026-08-27-ug-auto-mode-approval.md` and gap 14 in
|
|
74
|
+
* `docs/okf/codex-adapter-parity-gaps.md`.
|
|
62
75
|
*/
|
|
63
76
|
export declare function issueDelegation(opts: IssueDelegationOptions): Promise<IssueDelegationResult>;
|
|
@@ -41,12 +41,30 @@
|
|
|
41
41
|
// REPORT FALLBACK (`--report <path>`): binds the launcher-supplied
|
|
42
42
|
// report by BOTH its content (`reportContentHash`) and its path
|
|
43
43
|
// (`reportPathHash`, via the same `hashDelegationCwd` the cwd binding
|
|
44
|
-
// uses, so both sides of the delegation hash paths the same way).
|
|
44
|
+
// uses, so both sides of the delegation hash paths the same way). The
|
|
45
|
+
// path bound is NOT the operator's original `--report` argument: this
|
|
46
|
+
// verb COPIES the file (mode 0600) to the conventional location
|
|
47
|
+
// `delegationReportPathFor` derives from the child session id
|
|
48
|
+
// (`harness.generated/.delegation-reports/<child-sid>.md`) and hashes
|
|
49
|
+
// THAT path instead. The child's PreToolUse hook has no channel to learn
|
|
50
|
+
// an arbitrary operator-chosen path, so binding the original path would
|
|
51
|
+
// leave the hook unable to derive it, exactly the gap agent-tasks
|
|
52
|
+
// 49d1ee41 closes; the conventional path is the one location both the
|
|
53
|
+
// writer here and the hook can compute from nothing but a session id.
|
|
54
|
+
// The copy step never silently overwrites a DIFFERENT file already
|
|
55
|
+
// staged there (see `report-conflict` below): the conventional location
|
|
56
|
+
// is per-child, not per-delegation, so re-delegating the same child with
|
|
57
|
+
// a changed report is a caller error to surface loudly, not a quiet
|
|
58
|
+
// clobber of whatever the hook may already be mid-verification against.
|
|
59
|
+
import * as fs from "node:fs";
|
|
60
|
+
import { parseReport } from "@lannguyensi/understanding-gate";
|
|
45
61
|
import { sha256Hex, signingKeyExists, signingKeyPathFor } from "../../runtime/approval-signing.js";
|
|
62
|
+
import { atomicWriteFile } from "../../io/atomic-write.js";
|
|
46
63
|
import { resolveGeneratedDir } from "../../runtime/pending-approval.js";
|
|
47
64
|
import { resolveApprovalSessionId } from "../../runtime/session-id.js";
|
|
48
|
-
import { checkApprovalMarker, hashDelegationCwd, parseApprovalLifecycle, writeDelegationMarker, } from "../../policy-packs/builtin/understanding-before-execution-runtime.js";
|
|
65
|
+
import { checkApprovalMarker, delegationReportPathFor, hashDelegationCwd, parseApprovalLifecycle, writeDelegationMarker, } from "../../policy-packs/builtin/understanding-before-execution-runtime.js";
|
|
49
66
|
import { readRegularFileRejectingSymlink } from "../../io/read-regular-file.js";
|
|
67
|
+
import { resolveMode, toPackageMode } from "../../policy-packs/builtin/understanding-before-execution.js";
|
|
50
68
|
import { loadDeclaredUnderstandingPack, writeLedgerTag } from "../approve/understanding.js";
|
|
51
69
|
import { resolvePaths } from "../loader.js";
|
|
52
70
|
/** Default delegation lifetime when neither `--ttl` nor the pack's `approval_lifecycle.max_age` is set (ADR "TTL, cwd, and subagents"). */
|
|
@@ -97,6 +115,19 @@ function validateTaskId(taskId) {
|
|
|
97
115
|
* calls `process.exit`; every failure mode is a `{ ok: false }` result
|
|
98
116
|
* with a distinct `reason` (ADR slice plan format rule: every refusal is
|
|
99
117
|
* a block with a distinct diagnostic, never a fall-through to allow).
|
|
118
|
+
*
|
|
119
|
+
* Consumption is Claude Code only: the Codex adapter has no delegation
|
|
120
|
+
* consumer (`hook-codex-pre-tool-use.ts` reads neither `.delegations/` nor
|
|
121
|
+
* a child report through this mechanism), so a Codex session can never be
|
|
122
|
+
* the delegated CHILD of this verb. Issuing is a separate question and is
|
|
123
|
+
* NOT restricted the same way: the parent-session resolution below reads
|
|
124
|
+
* `$CODEX_SESSION_ID` like any other tier, and the parent-marker check
|
|
125
|
+
* that follows accepts any validly signed marker regardless of which
|
|
126
|
+
* hook minted it, so a Codex session holding one can be the delegating
|
|
127
|
+
* PARENT of a Claude `-p` child today. Both are a decided platform
|
|
128
|
+
* boundary, not an oversight, see the "Platform scope" amendment in
|
|
129
|
+
* `docs/decisions/2026-08-27-ug-auto-mode-approval.md` and gap 14 in
|
|
130
|
+
* `docs/okf/codex-adapter-parity-gaps.md`.
|
|
100
131
|
*/
|
|
101
132
|
export async function issueDelegation(opts) {
|
|
102
133
|
if (!UUID_RE.test(opts.childSessionId)) {
|
|
@@ -235,7 +266,9 @@ export async function issueDelegation(opts) {
|
|
|
235
266
|
const expiresAt = new Date(now.getTime() + ttlSeconds * 1_000).toISOString();
|
|
236
267
|
// Fallback shape: bind the launcher-supplied report by BOTH its
|
|
237
268
|
// content and its path (delegation-markers.ts, "Delegation marker
|
|
238
|
-
// shape" table: half a binding is worse than none).
|
|
269
|
+
// shape" table: half a binding is worse than none). The path bound is
|
|
270
|
+
// the CONVENTIONAL copy this verb writes, not the operator's original
|
|
271
|
+
// `--report` argument (module header, "REPORT FALLBACK").
|
|
239
272
|
let reportPathHash;
|
|
240
273
|
let reportContentHash;
|
|
241
274
|
if (opts.reportPath !== undefined) {
|
|
@@ -248,7 +281,98 @@ export async function issueDelegation(opts) {
|
|
|
248
281
|
};
|
|
249
282
|
}
|
|
250
283
|
reportContentHash = sha256Hex(read.content);
|
|
251
|
-
|
|
284
|
+
// Validate parseability NOW, with the SAME parser AND the SAME
|
|
285
|
+
// gap-fill mode `persistStdinReport` applies when the child's hook
|
|
286
|
+
// later tries to mint from this report (`stdin-report.ts`'s own
|
|
287
|
+
// `parseReport` call, `mode: toPackageMode(resolveMode(declared).mode)`
|
|
288
|
+
// at hook-pre-tool-use.ts). A hardcoded `"fast_confirm"` here would
|
|
289
|
+
// let a short-form report pass staging and then fail persist under
|
|
290
|
+
// an operator's actually-configured `grill_me`, blocking the child
|
|
291
|
+
// with no retry available (see below). Refusing an unparseable
|
|
292
|
+
// `--report` file here, before anything is signed or staged, matters
|
|
293
|
+
// because a failure caught only at mint time is not cheaply
|
|
294
|
+
// retryable: the adoption ledger records the report's content hash
|
|
295
|
+
// as spent BEFORE `persistStdinReport` even runs, so a bad report
|
|
296
|
+
// permanently spends that content hash, and this verb's own
|
|
297
|
+
// `report-conflict` refusal then blocks re-staging different content
|
|
298
|
+
// at the same conventional path until the file is removed by hand.
|
|
299
|
+
// Catching it here costs nothing and needs no manual cleanup.
|
|
300
|
+
const parsed = parseReport(read.content, {
|
|
301
|
+
taskId: opts.childSessionId,
|
|
302
|
+
createdAt: now.toISOString(),
|
|
303
|
+
mode: toPackageMode(resolveMode(declaredPack).mode),
|
|
304
|
+
riskLevel: "medium",
|
|
305
|
+
});
|
|
306
|
+
if (!parsed.ok) {
|
|
307
|
+
const summary = parsed.error.message.length > 0
|
|
308
|
+
? parsed.error.message
|
|
309
|
+
: `${parsed.error.reason}${parsed.error.missing.length > 0 ? ` (missing: ${parsed.error.missing.join(", ")})` : ""}`;
|
|
310
|
+
return {
|
|
311
|
+
ok: false,
|
|
312
|
+
reason: "report-unparseable",
|
|
313
|
+
detail: `--report ${opts.reportPath} did not parse: ${summary}`,
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
// Stage the conventional copy the child's hook will read back from.
|
|
317
|
+
// Never a silent overwrite: a DIFFERENT file already staged there
|
|
318
|
+
// (a stale copy from an earlier `--report` targeting the same
|
|
319
|
+
// child session id, or anything else) is a refusal, not a clobber.
|
|
320
|
+
// An IDENTICAL file already staged there is left as-is (idempotent
|
|
321
|
+
// re-delegation of the same child with the same report).
|
|
322
|
+
const conventionalPath = delegationReportPathFor(generatedDir, opts.childSessionId);
|
|
323
|
+
const existingAtConventional = readRegularFileRejectingSymlink(conventionalPath);
|
|
324
|
+
if (existingAtConventional.kind === "symlink" || existingAtConventional.kind === "not-regular") {
|
|
325
|
+
return {
|
|
326
|
+
ok: false,
|
|
327
|
+
reason: "report-conflict",
|
|
328
|
+
detail: `${conventionalPath} exists and is not a plain file; refusing to write the launcher-supplied report through it`,
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
if (existingAtConventional.kind === "unreadable") {
|
|
332
|
+
return {
|
|
333
|
+
ok: false,
|
|
334
|
+
reason: "report-conflict",
|
|
335
|
+
detail: `${conventionalPath} exists but could not be read (I/O error); refusing to overwrite it without first confirming its content matches`,
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
if (existingAtConventional.kind === "ok" && existingAtConventional.content !== read.content) {
|
|
339
|
+
return {
|
|
340
|
+
ok: false,
|
|
341
|
+
reason: "report-conflict",
|
|
342
|
+
detail: `a different report is already staged at ${conventionalPath} for child session ${opts.childSessionId}; refusing to overwrite it (no silent overwrite). Remove it first if the new --report content is intentional.`,
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
if (existingAtConventional.kind === "missing") {
|
|
346
|
+
try {
|
|
347
|
+
atomicWriteFile(conventionalPath, read.content, { mode: 0o600 });
|
|
348
|
+
}
|
|
349
|
+
catch (err) {
|
|
350
|
+
return {
|
|
351
|
+
ok: false,
|
|
352
|
+
reason: "write-failed",
|
|
353
|
+
detail: `could not stage the launcher-supplied report at ${conventionalPath}: ${err instanceof Error ? err.message : String(err)}`,
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
else {
|
|
358
|
+
// `existingAtConventional.kind === "ok"` with matching content:
|
|
359
|
+
// already staged, nothing to WRITE, but the pack doc promises mode
|
|
360
|
+
// 0600 for the conventional copy unconditionally, not only on
|
|
361
|
+
// first write, so a pre-existing identical file left at a looser
|
|
362
|
+
// mode by something other than this verb is still brought to 0600
|
|
363
|
+
// on restage.
|
|
364
|
+
try {
|
|
365
|
+
fs.chmodSync(conventionalPath, 0o600);
|
|
366
|
+
}
|
|
367
|
+
catch (err) {
|
|
368
|
+
return {
|
|
369
|
+
ok: false,
|
|
370
|
+
reason: "write-failed",
|
|
371
|
+
detail: `could not set mode 0600 on the already-staged report at ${conventionalPath}: ${err instanceof Error ? err.message : String(err)}`,
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
reportPathHash = hashDelegationCwd(conventionalPath);
|
|
252
376
|
}
|
|
253
377
|
const cwdHash = opts.cwd !== undefined ? hashDelegationCwd(opts.cwd) : null;
|
|
254
378
|
const taskId = opts.taskId !== undefined ? opts.taskId : null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/cli/delegate/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/cli/delegate/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { Manifest } from "../../schema/index.js";
|
|
2
|
+
export interface BypassWithoutAutoApproveFinding {
|
|
3
|
+
/** The newest observed session that tripped this finding. */
|
|
4
|
+
sessionId: string;
|
|
5
|
+
/** That observation's `observedAt` (ISO). */
|
|
6
|
+
observedAt: string;
|
|
7
|
+
/** Rendered as the `⚠` line itself. */
|
|
8
|
+
message: string;
|
|
9
|
+
/**
|
|
10
|
+
* Indented detail lines rendered directly under {@link message},
|
|
11
|
+
* mirroring the other advisory checks' idiom. Ends with the exact
|
|
12
|
+
* `auto_approve` snippet (the same one `harness init` ships and
|
|
13
|
+
* `harness pack upgrade understanding-before-execution` inserts, see
|
|
14
|
+
* auto-approve-default.ts) and names the upgrade command.
|
|
15
|
+
*/
|
|
16
|
+
detail: string[];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Pure(-ish): manifest + `generatedDir` in, a finding out (or
|
|
20
|
+
* `undefined` when there is nothing to flag). Only fires when:
|
|
21
|
+
* - the `understanding-before-execution` pack is declared AND enabled
|
|
22
|
+
* (mirrors every sibling advisory's gate: a pack that never runs
|
|
23
|
+
* has no auto-approve path to be missing);
|
|
24
|
+
* - at least one of the newest `recentSessions` hook-written
|
|
25
|
+
* observations records `permissionMode === "bypassPermissions"`;
|
|
26
|
+
* - `auto_approve` does NOT cover that combination today: either the
|
|
27
|
+
* block is absent/malformed (`parseAutoApprove` returns `null`), or
|
|
28
|
+
* `bypassPermissions` is not in `when`, or `claude-code` is not in
|
|
29
|
+
* `harnesses` (an operator who opted Codex in alone, dropping
|
|
30
|
+
* Claude Code, still has this exact gap on the Claude Code side).
|
|
31
|
+
*
|
|
32
|
+
* Reports the NEWEST qualifying observation only: this is a "you have
|
|
33
|
+
* at least one gap" flag, not a per-session audit; `ugAutoApprovals`
|
|
34
|
+
* already owns the full listing idiom for that.
|
|
35
|
+
*/
|
|
36
|
+
export declare function checkBypassWithoutAutoApprove(manifest: Manifest, generatedDir: string, opts: {
|
|
37
|
+
recentSessions: number;
|
|
38
|
+
}): BypassWithoutAutoApproveFinding | undefined;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// Doctor finding for the Mac-mini incident shape (task 8f637efd,
|
|
2
|
+
// docs/decisions/2026-08-27-ug-auto-mode-approval.md, "Amendment: install
|
|
3
|
+
// default"): a session ran under `bypassPermissions` (observed on the
|
|
4
|
+
// PreToolUse payload by the hook itself, see
|
|
5
|
+
// permission-mode-observations.ts) but `auto_approve` is missing, or
|
|
6
|
+
// present without covering `bypassPermissions` for `claude-code`. In
|
|
7
|
+
// that shape every gated call in the session falls through to the manual
|
|
8
|
+
// `harness approve understanding` prompt, which is exactly the gap the
|
|
9
|
+
// operator hit: the pack expects an unattended session under
|
|
10
|
+
// `bypassPermissions` to have opted in, and nothing said otherwise.
|
|
11
|
+
//
|
|
12
|
+
// This check reads ONLY the hook-written observation
|
|
13
|
+
// (`.permission-mode-observations/`), never the ledger and never the
|
|
14
|
+
// persisted report: both are agent-writable, and the evidence-ledger
|
|
15
|
+
// trust boundary this pack already enforces for approval
|
|
16
|
+
// (docs/okf/evidence-ledger-trust-boundary.md) applies to a doctor
|
|
17
|
+
// finding just as much as to a gate decision: a fact the AGENT can write
|
|
18
|
+
// must not be what tells the operator "you meant to opt in and didn't".
|
|
19
|
+
//
|
|
20
|
+
// Deliberately advisory (⚠), never an error: an operator who runs
|
|
21
|
+
// `bypassPermissions` sessions on the manual approval path on purpose
|
|
22
|
+
// (never wanting unattended auto-approval) is not misconfigured.
|
|
23
|
+
//
|
|
24
|
+
// THE OBSERVATION IS UNSIGNED (review round 3, F2). "Never the ledger,
|
|
25
|
+
// never the persisted report" above is about the WRITE PATH (this
|
|
26
|
+
// finding does not trust an agent-writable ledger row or report field),
|
|
27
|
+
// not a claim that the observation itself is tamper-proof: unlike the
|
|
28
|
+
// approval marker, `permission-mode-observations.ts`'s record carries no
|
|
29
|
+
// signature, so it is evidence that `harness pack hook pre-tool-use` was
|
|
30
|
+
// invoked with a given `session_id`/`permission_mode` on its stdin, not
|
|
31
|
+
// proof that a particular session actually ran under that mode: an
|
|
32
|
+
// already-approved session (or anything else able to invoke the hook
|
|
33
|
+
// binary directly) can write, or forge, one. That is exactly why this
|
|
34
|
+
// finding stays advisory-only and never feeds a gate or an approval: the
|
|
35
|
+
// operator who reads it decides what it means. Signing the observation
|
|
36
|
+
// was considered and rejected: a signature would only prove the hook
|
|
37
|
+
// signed the record, not that the named session actually ran under that
|
|
38
|
+
// mode, and it would open a second, unconditional signing surface for
|
|
39
|
+
// the marker-signing key (not a path to mint an approval marker, which
|
|
40
|
+
// stays namespaced by markerId). See
|
|
41
|
+
// docs/decisions/2026-08-27-ug-auto-mode-approval.md's "Amendment:
|
|
42
|
+
// permission-mode observation stays unsigned" for the full reasoning and
|
|
43
|
+
// the reopen criterion.
|
|
44
|
+
import { CLAUDE_CODE_HARNESS, harnessAllowed, listPermissionModeObservations, parseAutoApprove, permissionModeAllowed, renderAutoApproveSnippet, sanitizeForDisplay, } from "../../policy-packs/builtin/understanding-before-execution-runtime.js";
|
|
45
|
+
import { findEnabledUnderstandingPack } from "./understanding-mode-env.js";
|
|
46
|
+
/** The one payload literal this finding cares about (T1, see the ADR's signal table). */
|
|
47
|
+
const BYPASS_MODE = "bypassPermissions";
|
|
48
|
+
/**
|
|
49
|
+
* Pure(-ish): manifest + `generatedDir` in, a finding out (or
|
|
50
|
+
* `undefined` when there is nothing to flag). Only fires when:
|
|
51
|
+
* - the `understanding-before-execution` pack is declared AND enabled
|
|
52
|
+
* (mirrors every sibling advisory's gate: a pack that never runs
|
|
53
|
+
* has no auto-approve path to be missing);
|
|
54
|
+
* - at least one of the newest `recentSessions` hook-written
|
|
55
|
+
* observations records `permissionMode === "bypassPermissions"`;
|
|
56
|
+
* - `auto_approve` does NOT cover that combination today: either the
|
|
57
|
+
* block is absent/malformed (`parseAutoApprove` returns `null`), or
|
|
58
|
+
* `bypassPermissions` is not in `when`, or `claude-code` is not in
|
|
59
|
+
* `harnesses` (an operator who opted Codex in alone, dropping
|
|
60
|
+
* Claude Code, still has this exact gap on the Claude Code side).
|
|
61
|
+
*
|
|
62
|
+
* Reports the NEWEST qualifying observation only: this is a "you have
|
|
63
|
+
* at least one gap" flag, not a per-session audit; `ugAutoApprovals`
|
|
64
|
+
* already owns the full listing idiom for that.
|
|
65
|
+
*/
|
|
66
|
+
export function checkBypassWithoutAutoApprove(manifest, generatedDir, opts) {
|
|
67
|
+
const pack = findEnabledUnderstandingPack(manifest);
|
|
68
|
+
if (!pack)
|
|
69
|
+
return undefined;
|
|
70
|
+
const observations = listPermissionModeObservations(generatedDir, {
|
|
71
|
+
windowSize: opts.recentSessions,
|
|
72
|
+
});
|
|
73
|
+
const bypassObserved = observations.entries.find((e) => e.permissionMode === BYPASS_MODE);
|
|
74
|
+
if (!bypassObserved)
|
|
75
|
+
return undefined;
|
|
76
|
+
const autoApprove = parseAutoApprove(pack.config["auto_approve"]);
|
|
77
|
+
const covered = permissionModeAllowed(autoApprove, BYPASS_MODE) &&
|
|
78
|
+
harnessAllowed(autoApprove, CLAUDE_CODE_HARNESS);
|
|
79
|
+
if (covered)
|
|
80
|
+
return undefined;
|
|
81
|
+
// Sanitise at the render boundary as well, not only in the reader
|
|
82
|
+
// (mirrors persisted-reports.ts): both fields are payload-controlled.
|
|
83
|
+
const sessionId = sanitizeForDisplay(bypassObserved.sessionId);
|
|
84
|
+
const observedAt = sanitizeForDisplay(bypassObserved.observedAt);
|
|
85
|
+
return {
|
|
86
|
+
sessionId,
|
|
87
|
+
observedAt,
|
|
88
|
+
message: `bypassPermissions observed for session ${sessionId} (${observedAt}) but auto_approve does not cover it (hook-written, unsigned self-report; confirm from your own launch settings before enabling auto_approve); every gated call in that session needed a manual approval`,
|
|
89
|
+
detail: [
|
|
90
|
+
"run `harness pack upgrade understanding-before-execution` to insert the block below, or add it by hand under policy_packs[understanding-before-execution].config:",
|
|
91
|
+
...renderAutoApproveSnippet(0).split("\n"),
|
|
92
|
+
],
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=bypass-without-auto-approve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bypass-without-auto-approve.js","sourceRoot":"","sources":["../../../src/cli/doctor/bypass-without-auto-approve.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,0EAA0E;AAC1E,sEAAsE;AACtE,6CAA6C;AAC7C,qEAAqE;AACrE,qEAAqE;AACrE,yEAAyE;AACzE,uEAAuE;AACvE,6DAA6D;AAC7D,oEAAoE;AACpE,EAAE;AACF,qDAAqD;AACrD,qEAAqE;AACrE,qEAAqE;AACrE,yDAAyD;AACzD,mEAAmE;AACnE,yEAAyE;AACzE,wEAAwE;AACxE,EAAE;AACF,kEAAkE;AAClE,sEAAsE;AACtE,iEAAiE;AACjE,EAAE;AACF,uEAAuE;AACvE,kEAAkE;AAClE,wEAAwE;AACxE,sEAAsE;AACtE,yEAAyE;AACzE,yEAAyE;AACzE,wEAAwE;AACxE,mEAAmE;AACnE,qEAAqE;AACrE,sEAAsE;AACtE,yEAAyE;AACzE,uEAAuE;AACvE,qEAAqE;AACrE,wEAAwE;AACxE,sEAAsE;AACtE,uEAAuE;AACvE,qCAAqC;AACrC,mEAAmE;AACnE,yEAAyE;AACzE,wBAAwB;AAExB,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,8BAA8B,EAC9B,gBAAgB,EAChB,qBAAqB,EACrB,wBAAwB,EACxB,kBAAkB,GACnB,MAAM,sEAAsE,CAAC;AAE9E,OAAO,EAAE,4BAA4B,EAAE,MAAM,6BAA6B,CAAC;AAE3E,yFAAyF;AACzF,MAAM,WAAW,GAAG,mBAAmB,CAAC;AAmBxC;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,6BAA6B,CAC3C,QAAkB,EAClB,YAAoB,EACpB,IAAgC;IAEhC,MAAM,IAAI,GAAG,4BAA4B,CAAC,QAAQ,CAAC,CAAC;IACpD,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC;IAE5B,MAAM,YAAY,GAAG,8BAA8B,CAAC,YAAY,EAAE;QAChE,UAAU,EAAE,IAAI,CAAC,cAAc;KAChC,CAAC,CAAC;IACH,MAAM,cAAc,GAAG,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc,KAAK,WAAW,CAAC,CAAC;IAC1F,IAAI,CAAC,cAAc;QAAE,OAAO,SAAS,CAAC;IAEtC,MAAM,WAAW,GAAG,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC;IAClE,MAAM,OAAO,GACX,qBAAqB,CAAC,WAAW,EAAE,WAAW,CAAC;QAC/C,cAAc,CAAC,WAAW,EAAE,mBAAmB,CAAC,CAAC;IACnD,IAAI,OAAO;QAAE,OAAO,SAAS,CAAC;IAE9B,kEAAkE;IAClE,sEAAsE;IACtE,MAAM,SAAS,GAAG,kBAAkB,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;IAC/D,MAAM,UAAU,GAAG,kBAAkB,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;IACjE,OAAO;QACL,SAAS;QACT,UAAU;QACV,OAAO,EAAE,0CAA0C,SAAS,KAAK,UAAU,0MAA0M;QACrR,MAAM,EAAE;YACN,mKAAmK;YACnK,GAAG,wBAAwB,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC;SAC3C;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -61,6 +61,7 @@ function formatEnvironmentSection(report) {
|
|
|
61
61
|
const modeEnv = report.understandingModeEnv;
|
|
62
62
|
const ugAuto = report.ugAutoApprovals;
|
|
63
63
|
const autoApproveMode = report.ugAutoApproveMode;
|
|
64
|
+
const bypassWithoutAutoApprove = report.ugBypassWithoutAutoApprove;
|
|
64
65
|
// "nothing when `.approvals/` is absent" (ug-auto-approvals.ts's AC 1):
|
|
65
66
|
// stay silent unless the directory actually exists, mirroring the rest
|
|
66
67
|
// of this section's "no line for a check that found nothing" style.
|
|
@@ -78,6 +79,7 @@ function formatEnvironmentSection(report) {
|
|
|
78
79
|
if ((!bin || bin.status !== "warn") &&
|
|
79
80
|
!modeEnv &&
|
|
80
81
|
!autoApproveMode &&
|
|
82
|
+
!bypassWithoutAutoApprove &&
|
|
81
83
|
!showUgAuto &&
|
|
82
84
|
!showUgDeleg &&
|
|
83
85
|
!hasDriftContent &&
|
|
@@ -97,6 +99,11 @@ function formatEnvironmentSection(report) {
|
|
|
97
99
|
for (const line of autoApproveMode.detail)
|
|
98
100
|
out.push(` ${line}`);
|
|
99
101
|
}
|
|
102
|
+
if (bypassWithoutAutoApprove) {
|
|
103
|
+
out.push(` ⚠ ${bypassWithoutAutoApprove.message}`);
|
|
104
|
+
for (const line of bypassWithoutAutoApprove.detail)
|
|
105
|
+
out.push(` ${line}`);
|
|
106
|
+
}
|
|
100
107
|
if (showUgAuto && ugAuto) {
|
|
101
108
|
const modeParts = Object.keys(ugAuto.byMode)
|
|
102
109
|
.sort()
|