Skip to content

BUILD-REPORT-FLT125 — ARTIFACT RUN

Worktree: fmc-FLT125 · branch wt/WT-6022/FLT-125 · base 5c415e6 (fleet-mission-control master).

What was built

Extended the existing FLT-19 "design run" capability-routing/dispatch/artifact pipeline to cover document/presentation generation as first-class artifact-run types, and renamed the user-facing concept from "DESIGN RUN" to "ARTIFACT RUN". The wire lane (CapabilityRouter.LaneName = "design", AgentRunDispatcher.SendDesign, DesignRunRequest/DesignJobPayload contract) was deliberately kept unchanged — only the user-facing label and the capability set grew, so no fleet-dispatch listener needs to change.

New capabilities: pptx, docx, xlsx, html, pdf

  • src/FleetMissionControl.Api/Services/CapabilityRouter.cs
  • Capabilities now includes all five new types.
  • AllowedModels routes them through the full model pool (same as figma/stitch — no hard model constraint, unlike claude-design/image), so they go through the normal tier-ladder + quota-aware Plan() path (routed, not blocked).
  • src/FleetMissionControl.Api/Services/DesignJobPayload.cs
  • New EngineByCapability map: pptx→python-pptx, docx→python-docx, xlsx→openpyxl, html→local-render, pdf→render+export (per ticket).
  • Per-capability instruction text added to Build(), each explicitly telling the dispatched agent to produce the file LOCALLY and upload it to the artifact store — and explicitly not to send it anywhere else (FLOOR compliance, see below).
  • DELIVERY section extended to document the OOXML/pdf content-types for the binary kind.

Artifact persistence — metadata {path, size, sha256, generatedAt, sourcePrompt, format}

  • src/FleetMissionControl.Api/Contracts/CapabilityModels.cs (ArtifactRecord): added Sha256, SourcePrompt, Format, Path. SizeBytes and CreatedAt already existed and fulfil size/generatedAt — kept their original names for wire compatibility with the existing FLT-16/FLT-19 viewer contract instead of renaming (see Deviations).
  • src/FleetMissionControl.Api/Services/ArtifactStore.cs (Save):
  • Computes Sha256 from the stored bytes (SHA256.HashData).
  • Path = the artifact's path relative to the artifact root ({id}.bin), never the absolute host path (would leak local filesystem layout).
  • Format = the capability string (pptx/docx/xlsx/html/pdf/figma/stitch/claude-design/image).
  • New sourcePrompt parameter, server-derived from DesignRunRegistry's context, never trusted from the uploading agent.
  • ContentTypeAllowlist["binary"] extended with the three OOXML mime types (pptx/docx/xlsx); pdf already fit the existing application/pdf entry. No new Kind or sniff branch needed — OOXML files are ZIP containers, same as the pre-existing zip/octet-stream binary handling (no magic-byte sniff for binary kind, matching the existing design).
  • src/FleetMissionControl.Api/Services/DesignRunRegistry.cs: DesignRunContext grew a Prompt field (default "" for source compatibility with the old 4-arg call sites) so the artifact upload endpoint can pull the original prompt from the trusted registry context rather than the uploading agent's say-so.

Run-stream chip (filename + preview + download)

  • src/FleetMissionControl.Api/Contracts/ApiModels.cs (RunSummary): added ArtifactFileName, ArtifactDownloadUrl alongside the existing ArtifactId/ArtifactViewUrl.
  • src/FleetMissionControl.Api/Services/ArtifactStore.cs (ArtifactLink): grew FileName/ DownloadUrl (both optional, default-compatible) so /api/runs enrichment and the RunUpdated hub push can carry them without a second meta read.
  • src/FleetMissionControl.Api/Endpoints/DesignEndpoints.cs and Program.cs: both the live hub push and the /api/runs GET enrichment now populate filename/download alongside view.
  • frontend/src/app/components/run-stream.component.ts: the existing "view design" chip is renamed "view {filename}" (falls back to "artifact"), and a new independent "⬇ {filename}" download chip renders whenever artifactDownloadUrl is present — this is the only chip pptx/docx/xlsx/pdf artifacts get, since they are disposition=download (no inline viewer). Same off-origin guard (/^\/api\/artifacts\/[^/]+\/download$/) as the existing view-chip guard.

UI rename: DESIGN RUN → ARTIFACT RUN

  • frontend/src/app/components/design-panel.component.ts: section header "Design run" → "Artifact run"; toast "Design run dispatched." → "Artifact run dispatched."; capability button row grew pptx/docx/xlsx/html/pdf (all enabled, unlike the disabled image button).
  • Matching spec updates in design-panel.component.spec.ts and run-stream.component.spec.ts (button list, toast text, chip label, plus new tests for the download chip).

Jira attach — FLOORED, TODO left explicit

Per the ticket's FLOOR ("producing the LOCAL file = NO gate; SENDING externally = FLOORED — do not implement any external send"), attaching the artifact to the Jira ticket would be an external network call to Atlassian and was not implemented. There is also no existing attach path in this codebase — JiraBoardService is read-only (GetBoardAsync). A TODO(FLT-125-followup) comment is left in DesignEndpoints.cs at the artifact-upload handler, next to the note that the artifact stays local-only and is surfaced via the run-stream chip instead.

Build + test output

dotnet build -warnaserror
...
Build succeeded.
    0 Warning(s)
    0 Error(s)

dotnet test
...
Passed!  - Failed:     0, Passed:   361, Skipped:     0, Total:   361, Duration: 3 s - FleetMissionControl.Api.Tests.dll (net10.0)

Backend test count grew from 322 (pre-change baseline, verified via git stash + dotnet test on the unmodified worktree) to 361 — 39 new tests added across CapabilityRouterTests.cs, DesignJobPayloadTests.cs, ArtifactStoreTests.cs, and DesignRunRegistryTests.cs.

openapi/FleetMissionControl.Api.json was regenerated automatically as part of dotnet build (the GenerateOpenApiDocuments MSBuild target runs on every build) — the diff shows the new ArtifactRecord/RunSummary fields landed in the contract as expected.

TODOs / stubs (explicit, by design)

  1. Skill execution is not wired. Per the ticket ("You need NOT fully wire skill execution"), the dispatched agent is instructed to use python-pptx/python-docx/openpyxl/local-render/ render+export, but nothing here installs those libraries or executes them — that's the executing agent's job, same as figma/stitch today only carry MCP-tool instructions.
  2. Jira attach — FLOORED. See above; explicit TODO comment left in DesignEndpoints.cs.
  3. image capability stays blocked (out of scope for this ticket — untouched).

Deviations from a literal reading of the ticket

  1. generatedAt reuses the existing CreatedAt field rather than adding a duplicate GeneratedAt property. CreatedAt already carries an ISO-8601 UTC timestamp set at save time — functionally identical to "generatedAt". Renaming it would break the existing FLT-16/FLT-19 wire contract (tests, frontend ArtifactRecord.createdAt usage) for no behavioral gain; the XML doc comment on ArtifactRecord now says explicitly that CreatedAt fulfils this field.
  2. Frontend generated API client (fleet-api.generated.ts) was hand-edited, not regenerated via nswag run nswag.json. The worktree has no frontend/node_modules (nswag is an npm devDependency) and the ticket's build/test gate is dotnet build -warnaserror + dotnet test only — no frontend build/test command was specified. The hand-edit was checked field-for-field against the auto-regenerated openapi/FleetMissionControl.Api.json (see diff) to keep it contract-accurate. Flagging this so a follow-up can run the real generator once npm install has been run in this worktree, to catch any nswag output-shape drift (e.g. ordering, [key: string]: any placement) the hand-edit didn't reproduce exactly.
  3. Frontend tests were not executed (no node_modules in this worktree) — spec files were updated to match the new component behavior by inspection, not verified by a live test run. Flagged for a follow-up npm ci && npm test pass.

Scope discipline

Touched: capability routing, design-job payload, design-run registry, artifact store, design endpoints, /api/runs enrichment, the design-panel and run-stream frontend components, and their tests/specs. Did not touch repo-registry, cards, or autonomy code, per the ticket's stay-in- scope instruction.