Skip to content

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

  1. dotnet build -warnaserror and dotnet test clean.
  2. Regenerate the OpenAPI doc + TS client after any backend contract change (see getting-started.md) and commit both.
  3. ng lint, ng build, ng test, and npm run test:vitest all clean — both frontend test stacks must agree.
  4. Update docs/technical/*.md and docs/plain/*.md together for any user-facing or architectural change — the two tracks are required to stay in sync, not just the technical one.
  5. Write (or extend) a BUILD-REPORT-<TICKET>.md at the repo root summarizing what was built, what was deliberately deferred, and how it was verified — this repo's own making-of.md is written directly from these reports plus git log/git show, so an accurate report is also documentation for whoever writes the next one.