Skip to content

pr-route-provenance: fence-aware markers, bounded row growth, duplicate receipts, disposition-enum coupling #777

Description

@justin808

Follow-up from advisory review threads on #687 (skills/pr-batch/bin/pr-route-provenance, tests in skills/pr-batch/bin/pr-route-provenance-test.rb). None of these blocks #687. Each was reproduced against head d9dbd54f8385c35b02627d4e2738bae299a4cfc3.

1. Marker discovery is not fence-aware and is implemented three times

apply_to_body (raw body.scan(START_MARKER) / body.sub(...)), validate_managed_section!, and validate_applied_body! each re-implement the "zero or exactly one ordered managed section" check with raw string scans. agent_details_closing_index already walks fenced blocks, but marker discovery does not.

Reproduced: a PR body whose only marker pair sits inside a ```markdown example (no live section) gets the example's inner text replaced by the generated table, which then renders as literal text inside the fence. When a live section also exists, apply refuses with "zero or exactly one ordered managed section" even though the body is well-formed.

Direction: one fence-aware helper (for example managed_section_span(text)) that skips fenced blocks and returns the single live span or raises, used by all three call sites.

2. Bounded ledger gaps

  • commit_rows caps the number of rows at MAX_COMMIT_ROWS but not the size of a row: every wave that attributes the same commit is concatenated into one cell. Reproduced: 30 receipts attributing one commit produce a single 2,251-character row with 30 fragments. A squash workflow with many waves grows without bound and can push the body past GitHub's 65,536-character limit, at which point the PATCH fails.
  • wave_rows omits the middle waves above MAX_WAVE_ROWS (20), but commit_rows still cites them by number. Reproduced: with 25 receipts, the commit table says "Wave 15 ..." while the waves table shows only waves 1-10, the omission row, and 16-25.

Direction: cap fragments per commit cell (first/last N plus an "M more waves remain in the receipts" note), and either flag cited-but-omitted waves in the commit table or list the omitted wave numbers in the omission row.

3. Exact duplicate receipts are accepted

validated_receipts only checks batch/target uniqueness. Reproduced: passing the same receipt file twice (--receipt a.json --receipt a.json) renders two identical waves and attributes the commit to both, so one execution counts as two pieces of route evidence.

Direction: reject receipts whose canonical_json is identical before sorting and numbering.

4. Disposition enumeration is duplicated

MEASUREMENT_DISPOSITIONS (skills/pr-batch/bin/pr-route-provenance lines 15-21) hardcodes the same five keys as ValidateExecutionProvenance::DISPOSITIONS (bin/validate-execution-provenance lines 40-46). Reproduced: adding a value to the validator lets the receipt validate, then route_row raises KeyError (the CLI exits 1 with "Error: key not found"). Fail-closed, but drift is only detected at render time.

Direction: assert at load time or in the test file that MEASUREMENT_DISPOSITIONS.keys.sort == ValidateExecutionProvenance::DISPOSITIONS.sort, or derive the map from the validator's list with an explicit default.

5. Cleanup while in the file

display(value) is identical to code(value). Fold the call sites into code, or give display a distinct rendering if one was intended.

6. GitHubClient parity with skills/post-merge-audit/bin/completed-batch-audit-receipt

PrRouteProvenance::GitHubClient#gh_api (skills/pr-batch/bin/pr-route-provenance lines 50-62) differs from the sibling gh_api in skills/post-merge-audit/bin/completed-batch-audit-receipt (lines 1495-1532):

  • No subprocess timeout: the sibling uses capture_process(command, input:, timeout: gh_timeout_seconds) with a configurable default; this client calls Open3.capture3 with no bound, so a stalled gh api blocks the integration closeout indefinitely.
  • No --hostname: the sibling takes an explicit host for GitHub Enterprise; this client always targets github.com.

The missing force_encoding("UTF-8") before JSON.parse is a separate defect being fixed on #687 itself and is not tracked here.

Direction: reuse the sibling's bounded capture and host handling, or lift both into a shared helper.

Done when

  • apply_to_body ignores marker pairs inside fenced blocks (tests: fenced example plus live section updates only the live section; fenced example alone appends a new section and leaves the example intact), and the three ordered-section checks share one helper.
  • Wave fragments per commit cell are capped and cited-but-omitted waves are explicit; a test covers more than 20 waves attributing one commit.
  • Duplicate canonical receipts are rejected, with a test.
  • A test fails if the two disposition lists drift.
  • gh_api has a bounded timeout with a test, and the host is configurable or explicitly documented as github.com-only.
  • ruby skills/pr-batch/bin/pr-route-provenance-test.rb and rubocop on both files pass.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Parked: low-priority optional work; requires explicit reprioritization before implementation.complexity:complexifyAdds enduring logic, modes, contracts or operational obligations; value is judged separately.triage:needs-scopeNarrow or reconcile the implementation/design before proceeding; see the triage assessment.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions