Skip to content

P3.31: durable pending human-triage surface + CLI approve path (#63) - #76

Merged
franciszver merged 12 commits into
mainfrom
feat/p3-31-durable-pending-surface
Jul 26, 2026
Merged

P3.31: durable pending human-triage surface + CLI approve path (#63)#76
franciszver merged 12 commits into
mainfrom
feat/p3-31-durable-pending-surface

Conversation

@franciszver

@franciszver franciszver commented Jul 26, 2026

Copy link
Copy Markdown
Owner

Summary

Closes #63

A vuln report routed to pending_human_approval previously had no durable surface: ActionLog.export_jsonl was only ever called from emit_snapshot at the top of a campaign iteration, so a --iterations 1 run's own events (including exploit_recorded/vuln_report_pending_human_approval) never reached the exported log; tools/run_campaign.py always used ExploitDB(":memory:")/DocumentationAgent(reports_dir=None); and there was no approve path reachable from the CLI at all. Approving VULN-0004 required a bespoke reconstruction script (tools/approve_vuln_0004.py) because DocumentationAgent._pending was in-memory only and the filing process had exited (issue #66's sharpening comment on #63).

What changed

  • Post-loop ActionLog export (redteam/campaign.py): run_campaign now calls action_log.export_jsonl(action_log_ref) unconditionally after the loop (in a try/finally, hardened further below), so a run's own events are never lost regardless of iteration count, an injected snapshot_fn, or an exception escaping the loop.
  • DocumentationAgent persists pending reports too (redteam/agents/documentation.py): a pending report is now written to <reports_dir>/<report_id>.pending-human-approval.json (same suffix convention tools/build_vuln_reports.py already used for VULN-0004), and __init__ loads every persisted filed/pending report from reports_dir back into memory at construction. This is what makes approve() reachable across a process boundary — no bespoke reconstruction script needed. A filed report wins over a stale pending leftover of the same exploit_id (self-healing after a crash between persisting-filed and unlinking-pending). A reports_dir this module can't parse raises DocumentationAgentError loudly rather than silently losing a pending report.
  • Durable pending-triage count: redteam/observability/findings.py gains pending_human_triage_count(); emit_snapshot wires it into a new optional, additive v1 observability_snapshot.schema.json field pending_human_triage_count (see contracts/README.md's versioning rule — new optional property, not added to required, stays v1).
  • CLI approve/list-pending path (tools/run_campaign.py): --list-pending --reports-dir PATH and --approve EXPLOIT_ID --reports-dir PATH --approved-by NAME (--db-path PATH | --unverified-i-vouch-without-db-check). Neither touches a live model/target. When --db-path names a persisted exploit DB, --approve re-derives the report from the original exploit record via build_vuln_report and refuses to approve on any field-value drift from the persisted pending artifact — the same verify-then-approve discipline tools/approve_vuln_0004.py established, generalized to any exploit_id.
  • Default run behaviour is unchanged: ExploitDB(":memory:")/DocumentationAgent(reports_dir=None) stay the default (a quick demo/smoke run per docs/DEMO_SCRIPT.md should still leave nothing behind). --reports-dir/--db-path are opt-in.
  • Deep-review self-fix: the auto-load behavior above regressed tools/approve_vuln_0004.py's own designed re-run flow (a genuinely-pending report would crash the script via the duplicate-rejection guard, plus a double-unlink on the pending file). Fixed and covered by a new regression test.
  • Doc test-count claims updated in docs/ATO_EVIDENCE_PACKET.md/docs/DEMO_SCRIPT.md per tests/test_doc_test_counts.py.

Cold-review remediation (this revision)

A cold review of the original state above returned DO-NOT-MERGE with 4 blockers and several majors/minors, all fixed in follow-up commits on this same branch, each red-first and gated (decluttersec-auditdeep-review):

  1. tools/approve_vuln_0004.py self-comparison (BLOCKER): the already_loaded branch compared the auto-loaded pending report against pending_on_disk — the same file — which always passed regardless of tampering. Proven: a hand-tampered VULN-0004 pending artifact (severity: "low", doctored clinical_impact) filed cleanly (rc=0) before the fix. Fixed: the comparison now uses an independent re-derivation via build_vuln_report(record, ...); already_loaded is used only to satisfy approve()'s in-memory precondition.
  2. --approve failed open (BLOCKER): no --db-path meant no provenance check at all (a hand-written pending report approved cleanly); a --db-path naming a non-existent file silently created an empty sqlite DB and downgraded to a warning; --approved-by defaulted to "owner". Fixed: the cross-check is now required by default (explicit --unverified-i-vouch-without-db-check escape hatch, loud WARNING), a missing --db-path file is a hard refusal, --approved-by has no default, and the report body is printed before stamping.
  3. --reports-dir without --db-path crashed the run (BLOCKER): the documented "merely collides" flag combo actually raised an uncaught DocumentationAgentError from file_report mid-loop, and — because the post-loop export had no try/finally — silently discarded that run's own action log. Fixed: file_report is now wrapped like its sibling component calls, the whole loop is in try/finally, and run mode refuses to start with this flag combo instead of emitting a NOTE.
  4. Filename/report_id mismatch could overwrite approved evidence (MAJOR): _load_persisted keyed reports by exploit_id from file content, never checking the filename or report_id uniqueness. Proven: a weird-name.pending-human-approval.json claiming report_id: VULN-0001, exploit_id: EXP-0002 caused --approve EXP-0002 to overwrite the already-filed, already-approved VULN-0001.json. Fixed: a persisted file whose name doesn't match its own claimed report_id is now rejected at load, report_id collisions across exploit_ids are rejected, and each pending report's real source path is tracked and unlinked on approval (not reconstructed from report_id).
  5. Everything else (MINOR/doc): --list-pending/--approve no longer leak a raw traceback for a malformed reports_dir; contracts/README.md and the schema's field description no longer overclaim "consumers stay valid" (true for producers, not for a consumer pinned to a pre-P3.31: pending human-triage reports have no durable surface #63 schema copy); pending_human_triage_count's two different scopes (snapshot-field vs. --list-pending's directory-wide count) are now documented in the schema, the Python docstring, and docs/ARCHITECTURE.md; the --db-path rebuild now derives force_human_gate from the trusted exploit record's category (not the untrusted field under verification) and carries fix_validation_status through; two stray Mermaid colour mutations in docs/ATO_EVIDENCE_PACKET.md reverted; the new CLI approval path is now documented as Beat 5 of docs/DEMO_SCRIPT.md; doc test-count claims refreshed to match the live suite.

Verification

  • Red-first, corrected claim: the original 3-commit red-first sequence claimed all 3 were red-first; in fact only 2 were (documentation-agent persistence, campaign post-loop export) — the pending_human_triage_count field shipped bundled with its own already-passing tests in the same commit (see that commit's own message). Every fix in the cold-review remediation above is genuinely red-first, with failing output quoted in each commit message.
  • Gates run inline for every fix: declutter (folded a duplicated CLI reports_dir-load try/except into one helper; removed a dead variable assignment pyflakes flagged after the FIX 1 change), sec-audit (re-ran all four of the cold reviewer's proven attacks against the fixed code — each now refused, fail-closed, with a clean CLI message rather than a traceback), deep-review (semantic diff of redteam/campaign.py isolated to exactly the intended try/finally wrap + one new try/except, no accidental logic change from the reindentation).
  • docs/vuln_reports/*.json (VULN-0001..0004) sha256-verified byte-identical before and after every commit in this remediation — never touched, never run against with any builder/approve tool.
  • Full suite: 375 passed locally (sibling checkout present) — grew from 359 with this remediation's new tests.

Test plan

  • python -m pytest tests/ -q → 375 passed
  • docs/vuln_reports/*.json sha256 unchanged (verified before this remediation and after every commit)
  • Manual end-to-end: file a pending report with one DocumentationAgent instance, del it, approve with a fresh instance via tools/run_campaign.py --approve ... --reports-dir ... --db-path ... --approved-by ... — works with no bespoke script
  • All four cold-reviewer attacks reproduced against pre-fix code (confirmed exploitable) and re-run against post-fix code (confirmed refused, fail-closed)
  • CI green (polling below)

Adds failing tests proving the gap: a single-iteration run's own events
never reach the exported action log (no post-loop export), a report left
pending by one DocumentationAgent instance cannot be approved by a fresh
one pointed at the same reports_dir, and no durable pending-triage count
exists. Bundles the small additive observability_snapshot.schema.json
field (pending_human_triage_count, optional, v1-additive per
contracts/README.md) and its already-passing tests -- the two remaining
red tests (documentation-agent persistence, campaign post-loop export)
are fixed in the next commit.

5 failing:
  tests/redteam/test_campaign.py::test_post_loop_action_log_export_includes_last_iterations_own_events
  tests/redteam/test_documentation_agent.py::test_pending_report_persisted_with_suffix_until_approved
  tests/redteam/test_documentation_agent.py::test_pending_report_persisted_by_one_agent_is_approvable_by_a_fresh_instance
  tests/redteam/test_documentation_agent.py::test_stale_pending_file_dropped_once_filed_exists
  tests/redteam/test_documentation_agent.py::test_corrupt_persisted_report_raises_loudly_not_silently_ignored

Refs #63
… export (issue #63)

DocumentationAgent now persists pending reports too
(<report_id>.pending-human-approval.json, same suffix convention
tools/build_vuln_reports.py already used for VULN-0004) and loads
persisted filed/pending reports back from reports_dir on construction --
so approve() is reachable from a fresh process/instance without any
per-report reconstruction script. A filed report wins over a stale
pending leftover of the same exploit_id on load (self-healing after a
crash between persisting the filed file and unlinking the pending one).
A reports_dir this module can't parse raises loudly instead of silently
losing a pending report.

run_campaign now exports the action log unconditionally after the loop,
not only at the top of each iteration via emit_snapshot -- a
single-iteration run's own events (directive_issued through
exploit_recorded/vuln_report_*) previously never reached the exported
jsonl at all.

Updates docs/ATO_EVIDENCE_PACKET.md and docs/DEMO_SCRIPT.md test-count
claims (346->352 with sibling, 240->246 in CI) per
tests/test_doc_test_counts.py, and tests/tools/test_approve_vuln_0004_gate.py's
now-stale "pending reports are never persisted" assertion.

352 passed (with sibling checkout).

Refs #63
tools/run_campaign.py's main() took no arguments and had no
--approve/--list-pending mode at all -- there was no CLI path to approve a
durably-pending report. 6 failing tests calling main(argv) directly
(no live model/target, throwaway tmp_path reports_dir/db).

6 failing:
  tests/tools/test_run_campaign_cli.py::test_approve_requires_reports_dir
  tests/tools/test_run_campaign_cli.py::test_list_pending_requires_reports_dir
  tests/tools/test_run_campaign_cli.py::test_cli_lists_and_approves_a_report_left_pending_by_a_separate_process
  tests/tools/test_run_campaign_cli.py::test_approve_unknown_exploit_id_fails_without_writing
  tests/tools/test_run_campaign_cli.py::test_approve_refuses_when_pending_report_drifts_from_its_source_exploit_record
  tests/tools/test_run_campaign_cli.py::test_never_auto_approves_no_default_exploit_id

Refs #63
…s (issue #63)

tools/run_campaign.py gains:
  --list-pending --reports-dir PATH
  --approve EXPLOIT_ID --reports-dir PATH [--db-path PATH] [--approved-by NAME]

Neither touches a live model or target -- both construct a
DocumentationAgent(reports_dir=...), which now loads persisted pending
reports back from disk (previous commit), so a report left pending by a
prior `run` invocation is approvable with no bespoke per-report script.
When --db-path names a persisted exploit DB, --approve re-derives the
report from the original exploit record via build_vuln_report and refuses
(exit 1, nothing approved) on any field-value drift from the persisted
pending artifact -- the same verify-then-approve discipline
tools/approve_vuln_0004.py established, generalized to any exploit_id.

Default `run` behaviour is UNCHANGED: ExploitDB(":memory:") and
DocumentationAgent(reports_dir=None) stay the default (a quick demo/smoke
run should still leave nothing behind) -- --reports-dir/--db-path are
opt-in. A stderr NOTE fires when reports_dir is unset (pending reports
from this run won't survive) or when reports_dir is set without db_path
(exploit IDs restart at EXP-0001 and may collide with prior durable
reports).

Updates docs/ATO_EVIDENCE_PACKET.md and docs/DEMO_SCRIPT.md test-count
claims (352->358 with sibling, 246->252 in CI) per
tests/test_doc_test_counts.py.

358 passed (with sibling checkout).

Refs #63
…fix=)

_persist and _persist_pending were identical apart from the filename
suffix; one method with a suffix keyword covers both call sites. No
behavior change -- 358 passed before and after.
…crashes on an auto-loaded pending report

Deep-review (BLOCKER, CONFIRMED) found this diff regressed
tools/approve_vuln_0004.py's own designed use case: DocumentationAgent now
auto-loads persisted pending reports at construction time, so main()'s
DocumentationAgent(reports_dir=_REPORTS_DIR) already has the pending
EXP-0004 report loaded by the time _file_pending() re-drives file_report()
on it -- colliding with the one-exploit-one-report duplicate-rejection
guard and crashing with DocumentationAgentError instead of approving.

Reproduced directly (see commit history): a fresh DocumentationAgent
pointed at a reports_dir holding a genuinely-pending report raised
DocumentationAgentError from _file_pending. Fixed by using the
already-loaded pending report directly (documentation.get_pending())
instead of re-filing when it's present; _file_pending stays as a fallback
for the (no longer reachable, but harmless) case where it isn't. Also
fixed a second-order double-unlink: DocumentationAgent.approve() now
removes the persisted pending file itself, so main()'s own
_PENDING_PATH.unlink() would FileNotFoundError right after -- made
missing_ok=True.

Also noted (PRE-EXISTING, not fixed here -- out of scope): the success
print's _FILED_PATH.relative_to(_REPO_ROOT) crashes if reports_dir isn't
under the repo root, the same class of bug issue #64 already fixed in the
sibling tools/build_vuln_report_p3_54.py via _display_path. Flagged in the
PR description for a separate pass.

New regression test (tests/tools/test_approve_vuln_0004_rerun.py) drives
the real main() end-to-end against a scratch reports_dir and asserts it
approves cleanly instead of raising, plus a second idempotent run.

Updates docs/ATO_EVIDENCE_PACKET.md and docs/DEMO_SCRIPT.md test-count
claims (358->359 with sibling, 252->253 in CI).

359 passed (with sibling checkout).

Refs #63
…ainst itself, not the exploit record

The already_loaded branch set pre_approval from documentation.get_pending()
-- read straight back off _PENDING_PATH by DocumentationAgent's own
auto-load -- and compared it against pending_on_disk, the same file. The
comparison always passed regardless of tampering; record =
_build_exploit_record() was built and never used.

Proof: file a legitimate pending VULN-0004 in a scratch dir, hand-edit
severity to "low" and clinical_impact to "DOCTORED: ...", run main().
Before: rc=0, doctored artifact filed as owner-approved. After: rc=1,
nothing written.

Fix: reconstructed_body now comes from build_vuln_report(record, ...) --
independent re-derivation from the trusted exploit record -- and that is
what gets compared against the on-disk artifact. already_loaded/pre_approval
is used only to satisfy approve()'s in-memory precondition, never as the
comparison target.

Red-first: tests/tools/test_approve_vuln_0004_tamper.py (quoted failing
output in this session: "assert 0 == 1" before the fix).
…k, no --approved-by default, missing --db-path refused

Four proven attacks against tools/run_campaign.py --approve:

1. With no --db-path, a hand-written VULN-0099.pending-human-approval.json
   approved cleanly with NO provenance check at all.
2. --db-path pointing at a missing file: ExploitDB(path) creates an empty
   sqlite, silently downgrading to "warning: skipping the cross-check" and
   approving as-is (rc=0) -- the one safety flag failed open on the most
   likely operator error (a typo).
3. --approved-by defaulted to "owner", so no explicit human identity was
   ever required.
4. The report body was never printed before being stamped approved.

Fix:
- --db-path + --approved-by are now both required for --approve, enforced
  at argparse time; --unverified-i-vouch-without-db-check is the explicit,
  loud escape hatch for a genuinely DB-less report (prints a WARNING).
- A --db-path that doesn't already exist, or has no record for the
  exploit_id, is now a hard refusal (exit 1) -- never silently created,
  never downgraded to a skipped check.
- The pending report body is printed before documentation.approve() is
  called.
- Also fixes FIX 5's related bug: the rebuild now derives force_human_gate
  from the STORED (trusted) exploit record's category via
  FORCE_HUMAN_GATE_CATEGORIES, never from pending["requires_human_gate"]
  (the field under verification), and carries fix_validation_status
  through from the pending report so a legitimately-updated report doesn't
  spuriously fail the cross-check.

Red-first: 6 new tests in tests/tools/test_run_campaign_cli.py reproducing
each attack (quoted failing output in this session before the fix: attack 1
rc=0 with a hand-written report; attack 2 rc=0 + "warning: no exploit
record ... skipping the field-for-field cross-check" against a typo'd
--db-path that ExploitDB silently created).
…paign; post-loop export in try/finally; --reports-dir without --db-path refuses to start

Reproduced: a second run_campaign() invocation against the same durable
--reports-dir with in-memory (default) exploit numbering restarts at
EXP-0001, collides with the pending VULN-0001 report run 1 left on disk,
and documentation.file_report() raises DocumentationAgentError uncaught --
killing the campaign mid-loop. Because the post-loop action_log export sat
after the loop with no try/finally, the crashed run's own action log
(including everything logged before the crash) was never exported at all.

Fix:
- documentation.file_report() is now wrapped in try/except
  DocumentationAgentError like every sibling component call in the loop
  (orchestrator/red_team/target_client/judge) -- records a
  vuln_report_filing_failed signal and continues; the confirmed exploit
  stays safely recorded in db (db.add_record already ran unconditionally
  before this call).
- The entire iteration loop is now wrapped in try/finally so
  action_log.export_jsonl(action_log_ref) always runs, even if some other
  exception the loop doesn't explicitly catch escapes an iteration.
- tools/run_campaign.py: `run` mode with --reports-dir but no --db-path now
  refuses to start (parser.error) instead of emitting a stderr NOTE and
  continuing into the same collision.

Red-first: tests/redteam/test_campaign.py
(test_duplicate_report_filing_does_not_crash_the_campaign,
test_action_log_exports_even_when_an_iteration_raises_uncaught) and
tests/tools/test_run_campaign_cli.py
(test_run_mode_reports_dir_without_db_path_refuses_to_start) -- quoted
failing output in this session: uncaught DocumentationAgentError from
redteam/campaign.py:436, and "assert False" on action_log_ref.exists()
before the fix.
…oved evidence via a filename/report_id mismatch

_load_persisted() keyed loaded reports by exploit_id taken from FILE
CONTENT, never checking the filename or enforcing report_id uniqueness.

Reproduced: a file named weird-name.pending-human-approval.json carrying
report_id: VULN-0001, exploit_id: EXP-0002 caused --approve EXP-0002 to
overwrite the already-filed, already-approved VULN-0001.json, and the
stale source file was never removed (_remove_pending_file unlinked a path
constructed from report_id, not the file's actual source path -- which
never existed under that constructed name).

Fix:
- _load_persisted() now rejects (DocumentationAgentError, fail loud like
  every other load-time defect this method already catches) any persisted
  file whose name does not match "<its own claimed report_id><suffix>".
- report_id uniqueness is enforced across all loaded reports -- two
  different exploit_ids claiming the same report_id is rejected even when
  both files are individually correctly named.
- Each pending report's real source path is now tracked in
  _pending_paths (populated on load AND on file_report's own persist), and
  _remove_pending_file unlinks that tracked path -- not a path
  reconstructed from report_id -- so approval always removes the file that
  was actually read.

Red-first: tests/redteam/test_documentation_agent.py
(test_load_rejects_a_pending_file_whose_name_does_not_match_its_own_report_id,
test_load_rejects_duplicate_report_id_across_different_exploit_ids,
test_approve_removes_the_actual_source_path_not_a_report_id_guess) -- quoted
failing output in this session: "DID NOT RAISE DocumentationAgentError" and
"AttributeError: 'DocumentationAgent' object has no attribute
'_pending_paths'" before the fix.
…on, doc-count refresh

- tools/run_campaign.py: --list-pending and --approve now catch
  DocumentationAgentError from DocumentationAgent(reports_dir=...) at the
  CLI boundary (clean stderr message + rc 1) instead of letting a raw
  traceback escape when reports_dir contains an unrelated/malformed JSON
  file. Red-first: tests/tools/test_run_campaign_cli.py
  (test_list_pending_on_a_directory_with_unrelated_json_fails_cleanly_not_a_traceback,
  test_approve_on_a_directory_with_unrelated_json_fails_cleanly_not_a_traceback).
- contracts/README.md + observability_snapshot.schema.json: the changelog
  claimed "pre-#63 producers and consumers stay valid" for the new
  pending_human_triage_count field -- false for a consumer validating
  against its own pinned pre-#63 copy of the schema (additionalProperties:
  false, unchanged): it will reject any snapshot now carrying the field.
  Reworded to state the one-directional truth (producers stay valid;
  consumers must update their own schema copy first).
- pending_human_triage_count is two different numbers under one name: the
  observability_snapshot field counts only the vuln_reports passed to
  emit_snapshot (this run's own accumulator), while
  --list-pending's identically-named printed key scans an entire
  --reports-dir on disk. Documented in the schema field description,
  redteam/observability/findings.py's docstring, AND
  docs/ARCHITECTURE.md's Observability Layer section (not just a Python
  docstring, per the brief).
- docs/ATO_EVIDENCE_PACKET.md: reverted two stray Mermaid colour mutations
  that were collateral from a scripted replacement (#e05253 -> #e05252,
  #3b3590 -> #3b3520).
- Documented the new CLI approval path (issue #63/#66) in
  docs/DEMO_SCRIPT.md as a new Beat 5, plus a "What this proves" bullet --
  a new operator-facing approval path shipping undocumented at v3.0.0 is
  not acceptable.
- Refreshed stale test-count claims in docs/ATO_EVIDENCE_PACKET.md and
  docs/DEMO_SCRIPT.md (359/253 -> 375/269) to match the live suite after
  this PR's new tests -- tests/test_doc_test_counts.py now passes again.

(tools/run_campaign.py's --db-path/rebuild fixes for the untrusted
requires_human_gate field and missing fix_validation_status were already
fixed and tested as part of the FIX 2 commit.)
…xcept; drop dead pre_approval assignment

tools/run_campaign.py: _cmd_list_pending and _cmd_approve had identical
try/except DocumentationAgentError blocks around DocumentationAgent
construction -- folded into a shared _load_documentation(args) helper.

tools/approve_vuln_0004.py: pyflakes flagged pre_approval as assigned but
never used after the FIX 1 re-derivation change (main() no longer compares
against it). The already_loaded branch's assignment was pure dead
computation; the _file_pending() call in the other branch is kept for its
side effect (populating documentation._pending) with the return value
simply no longer captured. No behavior change -- full suite still 375
passed.
@franciszver
franciszver merged commit 8584283 into main Jul 26, 2026
1 check passed
@franciszver
franciszver deleted the feat/p3-31-durable-pending-surface branch July 26, 2026 04:26
franciszver added a commit that referenced this pull request Jul 26, 2026
…, upstream filings, and kickoff-constraint gap (issue #59)

Extends tests/test_release_notes.py red-first with the corrected facts,
then rewrites docs/RELEASE_NOTES_v3.0.0.md to match:

- #63 and #68 are CLOSED (P3.31/P3.34) -- rewrites the limitations
  section as gaps found and closed, not open, and adds the three new
  limitations P3.31 introduced (pending_human_triage_count is per-run
  not directory-wide, --approve's --db-path opt-out, --reports-dir
  requiring --db-path).
- All four findings are now filed upstream (#167-#170); corrects the
  false "no upstream issue for VULN-0001/2/3" paragraph and notes
  #169/#170 as evidence against upstream #130's closure premise,
  without demanding a reopen.
- Owns the kickoff-brief hard-constraint gap explicitly: intent (no
  shared context) is met; the parenthesised OS-process mechanism is
  not, per ARCHITECTURE.md and the ATO packet, both already public.
- Corrects the stale "redteam.observability not in the forbidden
  import set" claim -- it now is (test_judge_agent.py), and adds the
  symmetric Red-Team-side AST scan.
- Re-derives test counts post-rebase (405 with sibling / 299 in CI,
  106 skipped) across RELEASE_NOTES/ATO_EVIDENCE_PACKET/DEMO_SCRIPT.

Rebase note: docs/ATO_EVIDENCE_PACKET.md and docs/DEMO_SCRIPT.md
conflicts were resolved by taking main's post-#74/#75/#76 versions
wholesale (verified main's wording already supersedes this branch's
pre-rebase edits to the same passages), then bumping only the test
counts to the new post-rebase live total.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TTi1zKDS1ajefLSmM9oL7E
franciszver added a commit that referenced this pull request Jul 26, 2026
… by the rebase

judge.py's (case, response, attempt_id) triple was cited at
judge.py:96-100,343 -- score()'s actual signature moved to 348-357 as
other main PRs (#74/#75/#76) touched the file. campaign.py's
try:-guarded component calls were cited at ...,436 for the
documentation.file_report try: block -- it moved to 446. Both verified
against the current file on disk before correcting; no other citation
in the doc drifted (spot-checked judge.py:44-47, chat.py:570-594,
the recording path, and the JSON-report field claims against the live
tree -- all still accurate).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TTi1zKDS1ajefLSmM9oL7E
franciszver added a commit that referenced this pull request Jul 26, 2026
…, upstream filings, and kickoff-constraint gap (issue #59)

Extends tests/test_release_notes.py red-first with the corrected facts,
then rewrites docs/RELEASE_NOTES_v3.0.0.md to match:

- #63 and #68 are CLOSED (P3.31/P3.34) -- rewrites the limitations
  section as gaps found and closed, not open, and adds the three new
  limitations P3.31 introduced (pending_human_triage_count is per-run
  not directory-wide, --approve's --db-path opt-out, --reports-dir
  requiring --db-path).
- All four findings are now filed upstream (#167-#170); corrects the
  false "no upstream issue for VULN-0001/2/3" paragraph and notes
  #169/#170 as evidence against upstream #130's closure premise,
  without demanding a reopen.
- Owns the kickoff-brief hard-constraint gap explicitly: intent (no
  shared context) is met; the parenthesised OS-process mechanism is
  not, per ARCHITECTURE.md and the ATO packet, both already public.
- Corrects the stale "redteam.observability not in the forbidden
  import set" claim -- it now is (test_judge_agent.py), and adds the
  symmetric Red-Team-side AST scan.
- Re-derives test counts post-rebase (405 with sibling / 299 in CI,
  106 skipped) across RELEASE_NOTES/ATO_EVIDENCE_PACKET/DEMO_SCRIPT.

Rebase note: docs/ATO_EVIDENCE_PACKET.md and docs/DEMO_SCRIPT.md
conflicts were resolved by taking main's post-#74/#75/#76 versions
wholesale (verified main's wording already supersedes this branch's
pre-rebase edits to the same passages), then bumping only the test
counts to the new post-rebase live total.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TTi1zKDS1ajefLSmM9oL7E
franciszver added a commit that referenced this pull request Jul 26, 2026
… by the rebase

judge.py's (case, response, attempt_id) triple was cited at
judge.py:96-100,343 -- score()'s actual signature moved to 348-357 as
other main PRs (#74/#75/#76) touched the file. campaign.py's
try:-guarded component calls were cited at ...,436 for the
documentation.file_report try: block -- it moved to 446. Both verified
against the current file on disk before correcting; no other citation
in the doc drifted (spot-checked judge.py:44-47, chat.py:570-594,
the recording path, and the JSON-report field claims against the live
tree -- all still accurate).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TTi1zKDS1ajefLSmM9oL7E
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

P3.31: pending human-triage reports have no durable surface

1 participant