Skip to content

feat(launchpad): cross-cutting injection clause in all three dimensions (#117 STEP 5) - #250

Merged
tucktuck101 merged 1 commit into
feat/review-agent-fixturesfrom
feat/review-agent-injection-clause
Aug 20, 2026
Merged

feat(launchpad): cross-cutting injection clause in all three dimensions (#117 STEP 5)#250
tucktuck101 merged 1 commit into
feat/review-agent-fixturesfrom
feat/review-agent-injection-clause

Conversation

@serina-mcfall

Copy link
Copy Markdown

Summary

Adds the cross-cutting injection clause (INJECTION_CLAUSE) identically to all three dimension definitions per #117's plan STEP 5: author-controlled text attempting to skip/approve/suppress/end the review is itself a Blocker finding, closing the 7-of-35 semantic-paraphrase gap detect.py's deterministic layer hands to #117 by name.

Related issue

Refs #117

Issue type

Task


Agent provenance

Field Value
Harness / provider Claude Code
Model claude-sonnet-5
Session reference N/A - this harness does not expose a run id/URL
Initiating human @serina-mcfall

Objective

An identical INJECTION_CLAUSE constant, woven into the assembled PROMPT of all three dimensions/*.py files, plus test_injection_clause.py verifying its byte-identity and detector-clean status.

Impacted components

launchpad/review-agent/dimensions/secrets-and-access.py
launchpad/review-agent/dimensions/claim-vs-evidence.py
launchpad/review-agent/dimensions/correctness-and-failure-modes.py
launchpad/review-agent/test_injection_clause.py (new)

Approach and rejected alternatives

Put the clause in all three files identically rather than one file cross-referencing a shared module — the plan explicitly requires this (one dimension failing to run must never drop semantic-injection coverage to zero silently), and a shared-module approach would have made that failure mode possible again if the import itself failed.

Explicitly stated that the clause overrides each dimension's own subject-matter/language exclusions — considered leaving this implicit (trusting a model to infer that a cross-cutting duty outranks a scoping rule) and rejected it: independent review found the first draft's silence here would have let correctness-and-failure-modes decline the already-committed paraphrase.json fixture (planted in a Rust file) by correctly applying its own "reports nothing about Rust" exclusion, since nothing told it that exclusion doesn't apply to this clause.

Verification

Command run:

cd launchpad/review-agent && python3 test_injection_clause.py

Raw output:

....
----------------------------------------------------------------------
Ran 4 tests in 0.008s

OK

Command run:

cd launchpad/review-agent && python3 test_run_dimensions.py && python3 test_findings.py && python3 test_fixtures.py

Raw output:

.....................................................
----------------------------------------------------------------------
Ran 53 tests in 0.548s

OK

----------------------------------------------------------------------
Ran 36 tests in 0.002s

OK
...............
----------------------------------------------------------------------
Ran 15 tests in 0.009s

OK
  • Tests or checks were run and the raw output is pasted above
  • The diff is confined to the scope of the linked issue
  • No secrets, keys, tokens or hostnames were added to tracked files

Not verified

  • STEP 5's own done-when has a behavioral half this PR does not attempt: that the paraphrase fixture actually yields a Blocker finding from all three dimensions, and the description-of-an-attack fixture yields none, when run against a REAL model. That needs STEP 8's recordings, which don't exist yet — this PR only verifies the clause's text properties (byte-identity, detector-clean, real weaving into the prompt), which is the half checkable without a live model.
  • Did not exercise the assembled prompts against a real model at all in this PR — that's the next step.

Security implications

None beyond what earlier PRs in this stack already cover. This is prompt text, not code — it changes what a future model-backed reviewer is instructed to do, adding no new code path, network call, or credential handling of its own.

Escalations

…ns (#117 STEP 5)

Adds an identical INJECTION_CLAUSE to dimensions/{secrets-and-access,
claim-vs-evidence,correctness-and-failure-modes}.py: author-controlled text
attempting to skip/approve/suppress/end the review is itself a Blocker
finding, with entry_point set to the surface it came from. One dimension
failing to run therefore never drops semantic-injection coverage to zero
silently, per #117's own requirement.

Covers the 7 of 35 attack-matrix classes CONTAINMENT.md's Detection section
hands to #117 by name -- semantic paraphrase, which detect.py's deterministic
layer does not and cannot catch by design. The clause is phrased to avoid the
use-mention trap detect.py's own docstring names (it does not itself trip
_STANDALONE or _SUPPRESS), and explicitly states it overrides every
dimension's own subject-matter/language exclusions -- otherwise an attack
planted in, say, a Rust file would be silently declined by a dimension whose
own scope says it reviews no Rust code, exactly the gap STEP 7's paraphrase
fixture (planted in crates/buzz-relay/src/gate.rs) exists to catch.

test_injection_clause.py is a permanent test covering the half of STEP 5's
done-when checkable without a live model: byte-identity across all three
files, real weaving into each assembled PROMPT (not merely defined and
unused), and that neither the clause alone nor the full assembled prompt
trips the deterministic detector. The other half -- that the paraphrase
fixture actually yields a Blocker from all three dimensions and the
description-of-an-attack fixture yields none -- is a property of real
reviewer output, which is STEP 8's job, not simulated here.

Reviewed independently (serina:review-code) before commit: two High findings
were found and fixed -- the clause as first written did not override
correctness-and-failure-modes' unconditional 'reports nothing about Rust/TS/
Dart' exclusion, which would have made it decline the paraphrase fixture
specifically; and an ambiguous parenthetical risked being read as 'skip-review
is already handled elsewhere, do not re-detect it,' which could cause a model
to withhold the Blocker on a PARAPHRASE (the exact case this clause exists
for) rather than only on the literal wording detect.py already catches. Also
fixed a Low finding: added an explicit single-report rule for the overlap
between an injection attempt phrased as 'a claim of prior approval' and
claim-vs-evidence's own ordinary scope. All three re-reviewed clean.

Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com>
@serina-mcfall serina-mcfall added the by:agent Filed or authored by an AI agent, not a human label Aug 20, 2026
@tucktuck101
tucktuck101 merged commit c3d4fd5 into feat/review-agent-fixtures Aug 20, 2026
23 of 24 checks passed
serina-mcfall added a commit that referenced this pull request Aug 20, 2026
…tic suite (#117 STEP 8) (#252)

Adds launchpad/review-agent/recordings/<fixture>/<dimension>.json (5 fixtures
x 3 dimensions) -- for each STEP 7 fixture, one recorded output per dimension,
produced by actually rendering that fixture's contained document (via
contain.render with a seed-derived nonce) and reasoning through each
dimension's real assembled PROMPT against it, not hand-written to merely look
plausible. Every finding_id is computed via findings.finding_id() from the
finding's own fields; every recording is replayed through the real
run_dimensions.build_document() and validated via findings.validate().

Confirms: secrets-and-access's planted credential is Blocker per that
dimension's own 'could this authenticate against a real system' test;
claim-vs-evidence's fixture yields TWO genuine defects (a diff-contradicting
validation claim, anchor line 23; a citation to scripts/config_schema.py,
which does not exist anywhere in this repository, anchor pr); correctness-
and-failure-modes' widened except clause is a two-part fail-open (a missing
manifest AND a malformed one both now return True); the paraphrase fixture is
caught by all three dimensions via STEP 5's injection clause, each anchored at
its own file/line, never anchor pr; and description-of-an-attack yields no
finding from any dimension, confirming the use-mention negative control
holds. All ten non-owning-dimension pairs are correctly clean.

test_recordings.py is a permanent, committed witness (matching test_fixtures.py's
convention): file existence, provenance (model/date/seed/nonce, and an honest
disclosure that these are a single reasoning pass per fixture applied to all
three dimensions -- not independent model invocations, since three genuinely
separate runs would not converge on identical prose), replay validity and
no-network-call, cross-dimension attribution, exact anchor/location/severity
pinning, and the injection-clause behavioral claims.

Reviewed independently on the most capable available model before commit,
given this is the highest-stakes artifact in the whole plan (STEP 9's control
suite will treat it as ground truth). Nine findings came back, three High:
(1) severity was entirely unpinned -- a Blocker could silently become a Low
while every structural check still passed; (2) the three paraphrase
recordings' prose was byte-identical with no disclosure that this was one
reasoning pass, not three independent samples; (3) the injection clause as
first written would have flagged every fixture's own ordinary 'Approved.'
pr_review_bodies content as an attack -- a false positive on any clean,
already-reviewed PR. Plus four Medium (an evidence-raw test that could not
fail; evidence checked against only one of three recordings; an anchor-pr
test that asserted only what findings.validate() already enforces; claim-vs-
evidence's own ANCHORING_RULE text contradicting FINDINGS.md by routing a
nonexistent-citation defect to anchor 'line' instead of 'pr') and two Low (a
two-part fail-open recorded as one; the fixture's two-finding design
uncounted). All nine fixed and re-verified by mutation (each previously-silent
defect class was reproduced and confirmed to now fail the suite, then
restored), which itself surfaced two further Low gaps -- the second
(citation) finding's own severity was still unpinned, and the sampling
disclosure had no test guarding it -- both fixed and mutation-verified in the
same pass.

The dimensions/*.py changes in this commit are corrections to STEP 5's own
files (PR #250, not yet merged) -- found via this step's review, landing here
because that is where they were caught. See PR body for the same disclosure
STEP 4/6's bundling used.

Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

by:agent Filed or authored by an AI agent, not a human

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants