Skip to content

loop --watch: print only what changed after the first pass (attention rule) #376

Closed

Description

@DanielNetzer

loop --watch: print only what changed after the first pass

Component: every file belongs to the lab Go module (manifest lab/go.mod).

Why: this adopts DeepSeek's super-agent attention rule: speak only when a person must act or can use a result, and stay quiet while nothing changes. The Lab UI already follows it, because its headline, nav badge and live region count only what needs you. forgecell loop --watch doesn't. Each pass, every 5 minutes by default, reprints the full report: every eligible Issue, its state, its Molecule and its reason, even when nothing changed since the last pass. A long-running watch buries the one line that matters.

Build (lab/internal/cli/loop.go, watchLoop and renderLoop):

  1. Pass 1 of a watch prints exactly what it prints today.

  2. Every later pass prints only the following:

    • Items: each item that is new since the previous completed pass, or whose rendered lines differ. The lines are the item line, its reason, and its recovery proposal and follow-ups. An item that dropped out of the eligible list since the previous pass gets one line, <repo>#<n> · no longer eligible (or <repo> <key> · no longer eligible for a keyed item).
    • Warnings and stops: a Warning: note or a Pass N stopped: line only when it was not printed on the previous pass.
    • Summary: one line in every case: Watch pass N: no change or Watch pass N: K change(s). When the NonProgress count is above zero, it adds · J Issue(s) need attention.
    • Header: the Watch pass N line and the Lab loop for … header are folded into that summary line, so an unchanged pass is exactly one line.
  3. A skipped pass (Pass skipped: <reason>) prints its line only when the reason differs from the previous pass's skip reason, or the previous pass was not skipped. Otherwise it prints Watch pass N: skipped (same reason).

  4. A pass that ended in an error is reported as today. The pass after it compares against the last completed pass.

  5. Unchanged:

    • every word of a one-shot forgecell loop and loop --once;
    • exit codes;
    • learn notices after a pass (they are results);
    • stderr messages;
    • the loop's behaviour.

    No new flag or setting: the full report of any Molecule stays one forgecell ledger <molecule> away.

  6. Docs: in forgecell --help (lab/internal/cli/cli.go, the loop paragraph) and docs/install.md (where --watch is described), add one sentence: after its first pass, a watch prints only what changed, plus a one-line summary.

Scope (files):

  • Edited: lab/internal/cli/loop.go, lab/internal/cli/cli.go (help text only), docs/install.md
  • New: lab/internal/cli/loop_watch_quiet_test.go
  • Existing _test.go files stay unchanged. If a base test pins the full output of a later watch pass, name it in the plan as an intended protected delta.

Tests (new file, using the existing watchWait seam and fake loop runs; no sleeps):

  • Pass 1 prints the full report.
  • An identical pass 2 prints exactly one line.
  • A pass where one item changes state prints only that item and a 1 change(s) summary.
  • A new item and an item that dropped out are both reported.
  • A repeated warning isn't reprinted.
  • A repeated skip reason prints the short line.
  • A pass error doesn't reset the comparison.
  • The exit code matches today's for the same sequence of passes.
  • One-shot loop --once output is byte-identical to today's.

Done when

  • go test -timeout 20m ./internal/cli/... passes from lab/.

Plan checks for this slice

Use one combined targeted go test -timeout 20m ./internal/cli/... check. Every plan check leaves reviewedAcceptance and independentProvenance empty.


Review checklist (read before coding)

The adversarial review checks these every time. Recent slices failed review on all of them, so build them in from the start:

  • Bounds: sizes, counts, time and total work are bounded, and hitting a bound fails safe (incomplete or truncated, never silently accepted).
  • Filesystem safety: paths cannot traverse. Use Lstat and O_NOFOLLOW, and check symlinks and ownership. Watch for TOCTOU races on shared dirs (/tmp). Only ever delete Forgecell-owned, marked paths.
  • Untrusted input: issue text, repository files, model and tool output, and ledgers can be malformed, huge or hostile. Validate them, never interpolate them into HTML or a shell, and never let them trigger retries or approvals.
  • No local leaks: no absolute local paths, environment variables or credentials in output, logs, the UI or ledgers.
  • Contract changes: a changed adapter invocation bumps the protocol, and approved digests still bind exactly what was shown.
  • Tests: deterministic. Use no wall-clock budgets (use readiness markers or seams), write nothing into the user's real temp or home, and cover the failure path as well as the happy path.

Plan checks (for intake)

Check category must be one of regression, candidate or setup. Use targeted package tests only as plain go test -timeout 20m commands (for example go test -timeout 20m ./internal/<pkg>/...; without -timeout, go test stops a package after 10 minutes, and cli and molecule exceed that on a loaded machine); the protected view adds -json itself when protected deltas are declared. The Formula baseline already runs the full suite once, so don't add another full go test ./... check, and don't repeat its go vet or gofmt checks. A plan may hold at most 16 checks in total, counting the 5 Formula baseline checks, so prefer one combined targeted command over one check per package.

  • Keep base tests compiling: the protected view runs the base _test.go files against your code. Never change the signature of an existing function, method or type that a base test file calls. Add a new function or field instead. A protected build failure can't be declared as an expected delta.
  • Keep base tests runnable: changed defaults or behaviour must not make a base test hang or panic. A package that aborts in the protected view hides every test after it, and nothing can be declared. Keep the old default when unconfigured and opt in to the new one.
  • Plan checks: the operator's visual or browser check is a manual step after delivery, never a plan check. Every check leaves reviewedAcceptance and independentProvenance empty.
  • Plan structure: new files need a parent component (in this repo, under lab/; a new root-level file blocks intake). A check's definitions must never include a file the change edits. A file the check only reads as data under test is not a definition.
  • Scope lists files, not directories: list every new file path, including each golden fixture under testdata/. A directory entry doesn't cover the files inside it, and the scope audit blocks them.

Activity

  1. DanielNetzer commented on Nov 4, 2026

    @DanielNetzer
    OwnerAuthor

    Fixed in #379.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions