Records port: the Factory's own run records, unidentified Runs and the cache #192
Copy link
Copy link
Open
Description
Part of the epic "Epic: slice 1, first approval. A Release on next reviews a pull request to this repo, and the machine user's approval stands".
Why. The second half of the Kernel's input. P-17 and P-22 showed how run records appear (#161, decision 0035), and the port is where that decision's adapters/ rows land.
Done when
-
adapters/records.pylists the Factory's own workflow's runs on the default branch started by a schedule or a dispatch (I-34), reads each withCache-Control: no-cacheand keeps the response'sdateandagefor the pass record (0035 ruling 4), parsesrun-name/1into the record's item, Intent, Role,content_idandrun, and reports a counted uncompleted record still under the workflow's name as unidentified after one more read 5 s later (0035 rulings 1 and 3). - A list read short (1,000 runs, or a 200 with no data) is unreadable (L-53), and
outcome.jsonis read only for a Run that did not succeed (decision 0029's row). - The contract test replays
trials/P-17.jsonandtrials/P-22.jsonthrough it and matches the fake'sa_queued_run_is_listed,a_new_run_is_listed_lateanda_new_run_is_listed_unnamed. - Every row waiting on
adapters/that this holds is re-pointed;tools/check_context.pypasses.
Needs. The facts port's PR (shared adapters/ rules and test harness).
Design. §3.10; §9.1 item 8; decisions 0003, 0029, 0035; L-53.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels
Activity
don-petry commentedon Oct 21, 2026
Working rulings (overturnable in review)
Solution Architect, delegated. On main at ab53e4f. Read from:
outcome/1read only then), 0038's row (the records port parsesrun-name/1), and 0003's row (a record counts only if it is the Factory's own workflow, on the default branch, started by a schedule or a dispatch);trials/P-17.jsonandtrials/P-22.json(the run list read as it came:cache-control: private, max-age=60, no request sentno-cache, noage, and every run namedsf-trial …, none inrun-name/1's form);schemas/run-record.schema.jsonandschemas/run-name.schema.json;tools/check_trials.py'sHEADERS, which has noage;a_queued_run_is_listed,a_new_run_is_listed_lateanda_new_run_is_listed_unnamed;What this story decides. What the second half of the Kernel's input is, read from GitHub: the Factory's run records, the ones still unnamed, and how stale the read was. The rules are already decided in 0036, and this story builds them.
1. One pull request, after the facts port's first. It reuses
adapters/github.py's transport and its replay twin, and addsadapters/records.py. It re-points every row that namesadapters/records.pyunder the facts port's ruling 4: 0003's row, 0029's, 0036's, 0039's outcome row, and 0038's parse. Why: the transport is the one place that reaches the network (#191, ruling 3), so the records port waits for it and adds nothing to it.2. The list.
scheduleandworkflow_dispatch(0003's row, I-34).Cache-Control: no-cacheon every read, and allowIf-None-Match.dateandagefor the pass record. Neither reaches the Kernel, and a non-zeroageis a stale read (0036, ruling 4).complete.runsfalse: atotal_countabove what the pages returned, a 1,000th record with more behind it, or a 200 whose body has no runs where the count says there are some (L-53).3. The name and the unidentified window.
run-name/1is parsed by a function inadapters/records.pyinto the record's item, Intent, Role,content_idandrun. It is held by unit tests onexamples/run-name.jsonand every counter-example of it, since no fixture holds a real Factory run-name. The first canary run the Controller names will, and its fixture then joins the contract test.workflow_dispatchof a Role's workflow, with an item in its input and a name not yet inrun-name/1's form, completed or not, is held for one more read 5 s later. It is reported as unidentified by its GitHub id only if still unnamed then (0036, rulings 1 and 3).4. The outcome.
outcome/1is read from a Run's artifact only for a Run whose conclusion is notsuccess(0029, 0039). It is read as a zip through the same transport, from the Actions artifacts API, with its size bounded and its one member named. No fixture holds an artifact read, so that test is@pendingand its row says "not held yet". It is held when the Controller's first canary Run records one.5. The fixtures. P-17 and P-22 are replayed for the list's shape, the late listing and the unnamed window, and matched to the three grounding rules.
tools/check_trials.py'sHEADERSgainsage,dateand the request'scache-control, so a fixture recorded by the port's own reads can keep them. Neither trial is re-run.6. Tests and mutants.
tests/test_adapters.pygains the records port's cases. Each rule gets a mutant naming its test, checked by the capped filtered run onadapters/records.py.7. Record. None: 0036 decided all of it. Rows only.
8. Gates. Export verify; a fresh Claude review and a security pass, which reads the zip read first. AGY on Flash. "Muse: not run". The capped filtered mutant run.
9. Out of scope. Reading
applied.jsonfor a thread's category, which is the facts port's PR 2 and uses this port's artifact read. Dispatching a Run, which is the Controller's.Needs the Maintainer? No.
don-petry commentedon Oct 21, 2026
Reopened: #268's body said "It closes #192 except for the outcome read", and GitHub read the keyword. #268 built the records port (merged at c90bdc4), but the outcome read is still owed. A Run's
outcome.jsonis an artifact, which GitHub serves by a 302 offapi.github.com. Decision 0044's transport refuses that redirect, so the read waits on a decision to allow that one host, and then onadapters/artifacts.py. Until then, a list holding a failed Run is not whole. The remaining box is decision 0029's row, "The port reads the outcome of a Run that did not succeed", which is waiting onadapters/artifacts.py.don-petry commentedon Oct 21, 2026
The Maintainer's answer (2026-10-21, in chat to the orchestration session): allow the one redirect. Decision 0044's transport may follow the artifact download's 302 only: from the artifact-download endpoint, only to GitHub's own artifact storage host, over HTTPS, with no auth header forwarded and the body size bounded; a row on 0044 and counter-examples in tools/check_repo.py. Built in adapters/artifacts.py, which completes this story's outcome read.