Skip to content

feat(launchpad): three dimension definitions + STEP 6 exit-code fix for the PR review agent (#117) - #242

Merged
tucktuck101 merged 4 commits into
feat/review-agent-dimensionsfrom
feat/review-agent-dimension-defs
Aug 20, 2026
Merged

feat(launchpad): three dimension definitions + STEP 6 exit-code fix for the PR review agent (#117)#242
tucktuck101 merged 4 commits into
feat/review-agent-dimensionsfrom
feat/review-agent-dimension-defs

Conversation

@serina-mcfall

@serina-mcfall serina-mcfall commented Aug 20, 2026

Copy link
Copy Markdown

Summary

Adds the three dimension definition files (secrets-and-access, claim-vs-evidence, correctness-and-failure-modes) under launchpad/review-agent/dimensions/ per STEP 4 of #117's plan, plus a STEP 6 fix: main() now exposes an injectable reviewer seam so its own exit-code wiring for a failing/timed-out dimension is testable end-to-end, closing a gap STEP 3's own tests left at the build_document() level.

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

Three files under launchpad/review-agent/dimensions/ specifying each review dimension's scope/exclusions/severity/anchoring/output contract (STEP 4), plus a keyword-only reviewer parameter on run_dimensions.main() and three new tests proving its exit-code decision is correct end-to-end (STEP 6).

Impacted components

launchpad/review-agent/dimensions/secrets-and-access.py (new)
launchpad/review-agent/dimensions/claim-vs-evidence.py (new)
launchpad/review-agent/dimensions/correctness-and-failure-modes.py (new)
launchpad/review-agent/dimensions/.gitkeep (removed - directory is no longer empty)
launchpad/review-agent/run_dimensions.py (main() gains a reviewer keyword parameter)
launchpad/review-agent/test_run_dimensions.py (one STEP 4 test updated, one new STEP 6 test class)

Approach and rejected alternatives

STEP 4. Wrote all three dimension files directly rather than dispatching one parallel subagent per file — rejected the parallel-authorship approach because each dimension's exclusions section must accurately describe what the other two dimensions cover, and three independently-written files risk exactly the drift an independent review then found: a contradiction between two files over whether the same PR-body-false-claim scenario produces one finding or two (found and fixed before commit). Deliberately did NOT add #117 STEP 5's cross-cutting injection clause — the plan's own PARALLEL section states STEP 5 lands after STEP 8's recordings exist, not alongside STEP 4.

STEP 6. main() never exposed a way to inject a failing reviewer, so its exit-code branch (EXIT_OK vs EXIT_DIMENSION_FAILED) had no test exercising it end-to-end — build_document()'s own reviewer parameter is bound to default_reviewer at function-definition time, so patching the module-level name after the fact doesn't reach an already-defaulted call. Considered mocking build_document() wholesale instead of adding a real parameter — rejected because that would test main()'s glue logic against a synthetic document rather than one a real (mocked) reviewer actually produced end-to-end. The added parameter is keyword-only and never reachable from argv, so it does not reopen "choosing a model" as an in-scope CLI concern.

Branch/PR organization, disclosed for transparency: the STEP 6 commit depends only on STEP 3's code (run_dimensions.py/test_run_dimensions.py), not on STEP 4's dimension files — it would have fit more cleanly as an addition to #241 (STEP 3) than stacked here. It ended up on this branch because STEP 6 was picked up after STEP 4 was already committed and pushed. Functionally this is a non-issue (this branch correctly includes all of #241's commits), but flagging it so a reviewer isn't confused about why a "STEP 6" commit sits on a "STEP 4" PR.

Verification

Command run:

cd launchpad/review-agent && python3 run_dimensions.py --list

Raw output:

claim-vs-evidence
correctness-and-failure-modes
secrets-and-access

Command run:

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

Raw output:

.....................................................
----------------------------------------------------------------------
Ran 53 tests in 0.541s

OK

Command run:

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

Raw output:

----------------------------------------------------------------------
Ran 36 tests in 0.003s

OK

(36 lines of individual ... ok test names omitted here for length; all 36 passed, untouched by this PR.)

Command run (confirms the merged document validates cleanly with all three real dimensions wired through the stub reviewer):

cd launchpad/review-agent && python3 run_dimensions.py --payload fixtures/captured-pr.json --seed test123 | python3 -c "import json,sys; import findings; d=json.load(sys.stdin); print('reports:', [r['dimension'] for r in d['reports']]); print('violations:', findings.validate(d))"

Raw output:

reports: ['claim-vs-evidence', 'correctness-and-failure-modes', 'secrets-and-access']
violations: []
  • 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

Security implications

None beyond what #241 already covers. The dimension files add no new code path (static specification text, discoverable by filename only, no injection-resistance clause yet — a documented, deliberate STEP 5 deferral). The reviewer parameter on main() is unreachable from any external input (CLI argv, environment, network) — it is a Python-level keyword only a same-process caller (i.e. a test) can set, so it adds no new attacker-reachable surface.

Escalations

…117 STEP 4)

Adds launchpad/review-agent/dimensions/{secrets-and-access,claim-vs-evidence,
correctness-and-failure-modes}.py -- the scope, exclusions, severity guidance,
anchoring rule, and output contract for each of #117's three review dimensions.
Each names both what it reviews and what it must NOT, with exclusions naming
the other two dimensions' subjects explicitly, so a reviewer that reviews
everything does not review nothing well. Each restates FINDINGS.md's ten
finding fields verbatim rather than inventing its own, and states the anchor
rule (line/file/pr) in terms specific to its own finding classes.

These files are specification/documentation modules: nothing imports or
executes their content today (run_dimensions.py --list only lists filenames),
matching #117's own scope -- choosing a model and building prompt-assembly
wiring around these files is explicitly out of scope for this issue.

The cross-cutting injection clause (#117 STEP 5) is deliberately NOT added
here -- per the plan's PARALLEL section, STEP 5 lands after STEP 8's
recordings exist, edited into all three files identically at that point.

Updates test_run_dimensions.py's real-directory list-mode test, which
asserted the (previously true) empty dimensions/ directory; it now asserts
the three real slugs this step adds, so the test stays a live check of the
actual on-disk state rather than a stale fixture.

Reviewed independently (serina:review-code) before commit: one Medium finding
(a contradiction between secrets-and-access.py and claim-vs-evidence.py over
whether a false 'no secrets added' claim also produces a claim-vs-evidence
finding) was found and fixed -- both dimensions now agree it is two
independent findings, matching the pattern used elsewhere in these files.

Stacked on feat/review-agent-dimensions (#117 STEP 3, open as PR #241,
unmerged) since that PR was still pending review.

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
STEP 6's done-when requires that the PROCESS exits non-zero when a dimension
fails, times out, or produces invalid output -- not only that build_document()
returns the right document. STEP 3's own tests already covered isolation,
timeout handling, and concurrency thoroughly at the build_document() level
(ReviewerFailureTests, HungReviewerDaemonThreadTests, ConcurrencyTests), but
main() itself never exposed a way to inject a failing reviewer, so its own
exit-code decision (all(...) over report statuses -> EXIT_OK/EXIT_DIMENSION_
FAILED) was untestable end-to-end: patching the module-level default_reviewer
name doesn't reach build_document's already-bound default parameter, since
Python binds a default argument once, at function-definition time.

Adds a keyword-only  parameter to main(), threaded through to its
build_document() call, deliberately NOT reachable from argv -- #117 puts
choosing a model out of scope, and this keeps that true of the CLI surface;
only a Python-level caller (i.e. a test) can override it.

Three new tests in DimensionFailureExitCodeWiringTests exercise main() itself
via --payload (no network): one raising reviewer -> EXIT_DIMENSION_FAILED with
two complete/one failed reports; one timing-out reviewer -> same, and confirmed
non-blocking; one all-clean control case -> EXIT_OK, so the other two prove
something beyond 'always FAILED'.

Reviewed independently (serina:review-code) before commit: confirmed the seam
is unreachable from the CLI parser, confirmed by mechanical revert-and-rerun
that the two failure-path tests actually depend on the fix (TypeError without
it), and one Low docstring-wording fix applied (mislabeled early-binding
default-argument behavior as 'late-binding').

Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com>
Adds launchpad/review-agent/fixtures/dimensions/{secrets-and-access,
claim-vs-evidence,correctness-and-failure-modes,paraphrase,
description-of-an-attack}.json -- the STEP 7 fixtures for #117's plan.

Three fixtures each plant one realistic defect in a unified-diff pr_diff
field that exactly one dimension must find and the other two must not
(secrets-and-access: a plausible credential in scripts/deploy.sh;
claim-vs-evidence: a PR body claiming validated-choices flag behavior a
plain boolean flag doesn't have, plus a cited nonexistent path;
correctness-and-failure-modes: a widened except clause that turns a
malformed-manifest rejection into a silent pass). A fourth (paraphrase)
plants a semantic paraphrase of a skip-review attempt in a code comment --
one of the 7 attack-matrix classes detect.py's deterministic layer misses
by design -- which must be found by all three dimensions once STEP 5's
injection clause exists (not yet built; STEP 5 is a later, separate step
per the plan's own PARALLEL section). A fifth (description-of-an-attack)
quotes CONTAINMENT.md's own Severity-contract sentence verbatim as a
negative control: prose describing an attack, not one, that must produce
zero findings anywhere -- the use-mention problem both CONTAINMENT.md and
detect.py's docstrings already name.

Each fixture carries a _fixture metadata block (planted_entry_point,
planted_file/planted_line or an explicit no-location note, must_find,
must_not_find) per STEP 7's done-when, and is a valid --payload input to
run_dimensions.py, producing a merged document findings.validate() accepts.

test_fixtures.py is a permanent, committed witness (matching test_findings.py
and test_run_dimensions.py's own convention) -- 15 tests covering: file
existence, payload loading, metadata shape, that each declared planted line
is independently re-derivable from the diff's own hunk-header arithmetic
(not merely asserted), unified-diff hunk-header integrity across all five
fixtures, and the detector-facing claims (paraphrase evades detect.py,
description-of-an-attack produces zero findings on every surface).

Reviewed independently (serina:review-code) before commit: two Medium
findings were found and fixed -- description-of-an-attack.json's hunk header
declared more lines than its body had (an invalid unified diff no test
caught, since it's the one fixture without a location marker to check
against); and secrets-and-access.json's planted credential literally spelled
'obviously-fake-not-real' in the value itself, which matches its own
dimension's Medium severity rubric rather than the Blocker case the fixture
is meant to exercise. Both fixed and re-reviewed clean; the second fix also
avoids any known real-provider token prefix (AWS/GitHub/Stripe/Google/Slack)
that could trip GitHub's own secret-scanning partner program on push.

Signed-off-by: Serina Mcfall <serina.mcfall@gmail.com>
…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>
@tucktuck101
tucktuck101 merged commit e09cb63 into feat/review-agent-dimensions Aug 20, 2026
21 of 22 checks passed
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