Skip to content

docs(agents): trim the always-loaded contract from 569 to 472 lines - #2522

Open
IanQiu979 wants to merge 3 commits into
kunchenguid:mainfrom
IanQiu979:fm/firstmate-agents-md-retrim-r1
Open

docs(agents): trim the always-loaded contract from 569 to 472 lines#2522
IanQiu979 wants to merge 3 commits into
kunchenguid:mainfrom
IanQiu979:fm/firstmate-agents-md-retrim-r1

Conversation

@IanQiu979

Copy link
Copy Markdown

Why

AGENTS.md is firstmate's always-loaded contract: every session of every fleet member pays its token cost on every turn, whether or not that session ever hits the situation a given line describes. It has grown to 569 lines, largely by accumulating conditional and reference detail inline instead of routing it to the owner that already documents it - exactly the failure mode firstmate-coding-guidelines exists to prevent.

This applies that skill's knowledge-placement decision tree and one-owner rule to the current file, taking it to 472 lines. It is the same restructure as 039747a (483 -> 393), redone against current main.

039747a never reached this repo. It sat unpushed on a local main that was 163 commits behind origin/main, and because /updatefirstmate is fast-forward-only, that one local commit silently blocked every self-update for 25 days. Landing the restructure here is what ends that.

What this is not

Documentation only. No behavior, contract, capability, or rule substance changes. No script, test, or workflow is touched, and CLAUDE.md remains the canonical @AGENTS.md pointer file.

There was deliberately no target line count. Nothing was cut to hit a number - only content with a verified owner elsewhere was routed, and the verification for each removal is recorded in the commit body.

What moved

  • Section 2 - the 78-line annotated state//config//data/ tree became a compact top-level map plus pointers. docs/configuration.md's "Operational home layout and state" section states outright that it is the single owner of the top-level layout and that producer script headers own exact child-file fields; every removed entry was traced to it, to CONTRIBUTING.md/README.md (metadata.internal), or to a named producing script (fm-check-register, fm-pr-poll, fm-pr-check-migrate, fm-classify-lib, fm-decision-hold, fm-procevent, fm-startup-network, fm-wake-lib, fm-pending-reply-lib, fm-spawn, fm-teardown) before removal.
  • Section 3 - the step-by-step mechanism narrative and its positional ordering phrases, which bin/fm-session-start.sh's header already owns as this very section states in its first sentence.
  • Section 4 - the quota-array selection procedure, routed to quota-array-dispatch, which declares itself its single owner.
  • Sections 1, 3-11, 13 - adjacent single-purpose sentences joined into one sentence each. Clause joins only; no substance dropped.

What deliberately did not move: section 9's rewrite table

039747a's consolidate-adjacent-sentences method would merge section 9's 11-row rewrite table into prose, and it did exactly that. This PR deliberately does not, and the deviation is the point.

That table is a lookup surface, not prose. An agent consults it mid-response, one term at a time, while rewriting an internal label out of a captain-facing message - not once at session start like the rest of the file. An agent scanning for a single term needs rows; compressing a lookup table into a semicolon-chained sentence optimises the wrong thing. Ten lines is cheap insurance against leaking internal jargon to the captain, a rule enforced on every message all session long.

039747a's method is right for prose and wrong for a table. Please do not re-merge it.

That reasoning is now recorded in AGENTS.md's own ## Maintaining this file section rather than only here, so it travels with the file and lands in front of whoever next runs a consolidation pass over it.

Restoring the table also restored the "do not expose" sentence above it verbatim. The merged draft had quietly added adapter to that prohibition list - a new rule, which a documentation-only restructure must not introduce, by the same reasoning that governs the two restorations below.

What was deliberately kept, not dropped

Three facts had no other owner. Removing them would have been a real loss, so they stayed inline:

  1. The never touch watcher / Claude and Cursor turn-end / sub-supervisor / wake-recovery internals class, kept as one sentence worded to be exactly as strict as the per-entry lines it replaces and no stricter. Delete-safety for every other state/ record now routes to that record's producing script rather than being asserted either way, so no permission was silently removed.
  2. Registered process-to-event sources keep supervision required even with no fleet work. This only ever appeared in the section 2 map; it moved to section 8, its real owner, beside the identical Relay rule.
  3. The watcher's custom-check execution boundary - only trusted repository scripts and hash-validated registered checks execute, and every other state check is rejected without running - which is documented nowhere else. It was folded into section 7's existing fm-check-register.sh rule, at its actionable point.

Section 4's hard "never"s were preserved inline for the same reason: never infer a credential store, provider family, or quota mapping from a name; never launch another harness's CLI to judge a candidate; missing evidence is disclosed uncertainty, never a credential or login escalation; only concrete contradictory evidence blocks. quota-array-dispatch itself names AGENTS.md section 4 as the owner of five things - intake boundary, load trigger, malformed-config refusal, every-candidate accounting, and the strongest-reasoning/tie rules - and all five remain inline.

Two substance changes the review caught, both reverted

The gate's review flagged two places where a "prose tightening" had quietly changed the contract. Both were put back:

  1. Relay may require had become Relay ... require. A modal verb is substance: may require is a permission, require is an obligation. It reads back exactly as before, and the process-to-event fact that moved here from section 2 keeps its own original strength rather than the two sharing one flattened verb.
  2. The bootstrap detect-only checks lost their always run guarantee. That clause is the contrast with the very next sentence's mutating sweeps, which run only under the lock; without it a session could infer that a lock-refused start skips detection entirely. Restored.

Both are the same class of error: a restructure that must not change substance had changed it in prose that looked like condensation.

Safety inventory

The commit body carries a section-by-section inventory naming every hard rule, refusal condition, fail-closed behaviour, and skill trigger and marking it PRESENT, in the same shape as 039747a's. If an item could not honestly be marked PRESENT, something was removed that needed putting back.

Relationship to #2520

#2520 is open against this same file and adds a two-sentence note that the directory map is a navigational excerpt rather than an inventory, and that an absent entry never proves a state/ file is foreign or safe to remove. That note is directly load-bearing for this restructure, so it is reproduced verbatim here rather than left to a rebase conflict where it could be lost. None of #2520's other seven fixes are touched.

Merge-order caution: because both branches add that same note, whichever lands second needs a quick check that it ends up present exactly once - not duplicated, not dropped. Everything else in the two branches is disjoint.

Verification

  • bin/fm-doc-audience-check.sh passes (surfaces=68 local_links=251).
  • No section was renumbered, so every AGENTS.md section N cross-reference from docs/, .agents/skills/, CONTRIBUTING.md, README.md, and bin/ script headers still resolves. Each was checked, and each referenced rule still exists.
  • CLAUDE.md is unchanged and still the canonical @AGENTS.md pointer file, not a symlink.
  • AGENTS.md is the only changed file across all three commits.
  • The full no-mistakes pipeline passed on this branch: review, test, document, and lint all clean. Push/PR/CI ran outside it because this repo is pull-only for the author, so the branch is delivered from a fork.

Commits

  1. The restructure itself.
  2. Three restorations after review: the may require modality, the always run guarantee, and section 9's bulleted table.
  3. The one-line note in ## Maintaining this file recording why that table stays a list.

Documentation-only restructure of AGENTS.md, firstmate's always-loaded core
contract. No behavior, contract, capability, or rule substance changed -
detail was routed to its existing owner and prose was consolidated per the
firstmate-coding-guidelines knowledge-placement decision tree and one-owner
rule. This re-applies the method of 039747a (483 -> 393) to current main,
which has since grown to 569 lines.

What moved / tightened:
- Section 2: replaced the 78-line annotated directory tree (an exhaustive
  restatement of per-file fields, config semantics, and inheritance rules)
  with a compact top-level tree plus pointers. Verified every removed
  semantic against its owner before removing it: docs/configuration.md's
  "Operational home layout and state" section states outright that it is the
  single owner of the top-level layout and that producer script headers own
  exact child-file fields, and it documents crew-harness, secondmate-harness
  ("not inherited because secondmates do not launch secondmates"),
  crew-dispatch.json, backlog-backend, backend, calm, startup-memory-budget,
  herdr-presentation-spaces, trace-context, cmux-socket-password, wedge-alarm,
  x-mode.env, captain.md/captain-shared.md, learnings.md, secondmates.md,
  procevent, and public-followup. metadata.internal=true is owned by
  CONTRIBUTING.md and README.md. Every remaining state/ artifact was traced
  to a producing script (fm-check-register, fm-pr-poll, fm-pr-check-migrate,
  fm-classify-lib, fm-decision-hold, fm-procevent, fm-startup-network,
  fm-wake-lib, fm-pending-reply-lib, fm-spawn, fm-teardown).
- Section 2: two facts had NO other owner and were kept, not dropped. The
  "never touch watcher / Claude and Cursor turn-end / sub-supervisor /
  wake-recovery internals" class stays inline as one sentence, worded to be
  exactly as strict as the per-entry lines it replaces and no stricter -
  delete-safety for every other state/ record now routes to that record's
  producing script rather than being asserted either way. "Registered
  process-to-event sources keep supervision required" moved to section 8,
  its actual owner, beside the identical Relay rule.
- Section 3: compressed the step 1-7 mechanism narrative and its positional
  ordering phrases, which bin/fm-session-start.sh's header already owns as
  stated in this section's own first sentence. Kept the run-once rule, the
  lock-refused read-only boundary, the trust-the-digest-once rule, the ABSENT
  semantics, the NETWORK CHECKS "treat none as passed until it lands" rule,
  the wake-durability-until-acknowledgement rule, the OPEN DECISIONS and
  UNREAD STATUS reconciliation duties, and the liveness-read-vs-current-state
  distinction.
- Section 4: routed the quota-array selection procedure to
  quota-array-dispatch, which declares itself the single owner of it and
  declares AGENTS.md section 4 the owner of the intake boundary, load
  trigger, malformed-config refusal, every-candidate accounting, and
  strongest-reasoning/tie rules - all five kept inline. Every hard "never"
  in the removed lines was preserved inline: never infer a credential store,
  provider family, or quota mapping from a name; never launch another
  harness's CLI to judge a candidate; missing evidence is disclosed
  uncertainty, never a credential or login escalation; only concrete
  contradictory evidence blocks.
- Section 7: folded the watcher's custom-check security model (only trusted
  repository scripts and hash-validated registered checks execute; every
  other state check is rejected without running) into the existing
  fm-check-register.sh rule, its actionable point, since no doc owns it.
- Section 9: merged the duplicated "do not expose" term list and the 11-row
  rewrite table into the boundary plus one compressed rewrite guide; every
  term->plain-English mapping is preserved, and the "adapter" term was added
  to the do-not-expose list to match the rewrite guide.
- Sections 1,3,4,5,6,7,8,10,11,12,13: joined adjacent single-purpose
  sentences into one sentence each (clause joins, no substance dropped).

Sequencing: PR kunchenguid#2520 also edits AGENTS.md, adding a two-sentence note that
the directory map is a navigational excerpt rather than an inventory and
that an absent entry never proves a state/ file is foreign or safe to
remove. That note is directly load-bearing for this restructure, so it is
reproduced verbatim here rather than left to a rebase conflict. Nothing else
from kunchenguid#2520 is touched.

Safety-inventory audit (every item still present inline or as an inline
stub+trigger; none lost):
- Section 1 hard rules 1-5 (never write to project + its exceptions and the
  concrete captain-approved operation boundary; never merge without captain
  word; never tear down unlanded work; crewmates never address captain;
  report faithfully) + shared-tracked-material list + no agent co-author:
  PRESENT.
- Section 2: fm-send fails closed unless FM_HOME explicit; never touch
  watcher/turn-end/sub-supervisor/wake-recovery internals; producing script
  decides edit/delete safety for every other state record; absent map entry
  is not proof a state file is foreign; status = wake event not current
  state; captain/shared-captain/learnings canonical regardless of harness
  memory: PRESENT.
- Section 3: run once; do not reimplement its components; lock-refused
  read-only boundary (no spawn/steer/merge/drain/repair/mutate); read-once
  and read-the-persisted-file rules; ABSENT semantics; mutating sweeps only
  when locked; wake records durable until the generation-bound
  acknowledgement; OPEN DECISIONS and UNREAD STATUS reconciliation;
  network checks not passed until they land; bootstrap consent + do not
  dispatch until tools and GH auth are good; bootstrap-diagnostics trigger:
  PRESENT.
- Section 4: harness-adapters trigger; verified harnesses only / never
  unverified / never launch one named by static config; every-candidate
  accounting; malformed config is an error, not something to select around;
  strongest-reasoning class preserved; ties broken without array-order bias;
  never infer a credential store or provider family; never launch another
  harness's CLI; uncertainty is not a credential escalation;
  quota-array-dispatch trigger; never max effort without captain; backend
  blocker never silently retried; per-spawn --backend is never later-task
  precedent: PRESENT.
- Section 5: reconcile only this home's own reports / never claim another
  home's work; stuck-crewmate-recovery, secondmate-provisioning, and /afk
  triggers; restart is a non-event: PRESENT.
- Section 6: project-management and secondmate-provisioning triggers; no
  unmentioned remote / removal preflight and unlanded-work checks; secondmate
  idle by default / no self-directed sweep / no child-tree reconstruction;
  knowledge-routing list; firstmate never writes a project AGENTS.md; /stow
  trigger and its scope limit: PRESENT.
- Section 7: evidence != authorization; diagnostic-reasoning trigger;
  delivery mode and yolo resolved at intake and passed explicitly;
  no-mistakes-prod-only surface rule; unregistered project defaults to
  no-mistakes with yolo off; serialization criteria; spawn worktree-isolation
  stop; fm-send is data plane only / fm-control owns lifecycle; no-mistakes
  owns its own pipeline / no manual gate / no stacked reviews; yolo authority
  split and its expansion boundary; ask-user-authority trigger; implementation
  worker never answers its own finding; never merge a red PR and yolo cannot
  authorize one; fm-pr-merge / fm-merge-local only; firstmate never runs axi
  respond for a crew-owned run; single supported abort and validate-once
  against the final head; forbid --yes; custom check.sh contract plus the
  watcher's execution boundary; teardown only after landing / refusal is
  stop-and-investigate / never force without discard authority; secondmate
  retirement safety; decision-hold-lifecycle trigger; promotion carries only
  intended fix changes and turns a reproduced bug into the regression test:
  PRESENT.
- Section 8: one live cycle / no other harness's wait shape / no shell & / no
  second cycle; Relay and registered process-event sources require the cycle
  with no fleet work; no turn ends blind; drain the queue before other work;
  acknowledge only after handling; wake-type handling list; never broadly
  kill watchers (sibling homes) and forced repair uses the home-scoped owner
  path; guard warnings do not replace the contract; spawn + brief isolation;
  turn-end guards are backstops not a substitute; full Away-mode stub
  (FM_INJECT_MARK + U+2063 marker format; daemon owns supervision; marked
  message stays internal; unmarked message means the captain returned and its
  catch-up gate must clear; no approval-authority expansion; bias to exit);
  stuck-crewmate-recovery trigger: PRESENT.
- Section 9: talk-in-outcomes; captain's nouns; never expose internal terms;
  all 11 rewrite mappings; scout and second mate stay untranslated; never
  relay verbatim; private-report exemption; evidence-first escalation form;
  reach-captain-immediately list; "Captain, shipshape." reply rule; PR full
  https URL: PRESENT.
- Section 10: work items only / secondmates never backlog items / routed work
  lives in that home's backlog; captain-gated hold command;
  decision-hold-lifecycle trigger; update on every dispatch, completion, and
  decision; note-hygiene and handoff-safety owners: PRESENT.
- Section 11: fm-brief owns the scaffold; every {TASK} replaced before
  dispatch; ship-brief isolation assertion; firstmate-coding-guidelines
  trigger; --herdr-lab guard and regenerate-rather-than-hand-edit rule;
  secondmate-provisioning charter trigger; sparse status appends; scaffold is
  a safety contract: PRESENT.
- Section 12: only AGENTS.md/bin/.agents/skills are loaded; /updatefirstmate
  trigger; never touches projects/: PRESENT.
- Section 13: all 14 agent-only skill load triggers, including the
  never-run-a-source's-blocking-command-yourself rule: PRESENT.
- Section 14: inert until FMX_PAIRING_TOKEN; token is not destructive
  authority; Relay-only home still needs the live cycle; fmx-respond
  triggers; promised final reply is durable state and only the owning home
  posts it: PRESENT.
- Captain instruction precedence and "Maintaining this file": PRESENT,
  unchanged.

Verification: bin/fm-doc-audience-check.sh passes (surfaces=68,
local_links=251); CLAUDE.md remains the canonical @AGENTS.md pointer file;
no other file changed; every cross-reference from docs/, .agents/skills/,
CONTRIBUTING.md, README.md, and bin/ headers into an AGENTS.md section still
resolves, since no section was renumbered and each referenced rule survives.
Section 9's term-rewrite table is a lookup surface: an agent consults it
mid-response, one term at a time, while rewriting an internal label out of a
captain-facing message. It is not prose read once at session start like the
rest of the file, so an agent scanning for a single term needs rows rather
than a semicolon-chained sentence. Ten lines is cheap insurance against
leaking internal jargon to the captain, a rule enforced on every message.

Commit 039747a's consolidate-adjacent-sentences method merged that table into
prose. That method is right for prose and wrong for a table, so this note goes
in the file's own "Maintaining this file" section rather than in a commit
body: it travels with the file and lands in front of whoever next runs a
consolidation pass over it.
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