Skip to content

Follow-up: Fail closed on six reconciled workflow-pack contracts #202

Description

@justin808

Context

React on Rails PR #4749 reconciles its vendored agent-workflow seam to immutable shakacode/agent-workflows commit 1958648b70a450aa67c15b833428485d17021045. Review and replay found six fail-open or ambiguous contracts that belong upstream. The consumer sync did not introduce them and should keep pack-identical helpers byte-identical instead of adding consumer-only overlays.

Problems and expected behavior

1. Unknown pr-ci-readiness buckets are accepted (script)

skills/pr-batch/bin/pr-ci-readiness treats an unrecognized check-row bucket as a successful row. A row such as {"workflow":"CI","name":"mystery","bucket":"future-state"} currently produces READY when it is the only required row. Missing or non-object row fields are likewise not validated before readiness classification.

Expected: fail closed when a fetched check row is structurally invalid or has an unknown bucket. Only explicitly recognized successful/skipped buckets may contribute to READY, and the result must explain why readiness is UNKNOWN or NOT_READY.

Reproduction:

PrCiReadiness.assess(
  pr_number: 1,
  required_used: true,
  rows: [{ "workflow" => "CI", "name" => "mystery", "bucket" => "future-state" }]
)
# Current verdict: READY

This behavior was reproduced against old consumer pin 4bb0c1e4944276e271799fd179e0c166b54ef4dc and the reconciled pack line.

2. Trust-role values accept malformed types (schema)

agent-workflow-seam-doctor normalizes trusted_bots and trusted_metadata_bots with Array(value) and to_s. Hashes, numbers, mixed arrays, and empty entries therefore become apparent logins instead of being rejected. A numeric scalar such as 42 can become the login "42".

Expected: accept a legacy nonempty string or an array containing only nonempty strings, then apply the existing normalization and cross-role overlap check. Reject every other shape before normalization. Cover the same schema at all trust-config consumption seams.

3. status: not_applicable can satisfy required priority evidence (schema)

closeout-evidence-replay --require-priority-dispositions rejects an empty status: not_applicable marker, but the same marker with a valid P1 finding returns SATISFIED.

Observed at the reconciled head:

overall_verdict: SATISFIED
priority verdict: SATISFIED
status: not_applicable
finding count: 1
missing: []
errors: []

Expected: the status and finding shape must be internally consistent. Required-mode priority evidence containing status: not_applicable must never produce SATISFIED, including when a syntactically valid finding is appended.

4. Fresh dispatch approval persists stale refresh provenance (script)

When a persisted dispatch-decision-refresh is followed by a fresh dispatch-decision, selection uses the new authority but history_resolution retains the old refresh resolution.

Observed:

selected status: selected
selected authority: {dispatch: false, route: true}
persisted action: dispatch-decision-refresh
persisted decision id: refresh-before-dispatch
replay without transient operator_decision: blocked-user-input

Expected: the fresh dispatch decision supersedes the prior refresh resolution, persists its decision id/action/updated authority, and replays to the same selected assignment without transient operator input.

5. Explicit GH_HOST does not verify the resolved repository URL host (script)

pr-security-preflight requests both nameWithOwner and url, but its explicit-GH_HOST branch validates only the slug and then assigns the expected host without parsing the returned URL. A same-slug response from a different host can therefore be accepted and misclassify repository-local trust state.

Expected: canonicalize and compare both the returned slug and URL host with the requested repository and explicit GH_HOST; fail closed on either mismatch. Add a same-slug/different-host regression.

6. Completed-audit refs and OUTSTANDING comma fallback are ambiguous (schema)

The completed-batch-audit v1 grammar reserves ; and | inside record refs but permits commas, while findings: OUTSTANDING ... uses comma splitting as a fallback. A multi-ref findings value containing a valid comma-bearing record ref cannot be parsed into a unique blocker identity.

Expected: make the grammar unambiguous everywhere it is documented, parsed, mirrored, and tested. One acceptable mechanism is reserving commas in refs; another is removing comma fallback in favor of an unambiguous delimiter. Preserve canonical ref normalization and blocker-union completeness.

Acceptance coverage

  • Unknown, missing, null, and non-object readiness rows fail closed; existing pass, skip, pending, fail, and cancel behavior remains covered.
  • Trust config accepts only legacy nonempty scalar strings or arrays of nonempty strings and rejects malformed/mixed shapes before normalization.
  • Required priority evidence cannot be satisfied by status: not_applicable, with or without findings.
  • Refresh-then-dispatch persists the fresh approval and restart replay succeeds without transient operator state.
  • Explicit-host security preflight rejects same-slug payloads whose repository URL resolves to another host.
  • Completed-audit parsing has one documented and tested, non-ambiguous ref grammar across pack copies.
  • Each regression is demonstrated red before the fix and green after it against the immutable pack source.

Mechanism disposition

  • Targets: script for readiness, dispatcher persistence, and host verification; schema for trust roles, priority evidence consistency, and completed-audit refs.
  • Motivating miss: machine-checkable readiness, trust, replay, and blocker identities currently accept unknown, contradictory, stale, or ambiguous state.
  • Replay evidence: React on Rails PR #4749 review threads and the concrete reproductions above at head 04c1b8149e4bf6f7ccb8edd41f8e478d78bdfcc6.
  • Non-goals: consumer-only overlays; changing pull-request trust boundaries; pull_request_target; optional final-candidate edge nits concerning absolute-path npm invocation, pathological post-SIGKILL reaping, or mutable documentation links.

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

    P1High priority: verified material impact; schedule ahead of speculative improvementscomplexity:neutralBounded repair, tests, docs or evidence with little net structural change. Not merge approval.follow-upFollow-up work split from another issue or pull requesttriage:drain-firstResolve the existing PR or concrete blocker before starting more work. Not merge approval.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions