Contributing¶
Practical conventions for working in this repo, distilled from how the last
several tickets (FLT-120, FLT-122, FLT-125, FLT-130/131) actually landed —
see making-of.md for the full build story each of those
came from.
Branch / worktree model¶
Every ticket is built in its own git worktree off origin/master, on a
branch named wt/WT-xxxx/<ticket> (worktree identity) or wt/WT-xxxx/<short
description> for docs/infra passes. Recent examples from this repo's own
history: wt/WT-6022/FLT-120, wt/WT-6022/FLT-122, wt/WT-6022/FLT-125,
wt/WT-6022/FLT-131. Work happens only inside the worktree; the main
checkout stays clean. Nothing merges to master without going through
review — see the build reports (BUILD-REPORT-FLT*.md) for the shape each
pass actually took: build, verify, write the report, land via a
fast-forward push once reviewed.
Scope discipline¶
Every build report in this repo ends with an explicit "scope discipline" or
"deviations" section — stating plainly what was touched, what was
deliberately left alone, and why. That habit is load-bearing for a fleet
where several tickets land in parallel worktrees against the same master:
FLT-122's report opens by naming the two tickets already on master it did
not touch or duplicate. Follow the same pattern: name what you didn't do,
not just what you did.
Contracts are frozen unless the report says otherwise¶
AgentRunRequest.Repo stays an absolute-path wire field even though
FLT-120 added RepoId/WorktreePath beside it, because narrowing it would
break the frozen run_agent contract every existing job payload depends on
(see AgentRunValidator.cs's own doc comments). The pattern across this
codebase: extend contracts additively (new nullable fields), never rename or
narrow a field a downstream listener already parses. If a wire shape truly
must change (the FLT-130/131 DispatchJob.SchemaVersion 1→2 bump is the one
exception in this repo's history), it needs to be coordinated with the
consuming side (fleet-dispatch) explicitly, not landed unilaterally.
Every new field a projection can't fill gets documented, not fabricated¶
Card's Model/Tier/GovClass/WtId/SessionId/LeaseId/SpendToDate/
TaintLevel fields exist in the contract with no data source yet
(Contracts/CardContracts.cs) — nullable, with a doc comment explaining
exactly why there's nothing to put there today. That's the standard: a field
the contract needs eventually is added now as documented-empty, never
guessed at or synthesized from an unrelated signal.
Security-sensitive changes go through a council gate¶
Several structural decisions in this repo — the file-queue transport over
HTTP for AgentRunDispatcher (FLT-56 SSRF-class risk), registry-only
pick-by-name with no live filesystem browser (FLT-120), the
CockpitSessionService loopback-only minting, the ArtifactStore hardening
(magic-byte sniffing, sandboxed CSP view, per-job upload tokens) — are
attributed to an explicit "council gate" or "council review" in code
comments and build reports. Any change that touches auth, signing, path
construction, or what an executing agent is allowed to write should look for
that same review, not just pass tests.
Tests mirror the pure/impure split¶
Pure logic (AgentRunValidator, CapabilityRouter, TierLadder,
AvailabilityGate, CardRegistry) is static and I/O-free specifically so it
is unit-testable without mocking the filesystem — new pure logic should stay
that shape. Services that do I/O (FleetDispatchReader,
RepoRegistryReader, ArtifactStore, AvailabilityWriter) are tested
against temp directories, one test file per service, matching the existing
tests/FleetMissionControl.Api.Tests/ layout.
Before you open a PR¶
dotnet build -warnaserroranddotnet testclean.- Regenerate the OpenAPI doc + TS client after any backend contract change
(see
getting-started.md) and commit both. ng lint,ng build,ng test, andnpm run test:vitestall clean — both frontend test stacks must agree.- Update
docs/technical/*.mdanddocs/plain/*.mdtogether for any user-facing or architectural change — the two tracks are required to stay in sync, not just the technical one. - Write (or extend) a
BUILD-REPORT-<TICKET>.mdat the repo root summarizing what was built, what was deliberately deferred, and how it was verified — this repo's ownmaking-of.mdis written directly from these reports plusgit log/git show, so an accurate report is also documentation for whoever writes the next one.