vpn: the sentinel's cron monitor is the ATTENTION GATE, not the digest (kanban nix-config#946) #1974

Merged
hermes merged 1 commit from kb/NCFG-946-sentinel-attention-gate into main 2026-10-13 15:24:33 -06:00
Collaborator

Kanban card nix-config#946 (worker session t_4ea665df). The private half is
nix-config-private#705, merged as 9cac5324; this PR pins that rev and flips the sentinel's
cron monitor onto the new entry point, so the two land together (a --gate flag the pin does not
have would break the monitor on every tick, and a monitor that never changes its text would stop
firing passes at all).

What

hosts/vpn/kanboard-sentinel's monitor_script was kanboard/sync.sh — sentinel.py --apply,
whose stdout is the WHOLE per-card digest. Hermes runs the monitor every tick, hashes its stdout
byte for byte and invokes the agent only when that hash changes, so every card move anywhere (a
worker finishing a card, a new capture, a Done card crossing an age bucket) bought a full agent
pass: measured 2026-10-13, 48 passes in 24 h over a board whose only ATTENTION rows were
holds no agent action can resolve.

  • new entry point kanboard/gate.sh (sentinel.py --apply --gate), installed as a real file
    by the hermes-kanboard-scripts activation beside sync.sh/digest.sh, and declared as the
    job's monitor_script. Same mechanical write-backs; the text is the ATTENTION GATE: one
    byte-stable kanboard: no attention while nothing needs an agent, the actionable rows when
    something does. sync.sh stays installed for a hand-run of the full-digest monitor.
  • the sentinel prompt says the pass is invoked by the gate (not by a clock tick), and adds
    rule 13: the card lifecycle move is the WORKER's (Review on PR open, Verify after merge,
    Done with evidence), with the sentinel's scripted arms as the fallback only.

Verification

  • test-kanban-sentinel-prompt.sh green (the pass-budget contract it pins is intact, and the
    new rule is additive), test-kanban-cron-scripts.py green (converge still gates declared
    script paths), test-kanban-cron-profiles.sh green.
  • nix-instantiate --parse hosts/vpn/default.nix OK.
  • The gate itself is pinned in the private repo: .forgejo/tests/test-kanboard-attention-gate.sh
    (26 scenarios, 6 mutation arms) — a steady board byte-identical across renders and across an
    age-bucket rollover with no bucket/age= anywhere, a startable card + a capture + an
    unconfirmed close each flipping it, every ATTENTION verdict either a HOLD or an action, and
    no verdict outside ATTENTION escalating.
  • Live read-only on vpn against the real board, same walk as the monitor: kanboard: no attention, 3.8 s wall (the full digest on the same board is 4.7 s).
  • Post-merge: the converged vpn cron store (monitor_script=kanboard/gate.sh), the installed
    gate.sh/sentinel.py bytes against this rev, zero agent runs over a measured steady window
    read from the job's own output docs, and one induced event passing within a tick.
Kanban card nix-config#946 (worker session t_4ea665df). The private half is **nix-config-private#705**, merged as `9cac5324`; this PR pins that rev and flips the sentinel's cron monitor onto the new entry point, so the two land together (a `--gate` flag the pin does not have would break the monitor on every tick, and a monitor that never changes its text would stop firing passes at all). ## What `hosts/vpn/kanboard-sentinel`'s `monitor_script` was `kanboard/sync.sh` — `sentinel.py --apply`, whose stdout is the WHOLE per-card digest. Hermes runs the monitor every tick, hashes its stdout byte for byte and invokes the agent only when that hash changes, so every card move anywhere (a worker finishing a card, a new capture, a Done card crossing an age bucket) bought a full agent pass: **measured 2026-10-13, 48 passes in 24 h** over a board whose only `ATTENTION` rows were holds no agent action can resolve. - **new entry point `kanboard/gate.sh`** (`sentinel.py --apply --gate`), installed as a real file by the `hermes-kanboard-scripts` activation beside `sync.sh`/`digest.sh`, and declared as the job's `monitor_script`. Same mechanical write-backs; the text is the ATTENTION GATE: one byte-stable `kanboard: no attention` while nothing needs an agent, the actionable rows when something does. `sync.sh` stays installed for a hand-run of the full-digest monitor. - **the sentinel prompt** says the pass is invoked by the gate (not by a clock tick), and adds rule 13: the card lifecycle move is the **WORKER's** (`Review` on PR open, `Verify` after merge, `Done` with `evidence`), with the sentinel's scripted arms as the fallback only. ## Verification - `test-kanban-sentinel-prompt.sh` green (the pass-budget contract it pins is intact, and the new rule is additive), `test-kanban-cron-scripts.py` green (converge still gates declared script paths), `test-kanban-cron-profiles.sh` green. - `nix-instantiate --parse hosts/vpn/default.nix` OK. - The gate itself is pinned in the private repo: `.forgejo/tests/test-kanboard-attention-gate.sh` (26 scenarios, 6 mutation arms) — a steady board byte-identical across renders and across an age-bucket rollover with no bucket/`age=` anywhere, a startable card + a capture + an unconfirmed close each flipping it, every `ATTENTION` verdict either a HOLD or an action, and no verdict outside `ATTENTION` escalating. - Live read-only on vpn against the real board, same walk as the monitor: `kanboard: no attention`, 3.8 s wall (the full digest on the same board is 4.7 s). - **Post-merge**: the converged vpn cron store (`monitor_script=kanboard/gate.sh`), the installed `gate.sh`/`sentinel.py` bytes against this rev, zero agent runs over a measured steady window read from the job's own output docs, and one induced event passing within a tick.
vpn: the sentinel's cron monitor is the ATTENTION GATE, not the digest (kanban nix-config#946)
All checks were successful
PR base guard / Base drift / conflict check (pull_request) Successful in 7s
NixOS Build Check / Scope changed deployable hosts (pull_request) Successful in 16s
Secret Scan / gitleaks scan (pull_request) Successful in 13s
NixOS Build Check / Flake input regression guard (pull_request) Successful in 5s
foremerge intent (advisory) all 1 changed path(s) covered by a live foremerge intent
NixOS Build Check / Hermes runtime gates (one eval of vpn's runtime) (pull_request) Successful in 1m3s
NixOS Build Check / Worker-scope reaper worker signature (mutation-tested) (pull_request) Successful in 3s
NixOS Build Check / Kanban pool capacity knob (runtime override + clamp, mutation-tested) (pull_request) Successful in 2s
NixOS Build Check / Sentinel pass budget contract (prompt forbids the full board walk, mutation-tested) (pull_request) Successful in 3s
NixOS Build Check / CI-scratch reaper class table (mutation-tested) (pull_request) Successful in 3s
NixOS Build Check / Repo gates (the folded selftest battery) (pull_request) Successful in 2m52s
NixOS Build Check / Scheduled foremerge reap command line (mutation-tested) (pull_request) Successful in 5s
NixOS Build Check / foremerge accept-order contract (real binary) (pull_request) Successful in 1m14s
NixOS Build Check / Build changed host closures (native, one eval) (pull_request) Successful in 3m54s
NixOS Build Check / .forgejo/tests python suites (hermetic) (pull_request) Successful in 28s
4cc77254fe
hosts/vpn/kanboard-sentinel's monitor_script was kanboard/sync.sh, whose stdout is the
WHOLE per-card digest; Hermes hashes that text and fires the agent when it changes, so
every card move anywhere bought a full pass - measured 2026-10-13, 48 passes in 24 h on
a board whose only ATTENTION rows were holds no agent action can resolve.

- a new entry point kanboard/gate.sh (= sentinel.py --apply --gate): the same mechanical
  write-backs, rendered as the gate. A steady board prints one byte-stable line and NO
  model call; a real event still wakes a full pass on that tick.
- sync.sh stays installed for a hand-run of the full-digest monitor.
- the sentinel prompt says the pass is invoked by the gate, and that the card lifecycle
  move is the WORKER's (rule 13) with the scripted arms only as the fallback.
hermes merged commit b7a10b3eb3 into main 2026-10-13 15:24:33 -06:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
ymrtech/nix-config!1974
No description provided.