agentleFS
musterd-iomusterd-io/musterd/.agents/skills/pre-registered-watch/SKILL.md

pre-registered-watch

Skill1 starsChanged 9 days agoMITView on GitHub

A measurement that takes days is a pre-registered question with an owner and a death date — not a recurring sweep nobody reads. States the falsifier, the population, and what voids its own window before collection starts; cannot be renewed in place; and "nobody looked" is a recorded outcome. Use when a claim is about a rate or a recurrence, when installing a cron job or a dashboard, or when a decision rests on a snapshot of something that moves.

We split this skill into the rules it gives an agent

2 rules. About 0 are common, 1 is distinctive, and 1 is very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈1

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 1

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Never a patrol, never a backlog-grooming pass.

    line 90
  2. Very specific:

    When --base is given but git cannot answer it — a ref that does not resolve, a shallow clone, no git at all — the run prints immutability …

    line 168

≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

pre-registered-watch

One of three thin instruments from dated-and-falsifiable. Each is a file convention plus a script of about two hundred lines; each works alone, on one machine, with no server. They share one rule: a record must be able to fail. If the check passes just as happily when the claim is wrong, it is a ritual, not an instrument.

Some questions cannot be answered at a moment. Is this flaky? Did the fix hold? Does that guard still catch anything? Each is a claim about a quantity that varies over time, and a single observation of a time-varying quantity is a claim about the conditions it ran under — not a general statement.

Most teams have exactly one primitive for that: install a recurring sweep, and hope someone reads it.

Nobody reads it

Measured on our own instrument, a sweep running every five minutes:

sweeps5,682
window24.8 days
size49.5 MB
observations323,682
times read0

The task asking someone to decide whether the instrument earns its keep sat open and unowned for 25 days. Two distinct failures live in that series, and the second is the one people do not expect.

One — the pre-registered target was breached, loudly, and nobody looked. The design pre-registered a particular event at target zero and called any instance a finding requiring inspection. It was 109. And the instrument was not silent about it: it set a non-zero exit code, wrote 214 alarm lines to its log, raised a report entry until it cleared, and fired an OS notification on repeats. The escalation path fired for 25 days and not one case was inspected.

An instrument that escalates into a channel with no owner is not observability. It is an alarm wired to a bell in an empty room.

Two — the rates in that series cannot be read at all. The number of distinct things sampled swung 23 → 196 → 9 inside the window. Any percentage over it spans three populations and means nothing. Twenty-five days and 49 MB of ambiguity, accumulated in silence.

A watch

One file, stating — before collection starts — the question, the falsifier that settles it, the population sampled, the conditions that disqualify its own window, where samples accumulate, who is accountable, and the date by which it must resolve.

---
question:   Does the new backoff let a retry storm reach the payments API again?
claim_ref:  SKILL.md
falsifier:  "Any 60-second window with more than 3 retries to /charge from one client id. Target ZERO. A count, not a rate: the client population is expected to grow inside this window, so any percentage over it would span two populations and mean nothing."
population: the 41 client ids present in the registry on 2026-09-21. A client added or removed inside the window voids this watch rather than silently changing the denominator.
void_if:
  - the set of client ids changes from the 41 present at opening
  - the backoff constants in src/retry.ts change
  - the series file is truncated or rotated within the window
series:     ~/telemetry/charge-retries.jsonl
cadence:    1m
opened:     2026-09-21
opened_by:  dolly
revisit_by: 2026-10-12
status:     open
---

(claim_ref points at this skill only so the shipped example resolves out of the box. In real use it is the decision, record or page the resolution posts back to.)

It ends resolved (a verdict) or void (the window was disqualified, or nobody looked). Both are terminal. Neither is silence.

The four rules that carry the weight

1. The decider opens the watch, in the same diff

Never a patrol, never a backlog-grooming pass. When a decision rests on a snapshot of something that moves, the person writing the decision opens the watch riding the act they are already performing. A watch somebody has to remember to open later is a watch that does not exist.

2. A watch cannot be renewed in place

revisit_by is immutable once merged. Continuing a question means a new file, a new question, a new diff someone reviews.

Renewal must cost a decision, because free renewal is the disease. The sweep above renewed itself 5,682 times, for nothing, because renewing was the default and stopping was the action.

3. void is an honest outcome, not a failure

A watch that expires unread resolves:

status:     void
resolution: "unattended -- revisit_by passed with nobody reading the series. No verdict."

That records that we failed to look, which is a real datum and precisely what an unread sweep hides. A team that cannot count its own unattended measurements has no idea how much of its instrumentation is theatre.

4. Prefer a target-zero count over a rate

A rate needs a stable denominator; a count with target zero does not.

Our sweep carried both over identical data and a population that swung 23 → 196 → 9. The count stayed readable and produced a finding. Every rate became uninterpretable. This choice costs nothing at authoring time and cannot be retrofitted after collection — which is exactly why it is a pre-registration rule and not analysis guidance.

The gate

./watches.py [watches_dir] [--base <git-ref>] [--root <repo-root>]

Exit 0 when every watch is well formed and none has outlived its date, 1 when one has, 2 when the directory cannot be read. Python 3.8+, standard library only — the frontmatter subset is scalars and block lists, parsed in the script rather than pulling in a YAML engine.

RuleScopeWhat it refuses
schematreeA missing field; a void_if with no conditions; a status outside the enum; a terminal watch with no verdict; a verdict on an open one; unreal or backwards dates; a claim_ref pointing nowhere.
A1 no watch outlives its datetreeAn open watch past its own revisit_by.
A2 revisit_by never movesdiffA renewal in place. Needs --base.

void_if needs at least one condition. A watch with no way to be void is claiming its population cannot change — which is the assumption that made the 49 MB series unreadable.

claim_ref must exist. It is the post-back target: a resolution has to land somewhere a reader already goes, or you have written rule 11 of the parent skill — a correction that is recorded but not routed.

A1 can break your build on a date rollover with no code change

Inherited deliberately from the sibling instrument controls-in-force, and for the same reason: a liveness check that cannot fail on its own is the disease. The pressure valve is one honest line — resolve it, or void it as unattended. Both leave a record.

The immutability check abstains rather than passing

--base is optional, and when it is absent the run says so:

ok watches: 1 total -- 1 open, 0 resolved, 0 void; none past its revisit_by
  ! immutability NOT checked -- pass --base <ref> to enforce it

When --base is given but git cannot answer it — a ref that does not resolve, a shallow clone, no git at all — the run prints immutability UNKNOWN ... Not a pass and still exits 0. A check that reports "clean" from its own outage is the exact failure this family is about, so it reports the outage instead.

This is not licence for background extraction

A watch is finite, question-scoped, and dies at its resolution. Rules 2 and 3 exist to make that structural rather than cultural. If you find yourself opening a successor every time one expires, the question is answered and you are running a sweep again — under a nicer name.

The falsifier, and the trap that turned up three times

Break each rule and watch it fire. Copy watches.example/, mutate one field, run the script on the copy. Thirteen refusals and four must-not-fire cases were exercised this way on 2026-09-21, including the git rules in a scratch repository with real commits:

MutationExpected
delete question / falsifierexit 1, "missing required field"
empty the void_if listexit 1, "needs at least one condition"
status: abandonedexit 1, "must be one of open | resolved | void"
status: resolved, no resolutionexit 1, "a terminal watch with no verdict"
add resolution to an open watchexit 1, "move the status, or drop the verdict"
revisit_by: 2026-02-31exit 1, "must be a real ISO date"
opened: 21/09/2026exit 1, same
revisit_by before openedexit 1, "must be after opened"
claim_ref pointing nowhereexit 1, "which does not exist"
an aged open watchexit 1, "open past its revisit_by"
a file with no frontmatterexit 1, "a watch is its pre-registration"
revisit_by moved, with --baseexit 1, "cannot be renewed in place"
an aged void watchexit 0 — terminal watches do not age
a new watch file, with --baseexit 0 — nothing to be immutable against
--base that does not resolveexit 0, "immutability UNKNOWN ... Not a pass"
no mutationexit 0
a directory that is not thereexit 2

Three of those rows did not fire on the first run, and none of them was a bug in the gate. Each was a fixture that could not construct the failure it was named for:

  • The void_if mutation used a regex that ate the closing ---, so the file had no frontmatter at all and was refused one rule earlier. The right refusal, for the wrong reason, which reads identically from the outside.
  • Both aged-watch rows set revisit_by into the past while leaving opened at today — so the schema rule fired first (revisit_by must be after opened) and A1 was never reached. One of them was even a must-not-fire case, which duly failed and looked like a real defect in A1.

This is the third time in three increments of this skill that the first falsifier run has been wrong about its own fixture rather than about the code. That rate is worth saying plainly: when a rule does not fire, the fixture is a likelier culprit than the rule, and the two are indistinguishable from the outside. Check the boring explanation before you file a bug against yourself.

The example's dates are real, so it will eventually go stale on purpose — revisit_by: 2026-10-12. That is the instrument working, on the one file where you can watch it happen without it being your problem.


From the musterd team — Your agents, on a team with you. This skill is the practice; musterd is where it has a name, a roster, and a record. Everything above works on one machine with no server; what a file cannot do is route an expiring watch to the person who owns it — opened_by is a name in frontmatter, not an address. musterd.io.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

Reviews can't be read right now.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.