Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
b0fc9df
docs: design generation-10 audit remediation
Wibias Aug 24, 2026
d6c2b33
docs: plan generation-10 audit remediation
Wibias Aug 24, 2026
78f045b
test: add generation-10 audit regressions
Wibias Aug 24, 2026
9408549
fix: require complete probe trigger evidence
Wibias Aug 24, 2026
20f4829
fix: harden final merge conversation safety
Wibias Aug 24, 2026
64db20d
fix: separate Off mode from trusted authority
Wibias Aug 24, 2026
4e3090d
chore: add one-shot audit remediation patcher
Wibias Aug 24, 2026
56b3756
chore: run one-shot audit remediation patch
Wibias Aug 24, 2026
37fcfb8
fix: close audit routing and boundary gaps
github-actions[bot] Aug 24, 2026
bf51218
fix: add behavioural transcript attestation trust model
Wibias Aug 24, 2026
3cf11da
fix: distinguish behavioural integrity from trusted provenance
Wibias Aug 24, 2026
5e0c576
fix: require attested behavioural evidence for gating
Wibias Aug 24, 2026
34b145d
fix: preserve exact-text confirmation in Off mode
Wibias Aug 24, 2026
bdbaa0f
test: cover Off exact text and behavioural attestation
Wibias Aug 24, 2026
b3f4d68
chore: remove one-shot audit patch workflow
Wibias Aug 24, 2026
a5f41ff
chore: remove one-shot audit patch helper
Wibias Aug 24, 2026
8741a72
docs: distinguish behavioural integrity from trusted provenance
Wibias Aug 24, 2026
5681bea
test: align classic branch pattern expectation with GitHub
Wibias Aug 24, 2026
0afccd6
test: expose off-mode self-attested intent gap
Wibias Aug 24, 2026
5f19558
fix: bind off-mode intent to workflow context
Wibias Aug 24, 2026
7ffae78
test: bind off-mode merge intent to workflow context
Wibias Aug 24, 2026
fd38e45
fix: pass merge intent as trusted workflow context
Wibias Aug 24, 2026
4f8ff12
docs: clarify off-mode intent provenance
Wibias Aug 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 151 additions & 0 deletions docs/superpowers/plans/2026-08-24-audit-generation-10-remediation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
# Audit Generation 10 Remediation Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Close every generation-10 audit finding while preserving the hard contract that `authorityMode=off` never invokes Windows Hello.

**Architecture:** Keep mutation-mode routing and OS-backed trusted authority as separate layers. Off mode explicitly disables the Authority-host layer and must not pretend raw request booleans are trusted provenance; high-assurance/all remain unchanged. Close the remaining false-green and static-analysis gaps with fail-closed validators and focused regressions.

**Tech Stack:** Node.js ES modules, node:test, GitHub CLI/GitHub Actions, C#/.NET Windows Authority host, Markdown policy/docs.

**Spec:** `docs/superpowers/specs/2026-08-24-audit-generation-10-remediation-design.md`

## Global Constraints

- `authorityMode=off` must never invoke Windows Hello or require an Authority-host acceptance.
- `high-assurance` and `all` retain scoped trusted-authority enforcement.
- Do not broaden mutation profiles, bypass expected-head checks, stack gates, ownership checks, idempotency, review-thread policy, or publication verification.
- Every finding gets a focused RED regression before the production fix.
- Final acceptance requires canonical `npm run check`, repository-security checks, Windows authority build/self-test, CodeQL, Dependency Review, and exact-head re-review.

---

### Task 1: Off-mode authority semantics

**Files:**
- Modify: `scripts/lib/authority-grant.mjs`
- Modify: `scripts/lib/mutation-execution-context.mjs`
- Modify: `scripts/lib/mutation-policy.mjs`
- Modify: `references/mutation-modes.md`
- Test: `tests/unit/authority-mode-enforcement.test.mjs`
- Test: `tests/unit/mutation-execution-context.test.mjs`

**Interfaces:**
- `classifyAuthority(...)` must not promote raw request booleans to trusted provenance when no grant verifies.
- Off-mode execution receives an explicit policy option indicating trusted authority is disabled by user configuration; no Authority host/redeemer is invoked.
- `authorizeMutation(...)` accepts that option only for Off mode and still enforces the selected profile/action allowlist.

- [ ] Add failing tests proving raw `explicitInstruction:true` is not independently trusted and Off never calls the Authority host.
- [ ] Run focused tests and capture RED.
- [ ] Implement Off-mode policy provenance without Windows Hello.
- [ ] Run focused tests to GREEN.

### Task 2: Required probe trigger-file coverage

**Files:**
- Modify: `scripts/lib/probe-evidence.mjs`
- Test: `tests/unit/probe-evidence.test.mjs`

**Interfaces:**
- `validateProbeEvidenceRecord(record, {triggerFiles, required})` rejects `clean` required evidence unless `files` is a duplicate-free exact set of all trigger files.

- [ ] Add failing tests for empty, partial, duplicate, and extra-file clean evidence.
- [ ] Run focused test to RED.
- [ ] Implement exact required-trigger coverage validation.
- [ ] Run focused test to GREEN.

### Task 3: Classic branch-pattern fail-closed parity

**Files:**
- Modify: `scripts/lib/snapshot-evaluators.mjs`
- Test: `tests/unit/classic-branch-protection-safety.test.mjs`

**Interfaces:**
- `patternMatchesBranch(pattern, branch)` returns a boolean only for the proven GitHub-compatible subset.
- Ambiguous/unsupported pattern syntax is surfaced as incomplete policy evidence so merge readiness becomes `unknown`, not an incorrect non-match.

- [ ] Add failing parity/fail-closed fixtures for `^`, backslash forms, dot-leading names, bracket classes, `*`, `?`, and `**`.
- [ ] Run focused test to RED.
- [ ] Replace permissive approximation with strict subset parser and propagate unsupported syntax as unknown policy evidence.
- [ ] Run focused test to GREEN.

### Task 4: Router prompt-injection boundary

**Files:**
- Modify: `scripts/lib/skill-router.mjs`
- Test: `tests/unit/skill-router.test.mjs`
- Test: `tests/evals/behavioural-adversarial-cases.json` when schema permits a direct paired fixture.

**Interfaces:**
- Attributed issue bodies/descriptions and generic repository-provided text are stripped from authority-bearing routing analysis.
- A genuine user instruction after the attributed section remains routable only when it is syntactically outside the untrusted attribution span.

- [ ] Add failing issue-body/description injection tests plus neutral controls.
- [ ] Run focused test to RED.
- [ ] Extend attribution stripping using channel-oriented parsing rather than a single new phrase special case.
- [ ] Run focused test to GREEN.

### Task 5: Named GraphQL mutation detection

**Files:**
- Modify: `scripts/lib/mutation-boundary-security.mjs`
- Test: `tests/unit/mutation-boundary-security.test.mjs`

**Interfaces:**
- Detect GraphQL mutation operation headers with optional operation names and variables.
- Extract the first top-level mutation field for registered-mutation checks.

- [ ] Add failing tests for `mutation UpdateThing(...) { ... }` outside and inside privileged mutation files.
- [ ] Run focused test to RED.
- [ ] Implement named/anonymous operation detection.
- [ ] Run focused test to GREEN.

### Task 6: Merge review-thread TOCTOU hardening

**Files:**
- Modify: `scripts/merge-pr-driver.mjs`
- Modify as needed: `scripts/lib/workflow-security.mjs`
- Modify: `references/merge-pr.md`
- Test: merge-driver unit/integration tests that exercise final recapture and live-policy evidence.

**Interfaces:**
- Immediately before merge authority/broker execution, recapture review threads against the same head/base decision.
- Automated merge requires provable active conversation-resolution enforcement with no bypass evidence gap; otherwise fail closed.

- [ ] Add failing race fixture where a thread appears after the earlier ship gate.
- [ ] Add failing fixture for unproven/bypassable conversation-resolution enforcement.
- [ ] Run focused tests to RED.
- [ ] Implement final thread recapture + enforcement proof gate.
- [ ] Run focused tests to GREEN.

### Task 7: Behavioral-eval trusted provenance

**Files:**
- Modify: `scripts/lib/behavioural-evals.mjs`
- Modify: `scripts/compare-behavioural-evals.mjs`
- Modify: `references/behavioural-evaluations.md`
- Test: `tests/unit/behavioural-evals.test.mjs`

**Interfaces:**
- Existing canonical transcript hash remains integrity evidence.
- Add explicit provenance trust classification; unsigned/local packs are diagnostic-only and cannot be accepted as trusted gating evidence.
- Attested evidence binds run identity plus canonical transcript hash and is the only path to `trusted:true`.

- [ ] Add failing tests showing a fabricated self-consistent sidecar cannot claim trusted gating status.
- [ ] Run focused test to RED.
- [ ] Implement trust classification and gating behavior without adding network dependencies to unit tests.
- [ ] Run focused test to GREEN.

### Task 8: Canonical audit ledger and final verification

**Files:**
- Modify: `github-delivery-audit-state.json`
- Modify: `CHANGELOG.md`

- [ ] Record generation 10 baseline/head, findings, dispositions, and remediation PR reference without marking anything fixed before exact-head verification.
- [ ] Run all focused tests.
- [ ] Run canonical `npm run check` in CI.
- [ ] Run Windows authority build/self-test, repository policy/security, CodeQL, Dependency Review, and Windows rewrite baseline.
- [ ] Perform a fresh exact-head bug/security/spec review.
- [ ] If any finding remains, return to its task and repeat RED/GREEN + exact-head validation.
- [ ] Open/update the PR body with exact verification evidence and the explicit Off-mode no-Windows-Hello acceptance statement.
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Audit Generation 10 Remediation Design

## Goal

Fix every substantive bug finding from the generation-10 adversarial audit without weakening the user's explicit `authorityMode=off` choice.

## Hard constraint: Off means no Windows Hello

When `authorityMode=off`, GitHub Delivery must never invoke the Windows Authority host merely to authorize a mutation. Off is an explicit opt-out of OS-backed approval. The implementation must not reintroduce Windows Hello for merge, human reply, close, delete, or any other action.

Off also must not claim that caller-provided `explicitInstruction` or `exactTextConfirmed` booleans are independently trusted user-consent provenance. In Off mode, authorization is derived from the routed mutation profile and workflow policy, while the receipt records that trusted authority was disabled by user configuration. `high-assurance` and `all` keep their existing OS-backed grant semantics.

## Findings covered

### GD-AUDIT-003 — caller-attested lifecycle intent in Off mode

Remove the misleading trust elevation of raw request booleans when no trusted grant exists. Add an explicit Off-mode authorization path that treats trusted authority as disabled by user configuration rather than synthesizing trusted user intent. Raw request booleans must not be reported as trusted provenance. High-assurance/all behavior remains unchanged.

Acceptance:
- Off-mode execution never calls the Authority host or Windows Hello.
- Raw `explicitInstruction:true` / `exactTextConfirmed:true` are not treated as independently verified provenance.
- Allowed actions remain bounded by the routed mutation mode/action policy and the normal broker invariants.
- High-assurance/all still require scoped trusted authority where configured.

### GD-AUDIT-018 — required probe clean evidence can omit trigger files

A required triggered probe may report `clean` only when its evidence proves coverage of every triggering file. Missing, empty, duplicate, or partial file evidence fails closed. Non-triggered/advisory probes retain their existing semantics.

### GD-AUDIT-006 — classic branch-pattern parity

Stop pretending the custom matcher is fully GitHub-compatible. Support only a deliberately proven subset of classic branch patterns with semantics matching GitHub's documented `File.fnmatch` behavior; fail closed when a pattern uses unsupported/ambiguous syntax. Add parity fixtures for escaped characters, bracket classes, dot-leading names, and unsupported constructs.

### GD-AUDIT-022 — issue-body/repository-text prompt injection

Destructive routing authority must come from trusted user text, not attributed repository content. Expand the attributed-untrusted-text stripper to cover issue bodies/descriptions and generic repository-text attribution, and add neutral/adversarial twins proving that an embedded `merge PR #...` instruction does not route to merge while a real user instruction still does.

### GD-AUDIT-023 — named GraphQL mutation detector bypass

The mutation-boundary scanner must detect both anonymous and named GraphQL mutation operations. Prefer a small operation-header parser over a mutation regex that only recognizes anonymous forms. Privileged files remain limited to the registered GraphQL mutation names; non-privileged production helpers must reject any GraphQL mutation.

### GD-AUDIT-015 — unresolved-review-thread merge TOCTOU

Keep GitHub-side conversation-resolution enforcement as the hard server-side backstop, and make merge execution require evidence that this enforcement is active and non-bypassable before the final merge mutation. Perform a final unresolved-thread recapture immediately before authority/broker execution and bind its capture to the same head/base snapshot. If conversation-resolution enforcement cannot be proven, fail closed instead of relying on the client-side snapshot alone.

### GD-AUDIT-021 — behavioral-eval self-attested transcripts

A plain JSON transcript plus matching canonical hash is internally consistent but not trusted execution provenance. Preserve unsigned/local behavioral scores for diagnosis, but they must be marked untrusted and must not be accepted as release/merge gating evidence. Add an attested provenance form whose signature binds the canonical transcript hash plus run identity; only attested runs may claim `trusted:true` for gating.

## Testing strategy

Use TDD for every finding:

1. Add focused failing regressions for the exact exploit/false-green case.
2. Run focused tests and preserve RED evidence.
3. Implement the narrowest production change.
4. Run focused GREEN tests.
5. Run canonical `npm run check` plus Windows authority build/self-test and repository-security checks through CI.
6. Re-review the exact final diff for security/spec/correctness before PR completion.

The Off-mode suite must contain a direct assertion that no Authority-host client/redeemer/Windows Hello path is invoked when `authorityMode=off`.

## Non-goals

- Do not remove or weaken `high-assurance` or `all` authority protection.
- Do not add a replacement interactive confirmation prompt in Off mode.
- Do not broaden mutation profiles or bypass expected-head, ownership, idempotency, stack, ship-gate, or publication checks.
- Do not turn behavioral-eval attestation into a network dependency for ordinary offline unit tests.
42 changes: 36 additions & 6 deletions references/behavioural-evaluations.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ traces fail closed. `run.provenance.transcriptsSha256` must equal
`hashBehaviouralTranscripts(parsedSidecar)`: the SHA-256 of `canonicalJson` of
the parsed sidecar object, not of the sidecar file bytes. Hashing the pretty-printed
file fails closed with `behavioural_transcript_hash_mismatch`.
`attachTranscriptProvenance(run, transcripts)` writes this field.
`attachTranscriptProvenance(run, transcripts)` writes this integrity binding.

Pack (`candidate.json`):

Expand Down Expand Up @@ -85,22 +85,51 @@ Sidecar (`candidate.transcript.json`):

Actions are the observed tool-call names plus authority-redemption and mutation-receipt actions. Do not grade free-form prose or self-attested summaries when the scorer can read a sidecar trace.

### Integrity is not trusted execution provenance

A plain run pack plus a matching transcript hash is internally consistent but still caller-produced evidence. `scoreBehaviouralRun` therefore marks the ordinary `github-delivery/behavioural-transcript` form as:

- `provenance.trusted: false`;
- `provenance.reason: unattested_behavioural_transcript`;
- `gatingEligible: false`.

Such scores remain useful for local diagnostics and quality comparisons. They must **not** be cited as trusted release or merge-gating evidence.

Trusted gating requires a host-produced signature over the canonical attestation payload returned by `behaviouralAttestationPayload(run, transcriptsSha256)`. The run uses provenance kind `github-delivery/behavioural-transcript-attestation` and carries the base64 signature. Verification uses a separately supplied trusted public key; a signature or `trusted:true` field embedded in the run is never sufficient by itself. The signed payload binds model, host, variant, skill version, and the canonical transcript hash.

Only a successfully verified attestation produces `provenance.trusted: true` and `gatingEligible: true`.

## Compare

Each run file must not embed `trace` objects. Observed evidence comes from a sibling
`<run>.transcript.json` file, and `run.provenance.transcriptsSha256` must equal
`hashBehaviouralTranscripts` of that parsed object.

Diagnostic comparison remains available without an attestation key:

```bash
node scripts/compare-behavioural-evals.mjs \
cases.json baseline.json current.json candidate.json
```

This reports metric quality while leaving each ordinary score `gatingEligible:false`.

Any workflow that wants to use behavioural results as a release/merge gate must require trusted attestations explicitly:

```bash
node scripts/compare-behavioural-evals.mjs \
--require-trusted \
--attestation-public-key host-public-key.pem \
cases.json baseline.json current.json candidate.json
```

The strict form exits non-zero unless **all three** runs have valid signatures under that externally supplied key and the candidate also matches/improves current quality and safety.

The command exits:

- `0` when the candidate matches or improves current quality/safety metrics;
- `1` when the candidate regresses a protected quality or safety metric;
- `2` for malformed/incomplete evaluation inputs.
- `0` when the candidate matches or improves current quality/safety metrics, and in `--require-trusted` mode all runs are trusted;
- `1` when the candidate regresses a protected metric or strict trusted-gating requirements are not satisfied;
- `2` for malformed/incomplete evaluation inputs or invalid attestation material.

The comparison reports:

Expand All @@ -113,7 +142,8 @@ The comparison reports:
- missing case count;
- tokens, tool calls, and duration as cost evidence;
- lift over the bare model;
- delta from the current skill.
- delta from the current skill;
- whether trusted gating is eligible.

Cost metrics are recorded, not automatically optimized at the expense of correctness. A future acceptance policy may require a minimum quality lift per token/tool-call cost once enough real runs exist.

Expand Down Expand Up @@ -164,4 +194,4 @@ Do not retain a new workflow, reviewer, prompt block, scanner, or context expans
- improve measured recall/precision/coverage/safety against current; or
- close a deterministic policy/evidence gap that cannot sensibly be measured by model output.

If a candidate adds cost without measurable quality/safety benefit, simplify or remove it.
Unsigned/local behavioural evidence may inform diagnosis, but it is not trusted release/merge-gating evidence. If a candidate adds cost without measurable quality/safety benefit, simplify or remove it.
2 changes: 2 additions & 0 deletions references/mutation-modes.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ The persistent user config defaults to `high-assurance`. It lives outside the in

`off` is an explicit opt-out that means **no Windows Hello / trusted-authority prompt**. It does not mean “the agent can do anything.” Direct merge instruction, exact-text confirmation for human replies, expected-head checks, ownership checks, idempotency, workflow routing, ship gates, and all other mutation-policy rules remain mandatory.

In `off`, caller-controlled mutation JSON is not provenance for those user-confirmation facts. Request fields such as `explicitInstruction` and `exactTextConfirmed` are ignored as authorization claims. A governing workflow that actually observed the current user instruction or exact outgoing text must provide that fact out-of-band through the mutation execution context. The generic mutation-document entrypoint cannot mint it. This preserves zero-Hello operation without turning a self-attested request boolean into consent.

Dry-run planning never requires trusted authority. When the selected mode requires authority at `--execute`, the trusted grant must contain `scopeSha256`; a legacy resource-only signature is not enough.

A **PR session** is an opt-in Hello grant, distinct from a branch lease. After Windows Hello, the approval UI may start a 5–60 minute session bound to one allowlisted repo, one PR, one head branch, and the approved merge base for merge. Later exact-scope `push_code` and `merge_pr` batches on that tuple skip Hello (`approvalMethod: pr_session`) but still receive one-time redeemable grants. A retargeted base requires Hello again. Branch leases remain `push_code` only for 1–10 minutes. Comments, human replies, close, and delete still need Hello. Mixed-action batches are not session-eligible.
Expand Down
Loading
Loading