~ knotless Search/
↑↓ move↵ open⇧↵ all results

chadtmiller.com/knotless

git that lives in your Atmosphere account

knotless.dev
1 collaborator

knotless 0.13.0 Latest 2 packages, 1 image

1
10

run log shows what a check prints while it runs

Merged
Merged chadtmiller.com asked to merge into main from chadtmiller.com/knotless:run-log

From the command line or MCP you can now read what a check prints while it runs: knotless run log <url> --pull <uri> --workflow test prints what it printed so far and the step it is on, and --follow keeps printing until it ends; the MCP tool run_log gives the same, with since and wait. A new appview query, dev.knotless.getRunOutput, serves it to whoever may read the check. The serve handler and the client loop are the parts worth a look.

Claude Opus 5.5 · medium

What changes

dev.knotless.getRunOutput (new lexicon, served beside getCheck): what the appview holds of a running check, as /_live/check sends it to the pages, but as one JSON answer: text after cursor (all that is kept without one), cursor, steps that ended, going, ended. With nothing new and the run going it waits up to wait seconds (at most 25) for the runner to send more, holding a follower meanwhile, so the runner sends every second. It is checked as getCheck and /_live/check are (named_check): anyone for a public repository, members for a private one. The cursor is <startedAt>/<byte>, so a cursor from the last run of a check that was run again at the same commit reads the new run from its start rather than skipping into it.

knotless run log <url> [--ref] [--commit] [--pull] [--queue] [--workflow] [--follow]: the newest check the run list filters find (checks of several workflows are an error naming them). Running: what it printed so far, then the check's line as run list prints it, with \tstep <name> for the step going. Ended: its log, then its line. --follow waits up to a minute for a check to show, prints output as it comes (asking 25 s at a time), and once the run's steps are over waits for the record to be filed and prints its line. A private repository's output is read with a delegation token for its space.

MCP run_log: the same arguments, plus since (a cursor it gave) and wait (at most 300 s, for the check to show and then for more output). Running: {text, cursor, steps, going, ended, check}, text its last 8000 bytes; finished: {text, check}, text the end of its log. In READS, so read-only servers offer it.

run list's line is now made by one function (check_line) both commands share. Docs: cli.md, mcp.md, actions.md, api.md (Live), serve.md (Live), runner.md.

Why

run list --log and run_list read only the check record, which has no log until the run ends, so an owner or an agent watching CI could not see which step a running check was on or what it had printed. The pages already had it through /_live/check, a server-sent event stream; a query is simpler for a CLI and the only thing an MCP tool can use. Long-polling with wait keeps it close to live without a tight loop, and counting the waiting client as a follower keeps the runner's one-second cadence.

How it was checked

  • agents_follow_a_run (knotless-mcp): an appview, a running and a finished check filed as records, output sent with putRunOutput as the runner: several workflows refused, output so far with the step going, nothing more after the cursor (waited), then only what came after and ended, the finished check's log, none found after waiting, the queue filter, a bad commit, and no appview.
  • a_run_shows_as_it_runs (knotless-run): a real run held on its first step: run log shows the masked output and step first; --follow prints the second step and ends on success; afterwards the log; every flag parsed; a bad flag refused.
  • a_private_repository_runs_in_its_space (knotless-run, spaces alpha): a private repository's held run read through the delegation token, then followed to its end.
  • Left to CI: build, clippy, coverage, lexicon lint, answers against the lexicon.

Shortcomings

  • --follow on a run whose runner dies without filing its record keeps asking until interrupted (it rereads the record each quiet 25 s, so it does stop once anything files the check).
  • The appview keeps only the last megabyte of a run; a client further behind than that gets the kept tail, with no marker of the gap.

Review

Start at crates/knotless-serve/src/web/live.rs (get_run_output, had), then crates/knotless/src/checks.rs (newest, again, output) and run_log in cli.rs.

Approved

This adds dev.knotless.getRunOutput, a long-polling query that serves a running check's kept output. It also adds knotless run log with --follow, and the MCP tool run_log. check_line is now shared with run list.

I read the serve handler, checks.rs (newest, again, output) and run_log in cli.rs, and found nothing that must change.

  • Auth: named_check runs before anything is served, so the read check matches getCheck.
  • Wait: the handler caps wait at 25 s and re-reads the run after the wait.
  • Cursor: the <startedAt>/<byte> cursor is handled sensibly. A cursor from another run, or an empty one, reads from the start.
  • Client loop: the output and run_log loops terminate on ended, on a filed record, or when wait runs out. Re-reading the record on each quiet 25 s round is what ends a follow whose runner never filed.

I did not open the tests (mcp.rs, run.rs), the MCP wiring in knotless-mcp/src/lib.rs, the lexicon or the docs files, and I did not read the checks. The tests, lexicon lint and coverage are for CI to enforce.

One small thing, not worth holding the pull for: when the 1 MB tail is cut, or a byte cursor lands inside a multibyte character, from_utf8_lossy may print a replacement character. You already list the cut with no gap marker among the shortcomings.

Approved

I read what changed since the approved commit, and the approval stands.

  • Private reads: member now takes the reader from the new space::reader, which joined also uses. That gives the same session choice as joined, so the owner's session is used when the owner is logged in. The refactor looks behaviour-preserving.
  • Follow loop: it now sleeps for zero time and re-reads the record on every quiet round, not only after ended. This matches the intent. The record is read again each quiet 25 s round, which is what ends a follow whose runner never filed.
  • Ended before it was followed: a missing log now prints nothing instead of failing. That is correct.
  • MCP test: the wait of 27 s checks that a wait longer than the appview's 25 s cap is asked again for the rest. That is a useful case.

I found nothing that must change.

Merged as bc4a8ce