Imported from microsoft/amplifier-work-tracker (
AGENTS.md). Install upstream withnpx skills add microsoft/amplifier-work-tracker. Copyright stays with the author.
AGENTS.md
For contributors changing this repo. (For agents using the bundle at
runtime, see bundle.md, skills/, and agents/ instead -- this file is
the other audience: the one editing the code.)
main is protected
Direct pushes are rejected -- work goes through a PR, and merges are
squash only (linear history required). Full ruleset:
.github/branch-protection-ruleset.json,
maintainer notes in CONTRIBUTING.md. One point worth
repeating here: required_approving_review_count is 0, not 1-with-a-bypass,
because a bypass actor bypasses the entire ruleset -- including required
status checks -- which would let the bypasser skip CI too. With 0 and no
bypass actors, the gate (PR, CI green, linear history, no force-push) applies
to everyone, including a solo maintainer merging their own PR.
The adapter seam
All bd-specific knowledge -- CLI shape, field names, exit-code quirks --
lives in src/amplifier_work_tracker/adapter.py and nowhere else. If
doctor reports a violated assumption, the fix scope is that one file.
Nothing above the seam should ever need to change for a Beads upgrade.
Project identity and registration repair
Workspace.create must pass an explicit database name when adopting an existing
project, preserving its server identity and other clients' registrations.
repair-registration is explicit local recovery, never automatic identity-guard
bypass: validate the effective endpoint, mapping, IDs and witness before writing,
and recheck both metadata bytes and permissions under the repair lock.
Keep replacement/rollback atomic and preserve the exact backup and mode.
If post-write verification fails, return the observed final metadata state and
separate verification from rollback failure/refusal; never infer either state
without readback.
Run test_identity_preservation.py and test_repair_registration.py using the
isolated-server fixtures; never test a repair against the shared server.
doctor is the gate, not a suggestion
Run amplifier-work-tracker doctor after any bd upgrade and before
trusting parallel agents against a queue. It must report 38/38
assumptions hold; anything less means Beads' behavior moved out from
under an assumption we depend on (or, for sweeps.alive, that the
reap/notify sweep loops have stopped completing sweeps, or, for
sweeps.reclaiming, that the reap loop is still turning but its last
sweep FAILED on one or more projects -- alive and not reclaiming are
different states, and sweeps.alive alone cannot tell them apart, or, for
project.removal, that remove/new no longer honestly handle a
database that outlives its project directory, or, for
service.restart_policy, that the installed unit's Restart= line has
regressed away from always -- see the 2026-08-14 outage note in
supervisor.py's DoltSupervisionExhaustedError -- or, for
systemd.user_bus_reachable, that this process cannot reach the systemd
--user session bus at all, commonly because XDG_RUNTIME_DIR was never
inherited by a session spawned outside a login session (tmux, ssh, an
agent spawn) -- see service.py's _systemd_user_env/
diagnose_systemd_failure docstrings). (The count was previously
misreported here as 26/26 while the CLI actually emitted 27 -- 23 from
contract.run_all plus 4 service/dolt checks; systemd.user_bus_reachable
is the 5th service-level check added alongside this reconciliation, for
28 total. This branch's six reopen/resolve assumptions bring the
merged total to 34, which is MEASURED from doctor on the merged tree --
not 33+2 arithmetic, which would have said 35. The two
defer/block.refuses_resolved assumptions -- the fence on the
destructive-reopen defect, model_performance-2nx -- then take it to
36, again MEASURED from doctor, not computed. read.unavailable_not_absent
(model_performance-8zv) makes it 37 -- measured on the rebased branch, not
computed from 36+1. sweeps.reclaiming (model_performance-oy4) makes it
38 -- again MEASURED by running doctor on this branch, not computed;
the run is committed at
docs/lanes/oy4-dead-holder-reclaim/evidence/doctor-measured.txt.)
Test scope
The browser tier intentionally refreshes its tracked LAST_RUN.json; preserve
and review that new measurement rather than discarding it to satisfy a source
hash check. Its Linux/aarch64 fixture uses direct Chromium child startup because
the pinned shell's zygotes can crash before rendering; the browser pin and all
rendered assertions remain unchanged.
Root CI (.github/workflows/ci.yml) runs tests/unit, tests/integration
(marker integration), and tests/cli (marker cli) -- see the Makefile
for the per-tier targets. modules/tool-work-tracker/tests/ is a separate
package with its own suite and is NOT exercised by root CI -- a green root
CI run does not cover it; run it directly if you touch that module.
Tests run against an ISOLATED dolt server, never the shared one
Both suites (tests/conftest.py and
modules/tool-work-tracker/tests/conftest.py) spin up a throwaway dolt sql-server on its own ephemeral port for the whole session (autouse,
session-scoped isolated_dolt_server fixture; the shared logic lives in
tests/_dolt_isolation.py) and repoint every dolt host/port pointer this
repo reads at it. A test run can no longer create a database on the
shared, permanent server at ~/.beads/shared-server:3308 -- structurally,
not by teardown discipline.
Why this exists: fixture-level teardown (below) is Python code that runs
after a test/fixture body, so a kill -9, an impatient timeout wrapper
escalating to SIGKILL, or a hard crash skips it entirely and leaves that
run's databases on the shared server forever. Measured on a live box: 202
residue databases, enough on their own to make bd init (a CREATE DATABASE under the hood) time out at 240s server-wide -- for real
projects too. Isolation closes that gap: a killed run leaves at worst an
orphaned dolt sql-server process and a /tmp directory, never growth on
the server every real project also lives on.
A session-scoped assert_isolated_server_clean fixture in both suites is
the final backstop: at session end it queries the isolated server directly
(not any fixture's bookkeeping) and fails loudly, naming and dropping
anything left -- on this per-session server, every non-system database is
test residue by construction, so this catches a leak even from a fixture
that never went through any of the safe helpers below at all.
A project lives in two places -- clean up both
Creating a project creates a directory and a database on the dolt server
it was pointed at (the isolated one, per the section above). A tmp_path
root only cleans up the first one. Skipping the second was measured on a
live box at 163 databases for 5 real projects: 157 of them residue, 47
from doctor runs alone. dolt holds every database open, so the bill
arrives continuously -- dropping the residue took that server from 1.15 GB
RSS / 313 MB on disk to 0.12 GB / 18 MB. Isolation (above) means that bill
can no longer land on the shared server, but it still matters within a
session: an untidy fixture bloats the isolated server's own disk/RSS for
the rest of a long run, and (per the safety net above) still fails the
session.
Unique names (see tests/conftest.py) are what keep concurrent runs from
colliding. They are not cleanup. So:
- Any fixture that creates a project must drop it again. Root suite:
drop_projectintests/conftest.py. Module suite: the sharedprojectfixture inmodules/tool-work-tracker/tests/conftest.py(setPROJECT_PREFIXin your test module to name it). - Teardown belongs in the fixture, not at the end of the test body -- the end of a test body does not run when the test fails, and a failing test is exactly when residue gets left behind.
- Removal goes through
adapter.drop_database/Workspace.remove, never raw SQL. Teardown uses the former becauseremoverefuses (correctly) while an item is HELD, and several tests deliberately hold one. tests/integration/test_no_database_residue.pypins this, and the session-scopedassert_no_leaked_projectsfixture fails the run if any project a fixture handed out is still there at the end.
For residue an older run already left on a server (e.g. the shared production one, from a run that predates the isolation fix above):
python scripts/sweep_test_residue.py # dry run: names every database, drops none
python scripts/sweep_test_residue.py --confirmed # actually drop them
python scripts/sweep_test_residue.py --patterns # what counts as residue
It only matches fixture-minted names (a prefix from this repo's suites plus
a machine-generated suffix), reports everything else as PROTECTED, and
refuses any database that still has HELD items. It is deliberately not
wired into CI, doctor, or any install path -- a destructive command that
runs itself is how you lose data you meant to keep.
Shutdown and stop proof
A supervisor stop is not proved by the immediate command parent exiting: retain
and drain its dedicated process group so pipe-less descendants cannot survive.
A service stop is not proved by systemctl stop returning zero either; bound
both the stop request and readback, and require observed clean state before
reporting success. Interrupted sweeps must not write a completed heartbeat.
If shutdown interrupts an alarm after reclaim has committed, log the item and
holder once and state that HTTP acceptance/delivery is unknown; do not infer an
alarm outcome or undo/retry the reclaim.
Do not leave an owned child behind an unbounded executor proc.wait: use a
bounded shutdown drain, then KILL/reap only the recorded owned child if TERM
does not finish it. Forced child termination must return nonzero even on the
ordinary signal path: successful reaping does not make a forced database stop
clean.
What "done" looks like
Full suite green, doctor 38/38, ruff check / ruff format --check /
pyright clean. For any change to the bundle's zero-state install path
(service bootstrap, work_tracker_install, prereqs), the acceptance gate is
a fresh Digital Twin Universe run from a genuinely empty machine (no bd,
no dolt, no state) showing zero raw bd calls in the transcript --
only bundle tools. That bar, and the four-run trajectory that reached it,
is recorded in the PR #2 description and its commit history
(fd7d371..e322c6d); there is no separate proof doc in this repo. Re-run
it before merging any change that touches that path.