Factory docs, home
Page navigation

Reference document, shown as written except that local paths appear as placeholders. Where it describes the Factory as intended, read it as design, not current state: only local infrastructure is accepted, no release, customer value or scheduled automation is established, and all 27 customer-value epics remain open. Current state: Factory model.

A local browser view of the Portfolio: registered projects, sourced progress, usage and the fact sources still to connect. It also shows what needs attention, a resolution guide for each blocker, and each project's editable core values. Behaviour and acceptance: human interface. Visual specification: design system.

npm --prefix apps/dashboard run build          # builds apps/dashboard/dist (ignored by Git)
npm --prefix apps/dashboard run check          # build, then the dashboard's own tests
node src/dashboard-server.ts --journal <local-portfolio-journal> [--port 4317] [--no-inspect] [--no-guidance-edits]

The server binds 127.0.0.1 only. It never creates, migrates or seeds a journal.

Modules

ModuleOwnsMay import
src/dashboard-contract.tsNeutral, browser-safe DTO types, DASHBOARD_PROTOCOL, period and token-counter IDsNothing
src/attention.tsPure attention projection and blocker resolution detailContract, Portfolio types
src/dashboard-view.tsPure projection: field-by-field DTO copies, cross-project totals, source descriptors and their standing, provider namesContract, attention, Portfolio error and types, guidance types
src/project-guidance.tsCore values domain (guidance)Journal, Portfolio registry
src/dashboard-server.tsHTTP composition root: loopback binding, Host/Origin/token guards, read-only journal access, the two commands, static manifestContract, projections, Portfolio, guidance, Journal
apps/dashboard/srcPresentation: routing, React components, words for typed codes, number/date/currency formatting, layoutReact; the contract through contract.ts only

test/dashboard-boundaries.test.ts checks these import rules against the actual source. There is no dependency-injection framework or second registry: the server constructs Portfolio directly and passes its views to the projection.

Protocol

Every JSON body, including errors, carries protocol: "factory-dashboard/3". The browser refuses a body with any other value and asks for a reload, so a page built for another contract never misreads data. Change the constant whenever a served shape or closed code set changes incompatibly.

Where decisions live

Projection (server). Everything that interprets domain facts:

  • Sums across projects: exact only when every part is exact. Otherwise the known parts give a lower bound that keeps every reason. A corrupt project adds corrupt-project.
  • Source standing from every Count a source supplies, never one convenient outcome. For example, an undated failed Run keeps verification incomplete even when the verified count is exact.
  • factsInView: whether the selected view shows a fact of that kind (dated in the period, undated, or a cumulative usage session). It makes no claim about facts outside the period.
  • Collection capability for each source. trusted-import means only a trusted local import records such facts; nothing collects automatically and the dashboard cannot connect a source. no-contract means nothing can hold the facts yet.
  • Measured value stays unknown (no-outcome-assessment). Raw measurements never qualify.

Browser. Words for closed codes (states, reasons, availability, capabilities), number, date and currency formatting, table layout and navigation. The browser computes no metric and decides no coverage.

Data, not code. Source IDs and names, provider IDs and names, collectors and Product IDs arrive as data. To add or replace a fact source, change SOURCES in the projection. If it has the same fact contract and an existing capability, the dashboard needs no change. A new capability kind is a contract change and needs a protocol bump.

Current sources

SourceCounts it suppliesCapability
Verification runsEvery verification outcome and both validity countstrusted-import
Work items and proposalsEvery work transition and rejected proposalstrusted-import
Pull requests and issuesEvery pull-request and issue transitiontrusted-import
ReleasesAlpha and stable availability and withdrawaltrusted-import
Model usageDated invocations and their tokens; undated invocations and sessions count as facts in viewtrusted-import
Customer outcomesNoneno-contract

The local repository check is separate: it is an explicit command (POST /api/products/:id/inspect) that records Git metadata for one registered Product. It is not a period source.

API

RouteDoes
GET /api/sessionCommand token, periods, capabilities (inspect, guidance)
GET /api/portfolio?period=The overview, with attention counts
GET /api/products/:id?period=One project, with its attention items
GET /api/attention?period=Attention across every registered Product
GET /api/products/:id/blockers/:blockerIdA blocker's governing report, history and recheck capability. The ID is URL-encoded.
GET /api/products/:id/guidance, …/guidance/history, …/guidance/revisions/:nCurrent, recent and one exact guidance revision
POST /api/products/:id/inspectBody {}: checks repository availability only
POST /api/products/:id/guidanceBody { commandId, expectedRevision, values }, at most 24 KiB. Recorded as direct entry.

Commands. Both need this dashboard's Origin, Sec-Fetch-Site: same-origin when sent, JSON, and the session token. Each is for a Product already in the registry, and each writes through CommandJournal.openExisting.

Reads. GET and HEAD open the journal read-only. They record nothing, run no Git and start no process.

Not exposed. There is no route that marks a blocker resolved, imports observations or runs an arbitrary check.

Attention

src/attention.ts projects Portfolio.currentConditions, which reads each Product stream and the usage stream separately. An unreadable stream becomes one facts-unreadable action, and the others are still read. The list has no state of its own: no read or dismiss flags, no queue and no approval step.

CategoryItems
ActionAn open blocker; a repository observed unavailable; a source that declared itself unavailable, unauthenticated or corrupt; stored facts that fail validation
NoticeA repository never checked, or checked more than a day ago; declared partial coverage
  • Not items. Empty guidance, missing coverage, dirty files, failed Runs and unknown metrics are never items or gates.
  • Needs you. ownerRequired needs a source that names the owner and says why further remedies cannot progress. Otherwise the stated responsibility (factory, external, owner per source, or not stated) is shown.
  • Identity. An item's ID is Product, subject and reason. A correction updates its evidence rather than adding an item, and repeated declarations of one source condition are grouped with their own windows.
  • Periods. Blockers and repository state are current and ignore the period. Source notices are those overlapping it.
  • Order. Actions, then notices; within each by Product and subject. No business priority is implied.
  • Counts. Counts describe reported items, never the absence of blockers.

Blocker resolution guide

/projects/:id/blockers/:blockerId shows:

  • the prerequisite, affected work, who can act and why remedies cannot progress;
  • the steps to resolve, from the source's optional details or its nextAction;
  • the remedies tried and their evidence;
  • the report history.

Evidence is text; it is never opened or fetched. Three outcomes are kept apart:

  • Reload facts reads recorded facts again and says so. It tests nothing.
  • Recheck is unsupported: no trusted collector checks blocker prerequisites. The repository check checks availability only and never resolves a blocker.
  • Resolved is only a source recording it. That shows as reported, or reported-with-evidence when the source cites evidence; neither is presented as verified.

Presentation rules

  • Overview. Each row shows the project, its stated focus, verified runs with their current validity, and repository availability with check time. Branch and revision are on the project page.
  • Current focus. Lists stated focus and open blockers; a blocker row opens its resolution guide. No source ranks next work yet, so the list is not presented as an ordered queue.
  • Actions. A central list across projects, with concise counts, required actions separated from notices, and a link from each item to its guide or project. Reload is labelled read-only.
  • Core values. The project page shows the current values, revision and provenance. The editor can add, reword, reorder, retire and restore values. Its behaviour:
    • Feedback is per field and concise.
    • A conflict keeps the losing draft and shows both sides. Choosing to keep the draft is explicit; it is never silently rebased.
    • A failed or uncertain save keeps its operation ID, so a retry cannot record twice.
  • Connections to finish. Each incomplete source shows its name, a short state and a caption that acknowledges imported facts. Reasons and capability are in a details disclosure. No control implies that a source can be connected from the browser.
  • Usage. Invocations are allocated by date. Undated invocations and cumulative sessions are shown separately and never assigned to a period. Costs are provider-reported estimates: not billing, and not spending in a period.
  • Brand. The Factory mark sits beside the wordmark. Plumb appears only in the empty Portfolio, below the factual message, when that read succeeded and reports nothing needing attention. Both are decorative, byte-identical copies of the reviewed files in apps/dashboard/public/brand, served from the static manifest. Sources and hashes: design system.

Tests

FileCovers
test/dashboard-view.test.tsCompleteness from every outcome; imported, incomplete, problem and unmeasured sources; period-scoped facts; corrupt projects; replaced and added descriptors; sums
test/dashboard-server.test.tsLoopback and Host checks; period views; reads never write or run Git; command guards; static manifest; escaping; missing and corrupt stores; protocol on every body
test/dashboard-format.test.tsUnknown versus zero; lower bounds; branch text; remedies; generic source wording; historical validity; refusal of other protocols
test/dashboard-boundaries.test.tsThe import rules above; guidance has no transport or filesystem access and only the dashboard consumes it; the browser never injects HTML or evaluates code
test/attention.test.tsResponsibility and owner classification; deduplication and correction; current blockers versus period notices; repository items; no inferred gates; corrupt Product and usage streams isolated; old blocker facts and their digests; blocker details bounds; resolution standing
test/dashboard-guidance-attention.test.tsGuidance command guards, bounds, restart, replay, stale and conflict; attention and blocker routes; corrupt usage isolation; read-only GET and HEAD; no resolve route
test/dashboard-editor.test.tsEditor operation reuse, failure classes, change summaries, attention words and encoded targets
test/dashboard-brand.test.tsBrand copies match reviewed hashes; mark size and favicon; Plumb placement, alt text and copy; PNG serving
apps/dashboard/test/brand-render.test.tsDashboard package, not the root suite: the real Portfolio view rendered (react-dom/server, via the dashboard's Vite) for each read state. Plumb only in the calm empty case, after the message, with empty alt and no interactive, table or metric ancestor

Browser behaviour, layout and screenshots are verified separately in a real browser.

Source: docs/dashboard.md