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.csCapabilitiesnow includes all five new types.AllowedModelsroutes 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-awarePlan()path (routed, not blocked).src/FleetMissionControl.Api/Services/DesignJobPayload.cs- New
EngineByCapabilitymap: 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): addedSha256,SourcePrompt,Format,Path.SizeBytesandCreatedAtalready existed and fulfilsize/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
Sha256from 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
sourcePromptparameter, server-derived fromDesignRunRegistry's context, never trusted from the uploading agent. ContentTypeAllowlist["binary"]extended with the three OOXML mime types (pptx/docx/xlsx);pdfalready fit the existingapplication/pdfentry. No newKindor sniff branch needed — OOXML files are ZIP containers, same as the pre-existing zip/octet-stream binary handling (no magic-byte sniff forbinarykind, matching the existing design).src/FleetMissionControl.Api/Services/DesignRunRegistry.cs:DesignRunContextgrew aPromptfield (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): addedArtifactFileName,ArtifactDownloadUrlalongside the existingArtifactId/ArtifactViewUrl.src/FleetMissionControl.Api/Services/ArtifactStore.cs(ArtifactLink): grewFileName/DownloadUrl(both optional, default-compatible) so/api/runsenrichment and theRunUpdatedhub push can carry them without a second meta read.src/FleetMissionControl.Api/Endpoints/DesignEndpoints.csandProgram.cs: both the live hub push and the/api/runsGET 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 wheneverartifactDownloadUrlis present — this is the only chip pptx/docx/xlsx/pdf artifacts get, since they aredisposition=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 grewpptx/docx/xlsx/html/pdf(all enabled, unlike the disabledimagebutton).- Matching spec updates in
design-panel.component.spec.tsandrun-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)¶
- 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/stitchtoday only carry MCP-tool instructions. - Jira attach — FLOORED. See above; explicit TODO comment left in
DesignEndpoints.cs. imagecapability stays blocked (out of scope for this ticket — untouched).
Deviations from a literal reading of the ticket¶
generatedAtreuses the existingCreatedAtfield rather than adding a duplicateGeneratedAtproperty.CreatedAtalready 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, frontendArtifactRecord.createdAtusage) for no behavioral gain; the XML doc comment onArtifactRecordnow says explicitly thatCreatedAtfulfils this field.- Frontend generated API client (
fleet-api.generated.ts) was hand-edited, not regenerated vianswag run nswag.json. The worktree has nofrontend/node_modules(nswag is an npm devDependency) and the ticket's build/test gate isdotnet build -warnaserror+dotnet testonly — no frontend build/test command was specified. The hand-edit was checked field-for-field against the auto-regeneratedopenapi/FleetMissionControl.Api.json(see diff) to keep it contract-accurate. Flagging this so a follow-up can run the real generator oncenpm installhas been run in this worktree, to catch any nswag output-shape drift (e.g. ordering,[key: string]: anyplacement) the hand-edit didn't reproduce exactly. - Frontend tests were not executed (no
node_modulesin 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-upnpm ci && npm testpass.
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.