# BUILD_STATUS — durable implementation checkpoint

Last updated: 2026-10-17 12:30 +08 (Asia/Singapore). Read this first if your context was compacted.
Milestones M1–M6 are complete; every number below was measured on this Mac today. The full evidence
trail is `docs/VALIDATION.md`, the machine-readable e2e report is `docs/validation/e2e-results.json`,
and the independent post-build system audit (findings register, repairs, measured optimizations) is
`docs/audit/2026-10-17T152518+08/SYSTEM_AUDIT.md` with its index at
`docs/audit/2026-10-17T152518+08/README.md`. The audit found and fixed 15 product defects (F-01…F-19,
minus three unrecoverable ids) and one harness defect; it is the more current of the two documents
where they disagree.

## Pinned environment (measured, not assumed)

| Component | Value |
|---|---|
| Node | v24.21.0 (`~/.local/node-v24.21.0-darwin-arm64/bin/node`) — runs `.ts` directly (strip-only) |
| pnpm | 11.25.0 · TypeScript 5.9.3 · Vite 8.3.4 (rolldown) |
| SurrealDB | 3.2.3+20260721.40522d1 (aarch64), `/opt/homebrew/bin/surreal`, `surrealkv://data/db/atlas-skv` |
| oMLX | `http://127.0.0.1:8000/v1`, model `Qwen3.8-Flash-Next-Uncensored-oQ5e-mtp`; owned by the user, never started or reconfigured by this build |
| Browser driver | puppeteer-core 25.14.0 driving Google Chrome at `/Applications/Google Chrome.app` |
| OS / hardware | macOS 27.0.1 (build 26A434), MacBook Pro Mac17,6 — Apple M5 Max, 18 cores, 128 GB RAM |

Ports owned by this project: **18080** SurrealDB (HTTP+RPC), **8787** API, **5173** Vite UI.
Ports 8001/8002 hold unrelated SurrealDB instances and 8000 holds oMLX — do not touch them.

## Milestone status

| Milestone | State | Evidence (measured today) |
|---|---|---|
| M1 Durable vertical slice | DONE | `reset --confirm` writes 559 defense + 542 commercial entities and 1,500 relations each; API restart re-reads them; check 1 passes |
| M2 Live Atlas and ontology | DONE | 3D/2D/scene/table on one snapshot contract, four lenses, inspector with evidence + eligible actions; checks 2–8 and journeys 13–14, 20 pass |
| M3 Both segment packs | DONE | Deterministic generators in `packages/ontology/src/{defense,commercial}.ts`; both mandatory stories computed from stored rows and asserted in the browser (shortfall 6 / 220 → 80 → 0 at SGD 1,380.00) |
| M4 Ingestion, actions, evidence | DONE | CSV/JSON mapping with per-row reasons (3 accepted / 3 rejected), lineage chain source → run → observation → record, idempotent re-import (lot count 53 → 53), version conflict, scope checks; checks 9–11 + journey 15 |
| M5 Copilot, scenario and history | DONE | `/api/copilot` grounded flow citing 26 stored uids with clickable citations and a supporting subgraph; what-if branch proves live stock never moves (`on hand 80 → 80`, journal `#18 → #18`); baselines + as-of replay |
| M6 Validation and handover | DONE | `pnpm test` **22 pass / 0 fail** (38.1 s); `pnpm test:e2e` **82 checks / 0 failures**, 9 journeys PASS (2 m 27 s); perf sample under target; ten docs written |

## Exact working commands (verified)

```bash
cd "/Users/aquek/Documents/ChatGPT/SPECTRA Team/spectra-3d-atlas"
cp .env.example .env.local                    # first time only; fill SURREAL_ADMIN_PASSWORD

pnpm preflight                                # ports, database, oMLX, fixtures; starts what is missing
pnpm dev                                      # SurrealDB + API + UI under one supervisor (Ctrl-C stops all)
node tools/run-tool.mjs status                # what is running, on which pid/port

pnpm -r typecheck                             # contracts + ontology + api + web — clean
pnpm build                                    # dist/: 1,318.47 kB JS (gzip 359.63 kB), CSS 34.63 kB
node tools/run-tool.mjs test                  # 14 integration + 8 unit checks (~38 s)
node tools/run-tool.mjs test --file tests/integration/live-state.test.mjs   # integration only (~50 s)
pnpm test:e2e                                 # Chrome journeys 13–20 + perf (~2 m 27 s); resets and reseeds

node tools/run-tool.mjs seed --clean          # prune synthetic strays, keep imports/Studio/branches
node tools/run-tool.mjs reset --confirm       # rebuild both workspaces from scratch (~17 s)
```

`node tools/run-tool.mjs test` must be used instead of a bare `node --test tests/…`: Node's runner
takes glob patterns, and the wrapper resolves them (passing a directory fails). Tests need the stack
up — `pnpm preflight` or `pnpm start` first.

## Measured state right now (measured 12:30–12:40, after the e2e run, a verified restore and a re-seed)

Commercial scope at the default `maxNodes=500`: **500 nodes / 1,465 edges drawn of 568 matching
objects (`truncated: true`)**. On a clean seed the same query reports 1,462 drawn of 542 matching,
so the gap is journey output, not drift; full scope (`maxNodes=2000&maxEdges=6000`) is defense
559/1,500 and commercial 542/1,500 with `truncated: false`. A backup taken at 12:34 reported
**4,434 rows + 141 evidence files → 8.0 MB (8,372,699 bytes) in 787 ms**, and `counts` reports
1,128 entities / 3,009 relations. Seed generators are deterministic: defense 559 entities /
1,500 relations and commercial 542 / 1,500, with `validationIssues: []` on both packs.

A restore of the 12:34 backup (12:38) and a plain re-seed (12:39) left every number above
unchanged — same 568/1,465 scope, same 1,128 entities / 3,009 relations — which is the durability
check itself, not a restatement.

## Decisions that are easy to regress (all measured on SurrealDB 3.2.3)

1. `RELATE type::record(...)` is a **parse error**; bind endpoints with `LET $in_x/$out_x`.
   Relation tables reject plain `UPSERT`; restore writes `RELATE entity:\`k\`->entity_link:\`k\`->entity:\`k\``.
2. Path assignment into a quoted/dashed key (`properties.reservations["trf-x"] = ...`) stores `NONE`.
   Rewrite the whole `properties` object instead.
3. HTTP bodies over ~1 MB return **413 length limit exceeded**. History baselines are chunked into
   `snapshot_part` at 200,000 chars; restore batches are bounded by bytes (512 KiB) *and* statement count.
4. `SHOW CHANGES FOR TABLE entity SINCE 0` returns empty here, so out-of-band detection compares
   uid+version sets against journal **receipts** written inside each journaled transaction.
5. `entity_link` rows carry a generated record id *and* a separate `uid`; reconciliation matches on `uid`.
6. BM25 with the analyzer (CLASS, PUNCT) never yields `bx4` from `BX4-TUAS-01`; `/api/search` adds a
   `string::contains(string::lowercase(...))` top-up and reports `mode: bm25+substring`.
7. Every journaled write records `explained = { entities: [{uid, version}], relations: [...] }`.
   The watcher evaluates a pending suspicion *before* refreshing `observed`, and needs the same
   unexplained value on two consecutive passes; deletions confirm through a second loop.
8. `reservedQuantity` always equals the sum of `properties.reservations` from t0 — enforced by both
   seed generators (`reservationLedger`) and by `validateProperties` (`dataType: object`).
9. Advisory preconditions carry `blocking: false` and never stop a commit; only blocking failures do
   (`committable()` / `blockingFailures()` in `apps/api/src/actions.ts`).
10. Restore pauses the watcher (`setReconciliationPaused(true)`) and journals a `baseline.captured`
    event per workspace, so live clients re-read instead of trusting a dead cursor.
11. **Seed verification reconciles uid sets; it does not compare counts.** A seed is additive, so
    failure means `missing` only; everything else is reported as `drift` (`expected`, `stored`,
    `extra`, `extraUids`). Exact-count equality made legitimate drift look like a defect.
12. **`seed --clean` prunes synthetic strays and their edges only.** `synthetic` exists on `entity`
    alone (`db/migrations/002_graph.surql:18`, default `true`); ingestion writes accepted rows with
    `synthetic: false` (`apps/api/src/ingest.ts:329`). `entity_link` has no such field, so an edge is
    removed only when one of its endpoints is a pruned stray — never by "extra" membership.
13. `evidence` has no entity column (`db/migrations/003_evidence.surql:37-50`), so a pruned object's
    evidence cannot be found by entity and is left in place as reported drift. Do not "fix" this by
    deleting all evidence extras: that is exactly what wiped imported lineage before (see VALIDATION fix 7).
14. The integration suite writes its test backup to `data/backups/itest-backup.json`, not the repo
    root. Keep probe output out of the working tree.

## Test status (measured 2026-10-17)

`node tools/run-tool.mjs test` → **22 pass / 0 fail** in 38.1 s (14 integration + 8 unit, includes a
full backup and restore). The integration file alone was run twice back-to-back at 14/14 to confirm
the version-conflict and re-seed checks are not order-dependent. `pnpm test:e2e` → **82 checks /
0 failures** across 9 journeys, exit 0, with all six captured browser problems attributed (four
deliberate `ERR_ABORTED` stream teardowns and one provoked, asserted 400).

## Still unverified / known gaps (full list in `docs/VALIDATION.md`)

- oMLX answer *quality* and latency: grounding is machine-proved, prose is not; inference took
  15.2–54.3 s depending on load, and nothing here paces it.
- FPS numbers come from headless Chrome with an emulated drag — trackpad feel, label legibility and
  contrast need human eyes. Narrow viewport is checked for "renders", not comfort.
- No latency measured at the expanded-scope cap (1,000 / 3,000) or against the 10k/30k stress fixture.
- One JS bundle, no code-splitting (Vite warns); fine on loopback, not over a real network.
- `/health` reports the database version as `unknown`; the binary is 3.2.3 (`surreal version`).
- Single-user local POC: signed actor/role/workspace cookie, no TLS, no multi-tenant story.

## Next concrete task

Nothing is owed to M6 — the build is validated and handed over. Optional follow-ups live in
`docs/ROADMAP.md` (Neo4j projection, AI Assurance, code-splitting, an "imported rows" view so
preserved drift is visible without reading a seed report).
