0028. BYO connect is fetch-to-local-workspace; answering stays offline¶
- Status: accepted
- Date: 2026-07-03
Context¶
Milestone 18 (spec 0117) makes Tessera usable on repositories we do not own:
tessera connect github <owner>/<repo> must give a design partner grounded,
provenance-complete RCA over their CI failures. ADR 0014 solved the
adjacent problem for our repo with a run-once dev script and a
committed snapshot — but a foreign snapshot cannot be committed (it is
not ours to redistribute, it would bloat the repo, and it would stale), and
a pilot cannot depend on hand-editing pinned run ids in a script. Meanwhile
the constraints that made ADR 0014 right still bind: clone-and-run stays
offline and key-free, CI never touches the network, and answering must be
deterministic over its inputs.
Two measured facts shape the design (probed 2026-07-03, spec 0117): GitHub
serves run/job/PR metadata of public repos anonymously, but returns 403
for log content without a token — and the log endpoints answer with a 302
to signed blob storage, where a naive HTTP client would re-send the
Authorization header.
Decision¶
Fetch writes a gitignored local workspace; answering reads only that.
tessera connect github <owner>/<repo> is the network moment: a bounded
fetch (recent runs, failed-run logs for the most recent failures, one page
of PRs — explicit caps, the most common three tunable by CLI flags)
normalized into the committed-snapshot format of ADR 0014 (runs/ + logs/,
plus a prs/ extra) under var/connect/<owner>-<repo>/,
with a MANIFEST.json pinning what was fetched (run ids, snapshot date,
caps, scrub counts, named misses) and a NOTICE naming the upstream.
tessera ask <owner>/<repo> "…" builds the graph from those files alone —
the same GitHubActionsSource, parameterized by directory, zero engine
change — so answers are deterministic over the snapshot and work with the
network unplugged. The workspace is never committed: the committed proof
of generality is a report (numbers + reproduce commands in specs/STATUS),
not foreign fixtures.
Auth is optional, minimal, and read-only. Metadata fetches run
anonymously by default. The optional classic no-scope GITHUB_TOKEN
from the gitignored .env raises rate limits and unlocks failed-run logs;
without it, connect degrades honestly to a metadata-only snapshot and says
so. The connector never reads TESSERA_GITHUB_TOKEN (the actuator's RW
credential stays off every read path), never persists or echoes the token,
and follows log redirects manually so the auth header is sent to
api.github.com and nowhere else.
Foreign text is scrubbed before it reaches disk. Named content patterns
(GitHub/AWS/Slack token shapes, Authorization: header values, sensitive
key=value assignments) replace matches with a visible marker; counts land
in the manifest. What the caps or format divergence exclude is a named
miss in the manifest and the connect output — the ADR 0014
preserve-the-divergence posture extended: misses are recorded, never
papered over.
Consequences¶
- Easier: a pilot is two commands on any public repo; clone-and-run, CI, and the six committed battery lines are untouched (no foreign data in the repo, no token in CI, no network at answer time).
- Easier: one snapshot format means one reader — the source module, locator kinds, RCA, verifier, and provenance rendering all apply to foreign repos with zero change; fixes benefit both corpora.
- Accepted cost: a workspace is a point-in-time copy that stales as the
upstream repo moves; the manifest's pinned ids + snapshot date make that
visible, and re-running
connectreplaces the snapshot atomically. - Accepted cost: recorded BYO numbers are timestamped measurements over data that expires (GitHub log retention ~90 days) — reproducible same-day, historical afterwards; the "ran on X" pattern the project already uses.
- Accepted cost: without a token the snapshot has no logs, so RCA grounds on run metadata only — a stated degradation, not a hidden one.
- Accepted cost: the unchanged DevEx RCA keys "recurring failure" on a
shared error signature, which on real logs is frequently the generic
Process completed with exit code N.trailer — a weak recurrence signal (the claim stays true and verifier-checked, but the label over-reaches on unrelated same-exit-code failures). Surfaced by the per-repo smoke battery and stated in the pilot runbook; the signature refinement is deferred named future work becausedevex/rca.pyis frozen this milestone. - Accepted cost: the snapshot's on-disk format is the committed corpus's
format plus a
prs/directory the committed corpus does not have (a workspace extra for the lexical path); the sharedruns/+logs/shape is what the unchanged source reads.
Alternatives considered¶
- Commit foreign snapshots like ADR 0014. Rejected: not ours to redistribute, unbounded repo growth, and instantly stale — and it would make the eval's committed corpus depend on someone else's CI.
- Fetch live at answer time. Rejected: breaks offline determinism and the key-free posture (the same reasons as ADR 0014, now with a token in the answer path — worse).
- Require the token and always fetch logs. Rejected: anonymous metadata-only mode is a real, honest pilot on rate-limited days, and "never required" is the posture the kickoff fixed.
- Use the run-logs zip (one request per run). Rejected: it bundles every job's logs — hundreds for a matrix project — where the per-job endpoint fetches only the ≤3 failed jobs the snapshot needs.
- A connector plugin framework. Rejected again (ADR 0014): two doors
(
connect github,ingest <dir>) still do not justify an abstraction.