Skip to content

feat(upstream-watch): run the watch on GitHub Actions; routine only dispatches - #249

Merged
pacphi merged 16 commits into
mainfrom
feat/upstream-watch-actions
Sep 27, 2026
Merged

pacphi merged 16 commits into
mainfrom
feat/upstream-watch-actions

Conversation

@pacphi

@pacphi pacphi commented Sep 27, 2026

Copy link
Copy Markdown
Owner

Summary

Decision 14: the upstream watch runs as a scheduled GitHub Actions workflow in this repository; the cloud routine only dispatches. The routine's first run (session cse_01Xb8wcBL8h335pxUeQ9sbnQ) was blind — a cloud session reaches only attached repositories (HTTP 403 upstream), apt's gh 2.45 lacks --slurp, gh auth status called the injected token invalid — and the script still printed "No new upstream events."

  • Workflow .github/workflows/upstream-watch.yml: daily 0 14 * * * + workflow_dispatch (post switch). Job watch (issues: write) runs node scripts/upstream-watch.mjs comment --json, posts the body on Upstream watch #243 after checking it is non-empty and well formed, reads the posted length back, and re-applies the upstream-dispatch label while a released line carries branch=. A blind run (exit 3) fails the job. Job preview (read-only) runs on pull requests that touch the watch — this PR's run proves the workflow token reads the upstream threads.
  • comment subcommand (scripts/upstream-watch/ledger.mjs): reads only watchPolicy.ledger.authors comments, starts from the newest checked-at (future values ignored), drops recorded lines, renders a deterministic comment (text block + one sentence per line, ids in code spans, ≤60,000 chars), keeps the previous start when anything failed or was cut. No model.
  • Script fixes: no --slurp (--paginate --jq '.[]'); auth probe gh api rate_limit; check never reports a quiet day after a failed read; blind judged on upstream threads only.
  • Registry: watchPolicy.ledger.authors (pacphi, github-actions[bot]), separate from ours (which decides "our last word" upstream). Schema + validator.
  • Adversarial review (0 blocker / 3 major / 7 minor): unknown support-window floor now holds Ruflo-carried fixes instead of dispatching them; a released fix is re-signalled until dispatched and the routine reads all ledger authors' comments, skipping existing branches and non-pending entries; plus the minors.
  • Test harness: scripts/run-tests.mjs drops the shell's FORCE_COLOR (Claude Code sets 3), which coloured a pid in process-tree.test.mjs (orphaned wrapper → suite hang) and put a Node warning into about-security.test.mjs output. CI never set it.
  • Docs: UPSTREAM-WATCH.md (the check, the ledger, daily workflow, dispatch routine prompt), ADR-0041 §7 Updated, audit record decision 14 (4b-A..C) and open items, ubiquitous language, MAINTAINER.md, AGENTS.md.

Test plan

  • node scripts/run-tests.mjs unit — Node 26 5347/0, Node 22 green; tripwire: concurrent writers only
  • tsc, eslint, complexity ≤50, markdownlint, build-check, UI, doc guards, actionlint
  • Real read-only comment --json: 36 events, 0 fetch errors, not blind, no dispatch
  • CI incl. Windows and the preview job (token reads upstream)
  • After merge, with go-ahead: manual workflow_dispatch run (proves posting on locked Upstream watch #243); routine prompt + label trigger; re-enable routine

🤖 Generated with Claude Code

gh 2.45 (apt on Ubuntu 24.04) has no --slurp, so every thread read failed in
the cloud routine's first run. --paginate --jq '.[]' prints one comment per
line on every gh 2.x.
gh auth status called the cloud session's injected GH_TOKEN invalid while gh api
user worked with it; an Actions installation token cannot read /user either.
gh api rate_limit works with every token and fails without one.
The routine's first run printed "No new upstream events." although every read
had failed. check now names the unchecked threads instead, and check --json
reports blind when gh is unusable or no watched thread could be read.
watchPolicy.ledger.authors lists the logins whose ledger comments count: the
maintainer and the workflow's github-actions[bot]. watchPolicy.ours stays our
upstream logins, because it decides whose comment is our last word on a thread.
comment reads the ledger issue's comments by watchPolicy.ledger.authors only,
starts the check from the newest checked-at in them (else seven days ago), drops
lines already posted, and prints the comment: the lines in a text block ending
with checked-at (kept at the previous start when a read failed), then one fixed
sentence per line. It prints nothing on a quiet day, lists the dispatch branches
in --json, and exits 3 when blind, so the scheduled workflow needs no model.
Runs node scripts/upstream-watch.mjs comment at 14:00 UTC and on demand with
the workflow token, posts the script's body on the ledger issue after checking
it is non-empty and well formed, reads the posted length back, and re-applies
the upstream-dispatch label when a released line carries a dispatch branch. A
blind run (exit 3) fails the job. On a pull request that touches the watch, a
read-only preview job proves the token reads the upstream threads.
…patches

UPSTREAM-WATCH.md describes the comment command, the ledger authors, the daily
workflow and the dispatch routine with its label trigger and new prompt.
ADR-0041 §7 Updated; the audit record gains decision 14 (4b-A..C) and current
open items; MAINTAINER.md lists the new commands. The doc tests now read the
dispatch routine's prompt and pin the label the workflow applies.
A token scoped to the ledger's repository still reads our tracking issues
there while every upstream read fails; that run now counts as blind (exit 3)
instead of passing green. comment --json on an invalid registry prints the
same blind shape the workflow reads on every other failure.
…s unknown

A failed read of Ruflo's release dates left the support-window floor null, and
a null floor held nothing, so a Ruflo or AgentDB fix above the real floor got a
dispatch branch. With the workflow dispatching without a human, an unread floor
now holds every Ruflo-carried fix (waiting for the window, no branch=) and the
report marks the floor unknown. A registry with no window policy still holds
nothing. The bundling-chain test drops the window it could not compute.
The dispatch list came from new lines only and the routine read only the newest
comment, so a post whose label step failed, or a later maintainer comment, lost
the dispatch for good. comment --json now lists every released line with a
branch, recorded or not, so the workflow re-signals daily; the routine reads
all ledger authors' comments and skips a branch that exists or an entry main no
longer marks watching or fixed-unreleased.
Thread ids, pull request numbers and branches in the sentences are code spans,
so a bare #n no longer links to this repository and upstream threads get no
mention. A body past 60,000 characters posts the lines that fit and keeps the
previous checked-at, so the rest follow next run. A checked-at later than now
is ignored instead of silencing replies until that date.
…flow

The ubiquitous language names the workflow and the ledger authors; the audit's
Branch 4 open items drop a stale go-ahead line and record that Branch 3 adopted
ruvnet/ruflo#3167 and #3415 while #3194 waits for the window. UPSTREAM-WATCH.md
says who GitHub notifies about a failed scheduled run, that public repositories'
schedules pause after 60 idle days, and that a thread failing every day keeps
the check window growing.
…rphan it

Under FORCE_COLOR (set in Claude Code shells) console.log colours a number even
into a pipe, so the wrapper's pid parsed as NaN, the assertion ran before the
try block that kills the detached wrapper, and the live child kept the test
runner from exiting: the unit suite hung. The wrapper now writes the pid raw,
and the read sits inside the try so a failure still cleans up.
Claude Code shells set FORCE_COLOR=3. Node then colours console.log into pipes
and, beside NO_COLOR, prints a warning into captured output, so
about-security.test.mjs parsed a warning as JSON on both Node versions. Tests
read plain text; scripts/run-tests.mjs now removes FORCE_COLOR from the
suite's environment. CI never set it, so CI results do not change.
tsc --checkJs inferred the spread object's type without FORCE_COLOR, so
deleting it failed the typecheck (CI quality job).
@pacphi
pacphi merged commit 88e2699 into main Sep 27, 2026
30 of 31 checks passed
@pacphi
pacphi deleted the feat/upstream-watch-actions branch September 27, 2026 23:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant