feat(worker): persist trigger attribution on the run record (issue #54) - #165
Merged
Conversation
A forge run's record could join to the triggers.json entry that fired
it only by the flow name heuristic, which two triggers naming one flow
defeat. The receiver has computed matched: { index, type, ... } since
issue #49, and it survives on job.data.trigger all the way into
recordRun's argument; buildRecord simply never read it.
buildRecord now persists triggerIndex and triggerType as explicit
nullable literals beside the replica fields whose admissibility
argument they reuse: an integer and a fixed enum, nothing
attacker-chosen. The third matched key (label/phrase/action) stays out
on purpose, because a label that satisfied an any predicate is
collaborator-applied payload text. Cron records keep null for both:
their attribution is already exact via the repeat:<id>:<millis> jobId
join, which also reaches records written before these fields existed,
where a new field cannot.
Specs: INT-RUN-HISTORY-FILE-CONTRACT AMENDED (schema, Why, Acceptance:
index 0 persists as 0, never null; the matched label never persists).
INT-CONTAINER-JOB-INPUTS AMENDED (cross-reference only; matched stays
event.json-only inside the container). INT-WEBHOOK-PAYLOAD-SUBSET and
INT-OUTBOX-CONTRACT UNCHANGED, checked.
Suite run in the CI posture (REQUIRE gates, live Valkey): 2073 pass,
0 skipped.
Signed-off-by: Rob Boerman <robboerman@live.nl>
This was referenced Aug 11, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
First slice of issue #54 (Gap 2: github runs can't be attributed to their trigger).
What
buildRecordpersists two new additive, nullable fields,triggerIndexandtriggerType, read from the job's owndata.trigger.matchedas explicit no-spread literals beside the replica fields whose admissibility argument they reuse.triggerIndexis the rawtriggers.jsonarray position of the entry that fired (cron entries counted, the file index is the rule's identity). Index0persists as0, nevernull, and a test pins that.triggerTypeis that entry'son.type, a fixed enum (label|comment|pull_request).matchedkey (label/phrase/action) is deliberately NOT persisted: a label that satisfied ananypredicate is collaborator-applied payload text, and the record's PII-free property rests on holding no attacker-chosen string. The end-to-end wiring test asserts the serialized bytes carry index and type but never the label.nullfor both on purpose: cron attribution is already exact via therepeat:<id>:<millis>jobId join, which also reaches records written before these fields existed, where a new field cannot.matchedhas been minted by the receiver since issue #49 and survives onjob.data.triggerall the way intorecordRun's argument; persisting it was deferred there by that issue's no-new-record-fields scope, not by the record's posture. Issue #54's graph is the consumer that makes it earn its place.Specs (same PR)
INT-RUN-HISTORY-FILE-CONTRACTAMENDED: schema block, Why prose, Acceptance.INT-CONTAINER-JOB-INPUTSAMENDED: one cross-reference sentence;matchedstays event.json-only inside the container and never enters the prompt.INT-WEBHOOK-PAYLOAD-SUBSETUNCHANGED, checked (matchedis harness-computed, not a payload field).INT-OUTBOX-CONTRACTUNCHANGED, checked (chained children carry no trigger and record null/null, as manual runs do).Tests
Suite run in the CI posture (
PI_DISPATCH_REQUIRE_{LOADER,WORKER,RECEIVER}_TESTS=1, live Valkey): 2073 pass, 0 skipped. New: present case, 0-not-null pin, closed-route-set iteration, cron-shaped null case, matched-object-never-stored, PII sweeps, and the end-to-end serialized-bytes assertions.