Skip to content

fdev: board-tend user units, post-merge trigger, empty enable file and 7-day observation (Refs #5136) - #5205

Merged
EHotwagner merged 2 commits into
mainfrom
routine/5136-pr8-fdev-tend-units
Nov 1, 2026
Merged

EHotwagner merged 2 commits into
mainfrom
routine/5136-pr8-fdev-tend-units

Conversation

@EHotwagner

@EHotwagner EHotwagner commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

Refs #5136

Phase 2 PR 8 of the board reconciler design (docs/coordination/2026-10-31-board-reconciler-design.md §8 and §12 PR 8): the fdev user units, the post-merge trigger and the empty enable file, then 7 days plan-only. This PR installs nothing, enables nothing and writes nothing to the board. The install, the first run and the enable edits are separate Root admissions; the operating note docs/coordination/board-tend-fdev-units.md has the commands, the readback and the observation plan.

What is in it

  • Units (deployment/fdev-board-tend/*.in, rendered by the installer): fsgg-board-tend.service and .timer (hourly at :07, Persistent=true, 120 s random delay), and a local read-only fsgg-board-tend-daily.service and .timer (00:30 UTC) that writes the daily observation record.
    • Environment=FSGG_KIT_ROOT=<installed kit root> and a fixed, non-checkout WorkingDirectory (the coord: board-v2 tend reads the board-tend contract marker from the installed contract (Refs #5174) #5188 F1 scope note); the wrapper refuses to start in any other directory or with an unset, relative or kit-less root.
    • RuntimeMaxSec=900 with Type=exec (the cap is ignored on a oneshot service); XDG_RUNTIME_DIR=%t pinned and RuntimeDirectory=fsgg kept, so the engine's flock lock …/fsgg/board-writer-PVT_kwDOEYAWY84Bldpa.lock is always satisfiable; deliberately no PrivateTmp, because the writers also take the legacy lock at the literal /tmp path.
    • --apply is never on the command line. Apply is a later install.py units --mode apply --force, which refuses unless the enable file already names a rule.
  • Wrapper (fsgg-board-tend-run.py): scrubbed child environment (no GITHUB_TOKEN, no TMPDIR; GH_TOKEN from the 0600 credentials file), output in a 0700 directory with 0600 files, the pending-post-merge marker rules (a marker bypasses the 5-minute minimum interval, is cleared only by a run that started after it was written and exited 0, and is re-run when rewritten mid-run), and the summarize subcommand for the daily record.
  • Installer (install.py): units, kit-root and enable-file, each with --dry-run, refusing root and any path outside $HOME. kit-root writes the two released contract mirrors read-only after checking the released sha256 and exactly one fsgg:board-tend-contract/v1 marker; units pins the engine to its versioned path and refuses a kit root of another release.
  • Enable file: board-tend.json with "apply": [], "propose": false, "maxItemsPerRun": 100. No gated rule. The schema table is in the operating note.
  • Post-merge trigger (tools/routine-delivery.py): after a confirmed merge, only under --board-gate enforce (so only from the FS-GG/.github checkout), write the marker and run systemctl --user start --no-block fsgg-board-tend.service. Best effort, reported on stderr, absent from the result schema; no failure can change the merge outcome.
  • Docs: the operating note (pins, marker rules, enable-file schema, install, the 7-day plan and the per-rule enable criteria); the currency-gate design names the trigger; the writer census rebinds the helper's digest and states the third effect.

Fixtures: clock, source and sandbox

  • tests/fdev-board-tend: a fake $HOME in a temp directory, a fake engine script that records its argv and environment, the real wall clock for run-directory stamps, no network, no credentials, no systemd. systemd-analyze --user verify runs on the rendered units only where it exists.
  • BoardTendTriggerTests: an injected run callable and a temp state directory; no subprocess.
  • Local evidence outside the fixtures (not a test): the real installed engine 0.114.2 run through the wrapper under env -i, plan-only, into a scratch directory: board-v2 tend: plan-only; cards=279 apply=0 wouldApply=8 close=0 wouldClose=0 deferred=8 propose=19 refuse=1; written=0 noop=0 refused=0 uncertain=0; requests=12 graphqlPoints=12 rest=0; seconds=21.8, exit 0. --mode apply with the empty enable file printed the enable file lists no rules; plan-only.

Tests

  • bash tests/fdev-board-tend/run.sh: Ran 34 tests … OK.
  • python3 -I tests/skill-quality/routine-delivery.py: Ran 268 tests … OK (9 new in BoardTendTriggerTests).
  • python3 -I tests/skill-quality/routine-delivery-board-mutate.py: 73/73 mutants killed (8 new).
  • check-v1-writer-census --structural: PASS (74 source identities, 339 tracked executables). check-prose-citations: ok.

🤖 Generated with Claude Code

rev and others added 2 commits November 1, 2026 16:08
…d 7-day observation (Refs #5136)

Phase 2 PR 8 of the board reconciler design (section 8, section 12 PR 8). Plan-only by default; nothing is
installed or enabled by this change.

- deployment/fdev-board-tend: service and timer templates for `board-v2 tend` (hourly at :07) and a daily
  observation record; a wrapper that pins FSGG_KIT_ROOT and the fixed WorkingDirectory, scrubs the engine's
  environment, keeps output in a private directory and honours the pending-post-merge marker (never swallowed,
  cleared only by a run that started after it); an installer with units, kit-root and enable-file subcommands,
  each dry-runnable, user paths only; the empty enable file.
- tools/routine-delivery.py: after a confirmed merge in enforce mode, write the marker and start the unit with
  --no-block. Best effort, stderr only, result schema unchanged.
- tests: tests/fdev-board-tend (34) and the selftest workflow; BoardTendTriggerTests (9) in
  tests/skill-quality/routine-delivery.py, 8 new mutants in routine-delivery-board-mutate.py.
- docs: board-tend-fdev-units.md (pins, marker rules, enable-file schema, install, 7-day observation and the
  per-rule enable criteria); the currency-gate design names the trigger; the writer census rebinds the helper.

Refs #5136

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…; fixture isolates XDG_CONFIG_HOME (Refs #5136)

CI on e8afa0f: gates failed check-engine-flag-narrative (the retired --engine flag name, 16 findings in the new
files) and the fdev-board-tend fixture failed on a runner that sets XDG_CONFIG_HOME outside the fake HOME. The
option is now --engine-path (and --timeout-seconds), and the fixture pins XDG_CONFIG_HOME inside its fake HOME.

Refs #5136

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@EHotwagner

Copy link
Copy Markdown
Contributor Author

Root/fdev: independent review APPROVED e2c39d85.

  • Plan-only: the units cannot apply or close (no --apply; an empty enable file means plan-only; gated rules need the marker under the pinned kit root).
  • Wrapper: an allow-list environment, with the token never in argv or logs.
  • Writer lock: shared with the merge gate.
  • Post-merge trigger: best-effort and non-blocking.
  • Tests: 34, 268 and 73/73 mutants.

Triage: F1–F10 are non-blocking hardenings, follow-up #5206, due before any rule is enabled for apply. F11 and F12 are recorded; the real systemd behaviour is checked in Root's first-install readback.

The merge waits for Root's release-window hold (0.114.3). The install will use the 0.114.3 engine and kit root once published. Refs #5136

@EHotwagner
EHotwagner marked this pull request as ready for review November 1, 2026 16:44
@EHotwagner
EHotwagner merged commit 6082c1c into main Nov 1, 2026
62 checks passed
@EHotwagner
EHotwagner deleted the routine/5136-pr8-fdev-tend-units branch November 1, 2026 16:45
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