Context
The Fleet Manager MVP epic (#13015) is "define, start, observe the agent fleet." Define (the settings pane #13058) and start (lifecycle #13049 + spawn-time repo provisioning #13177) are built. Observe is half-built: FleetLifecycleService reports process state (status / listRunning), but nothing reports repo state. After #13177 each agent provisions its working repo at spawn — yet there is no fleet-wide read of whether each agent's repo is actually present, a valid checkout, conflicted, or not configured at all.
The Problem
Operating the fleet means knowing, per agent: is its working repo provisioned? Is the checkout healthy or is the path occupied by a conflicting / foreign occupant? Is the agent even wired to a repo yet? Today that requires manually deriving each path and stat-ing it. The settings pane (#13058) and the operator need a single read to render fleet repo health — the repo dimension of the "observe" pillar, the read-side mirror of #13177's provision-side.
The Architectural Reality
ai/services/fleet/inspectAgentRepo.mjs — classifies one repo's on-disk state read-only: {exists, isCheckout, state, provisioningAction}.
ai/services/fleet/deriveAgentRepoPath.mjs — the stable {managedRoot, agentId, repoSlug} → checkout path.
FleetRegistryService.listAgents() — enumerates agent definitions, secret-stripped via toPublic (keeps id + the free-form metadata, so metadata.repo = {cloneUrl, repoSlug} survives).
- Nothing composes these into a fleet-wide repo view. That aggregator is this ticket.
The Fix
ai/services/fleet/inspectFleetRepos.mjs (new, standalone — sibling to ensureAgentRepo): inspectFleetRepos({registry, managedRoot, inspect = inspectAgentRepo}) → maps registry.listAgents() to a per-agent repo-status array. For an agent with metadata.repo.repoSlug: derive the path → inspect({repoPath}) → {agentId, configured: true, repoSlug, repoPath, exists, isCheckout, state, provisioningAction}. For an agent without repo coordinates: {agentId, configured: false, …} with a state: 'unconfigured' marker — observability surfaces the whole fleet, never silently omitting un-wired agents. Read-only; inspect is an injectable seam (default-real) so the aggregation is unit-testable without the filesystem. managedRoot is a parameter (consistent with ensureAgentRepo / startAgentProvisioned), supplied by the eventual caller from FM config.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
inspectFleetRepos({registry, managedRoot, inspect}) (new) |
this ticket |
per-agent repo status over registry.listAgents() |
unconfigured agents reported, not omitted |
module JSDoc + spec |
inspectAgentRepo / deriveAgentRepoPath / listAgents verified on origin/dev |
registry.listAgents() (consumed) |
FleetRegistryService |
read the agent set incl. metadata.repo |
— |
existing JSDoc |
toPublic keeps ...rest incl. metadata (verified) |
Decision Record impact
none — aligned-with the #13015 MVP decomposition (the "observe" pillar's repo dimension).
Acceptance Criteria
Out of Scope
- The live caller / settings-pane wire that consumes this read (a later integration leaf; supplies
managedRoot from FM config).
- Any mutation / provisioning (read-only — provisioning is #13177's
startAgentProvisioned).
- Process / liveness observability (already exists:
status / listRunning).
- Deep git-health probes beyond
inspectAgentRepo's occupant classification.
Related
- Parent epic: #13015 (FM MVP — define, start, observe). The read-side mirror of #13177 (provision-side).
- Consumes: #13148 (
inspectAgentRepo), #13145 (deriveAgentRepoPath); reads metadata.repo (the #13177 convention).
- Consumer-to-be: the settings pane (#13058).
Live latest-open sweep: checked the live tracker + in-flight A2A claims immediately before filing; no equivalent (the only active claim is an unrelated marathon-benchmark lane).
Authored by Claude Opus 4.8 (Claude Code), @neo-opus-ada.
Origin Session ID: 4c598c8f-d8a7-4288-9420-e825a45d310e
Retrieval Hint: "Fleet Manager observe fleet repo status inspectFleetRepos listAgents"
Context
The Fleet Manager MVP epic (#13015) is "define, start, observe the agent fleet." Define (the settings pane #13058) and start (lifecycle #13049 + spawn-time repo provisioning #13177) are built. Observe is half-built:
FleetLifecycleServicereports process state (status/listRunning), but nothing reports repo state. After #13177 each agent provisions its working repo at spawn — yet there is no fleet-wide read of whether each agent's repo is actually present, a valid checkout, conflicted, or not configured at all.The Problem
Operating the fleet means knowing, per agent: is its working repo provisioned? Is the checkout healthy or is the path occupied by a conflicting / foreign occupant? Is the agent even wired to a repo yet? Today that requires manually deriving each path and stat-ing it. The settings pane (#13058) and the operator need a single read to render fleet repo health — the repo dimension of the "observe" pillar, the read-side mirror of #13177's provision-side.
The Architectural Reality
ai/services/fleet/inspectAgentRepo.mjs— classifies one repo's on-disk state read-only:{exists, isCheckout, state, provisioningAction}.ai/services/fleet/deriveAgentRepoPath.mjs— the stable{managedRoot, agentId, repoSlug}→ checkout path.FleetRegistryService.listAgents()— enumerates agent definitions, secret-stripped viatoPublic(keepsid+ the free-formmetadata, sometadata.repo = {cloneUrl, repoSlug}survives).The Fix
ai/services/fleet/inspectFleetRepos.mjs(new, standalone — sibling toensureAgentRepo):inspectFleetRepos({registry, managedRoot, inspect = inspectAgentRepo})→ mapsregistry.listAgents()to a per-agent repo-status array. For an agent withmetadata.repo.repoSlug: derive the path →inspect({repoPath})→{agentId, configured: true, repoSlug, repoPath, exists, isCheckout, state, provisioningAction}. For an agent without repo coordinates:{agentId, configured: false, …}with astate: 'unconfigured'marker — observability surfaces the whole fleet, never silently omitting un-wired agents. Read-only;inspectis an injectable seam (default-real) so the aggregation is unit-testable without the filesystem.managedRootis a parameter (consistent withensureAgentRepo/startAgentProvisioned), supplied by the eventual caller from FM config.Contract Ledger Matrix
inspectFleetRepos({registry, managedRoot, inspect})(new)registry.listAgents()inspectAgentRepo/deriveAgentRepoPath/listAgentsverified onorigin/devregistry.listAgents()(consumed)FleetRegistryServicemetadata.repotoPublickeeps...restincl.metadata(verified)Decision Record impact
none— aligned-with the #13015 MVP decomposition (the "observe" pillar's repo dimension).Acceptance Criteria
inspectFleetReposreturns one status entry per agent fromregistry.listAgents().repoPath+ theinspectclassification (configured: true).metadata.repois reported withconfigured: false+state: 'unconfigured'(not omitted).missing registry/managedRooteach throw a clear error.inspectseam is honored (injected stub drives the per-agent classification — no filesystem in the unit spec).test/playwright/unit/ai/inspectFleetRepos.spec.mjs, green via the custom unit config;node --check.Out of Scope
managedRootfrom FM config).startAgentProvisioned).status/listRunning).inspectAgentRepo's occupant classification.Related
inspectAgentRepo), #13145 (deriveAgentRepoPath); readsmetadata.repo(the #13177 convention).Live latest-open sweep: checked the live tracker + in-flight A2A claims immediately before filing; no equivalent (the only active claim is an unrelated marathon-benchmark lane).
Authored by Claude Opus 4.8 (Claude Code), @neo-opus-ada. Origin Session ID: 4c598c8f-d8a7-4288-9420-e825a45d310e Retrieval Hint: "Fleet Manager observe fleet repo status inspectFleetRepos listAgents"