Second AMEND from the verifier. Five blockers, all inside the 16-path fence; no
new path taken and no GREEN implemented.
1. The run carried an out-of-band `process.exit unexpectedly called with "1"`,
raised by main.ts:71 self-invoking on import and rejecting after the importing
case had returned. Vitest attributes that to no case and warns it can produce
false positives; it also falsified the document's "zero non-assertion
failures". The import is now wrapped so `process.exit` is stubbed
non-throwing and the case awaits a settle turn — the behavioral red is
unchanged, the error is owned, and the run reports `Errors 0`.
2. Two inventory cases demanded the raw values 'standalone' and 'postgres',
which the value-free classification unions cannot represent, so no compliant
implementation could have turned them green. They assert 'non-local' and
'non-pglite'.
3. SourceReadability gained `absent`. Without it an absent source has to be
reported `readable` — the one value meaning "read, and clean" — collapsing
the distinction the refusal path is built on. The invariant
`present === false` iff `readability === 'absent'` is now asserted per state
and across all seven rows at once.
4. Acceptance #6 had no positive anchor: an empty `bootstrap()` and an
always-refusing `onModuleInit()` satisfied every case, so "safe" could have
meant "does not work". Two cases now fail against that — bootstrap must reach
NestFactory.create and listen under the frozen policy with zero independent
read, and the sterile local lifecycle must actually run both migrations with
no Postgres handle. Both in-process and substituted at their seams; no port
is bound and nothing connects.
5. Every mkdtemp site in the six files now registers teardown at the point of
creation, including the child route's root created inside the spawn
arguments. The matrix leaves zero /tmp/kbn-02a-* residue, measured.
Focused matrix: 82 failing / 16 passing (98 cases, 6 files), Errors 0.
Typecheck, lint, Prettier, diff --check clean.
Author phase only; still no implementation. Kept as a distinct fifth commit so
the RED baseline, the contract restoration, the span audit and this supplement
remain separately reviewable.
The finding that mattered: every frozen-policy case mocked ./main.js wholesale,
so nothing executed main.ts, AppModule or the ConfigModule provider, and the
three independent reads at main.ts:28, app.module.ts:39 and config.module.ts:12
could all have survived a fully green matrix. Fence revision 15 -> 16 (granted
solely for apps/gateway/src/main.spec.ts) is used to import the real modules
with no ./main.js mock and assert no independent loadConfig, no tier probe and
no NestFactory.create. The existing anchoring case is left red-in-place rather
than deleted: its import-time-exit premise is the opposite of the new contract,
so it must be migrated by whoever implements GREEN.
Also closes: the executed dev:local child-process route, which requires a
LOCAL_START_* code AND the absence of the BETTER_AUTH_SECRET throw, since a
nonzero exit alone is satisfied by a crash; the real database onModuleInit
lifecycle, asserting migrations never run; the full disposable state-root
contract (absent, not-a-directory, symlink, non-private, foreign-owner), with
ownership substituted at lstat because a filesystem-built case passes trivially
wherever CI runs as root; unreadable and malformed sources, which refuse rather
than being skipped, built as EISDIR so they cannot be vacuous under any uid;
per-source non-local tier detection for all seven labels; canary retention and
console cases; post-freeze source-mutation cases; and a three-form static-import
guard.
Two defects of my own are fixed rather than papered over. The non-canonical
state-root case normalized to the valid path before the policy saw it, so it
demanded a refusal on input identical to the accepted one — a red that could
never go green for its stated reason. And the first substitution of @nestjs/core
was wholesale, which dropped APP_GUARD and killed app.module.ts at import, so
two cases failed for a reason they did not state.
Classification types are unions, not strings: a declaredTier?: string is
value-free only by convention, and a two-member union cannot carry a canary.
77 failing / 15 passing (92 cases, 6 files). Typecheck, lint and Prettier clean.
Zero non-assertion failures. Seven of the fifteen greens are vacuous against the
stub, up from three; that rise is recorded in the document so it cannot later be
read as coverage. The first RED report's implication that the GREEN targets were
covered behaviorally is corrected there too.
Refs: KBN-101-02a (Stack #1178)
Author phase only. No implementation: the scaffolding is deliberately
permissive so every refusal assertion fails behaviorally rather than on a
missing module. A `not implemented` throw would have satisfied `.toThrow()`
and produced a green matrix that proved nothing.
Adds the path-ownership manifest and 60 behavioral cases across the nine
mutation->guard rows: inherited env, shared dotenv, daemon JSON, app JSON,
root JSON, MOSAIC_GATEWAY_HOME redirect, exact local tier, exact PGlite plus
disposable state root, and one-shot freeze with dynamic import.
50 red / 10 green. Zero non-assertion failures. The ten greens are itemized
in the manifest, including three that are vacuous until the implementation
exists.
Enforcement sits at the apps/gateway seam. Nothing in packages/db or
packages/config is touched: -02a proves the local entrypoint never reaches
those paths rather than modifying code owned by -01, -03 and -10.
Refs: KBN-101-02a (Stack #1178)
Add the Slice-Zero catalog and selection HTTP surfaces for P3 Task 3:
GET /api/harnesses, GET /api/harnesses/:harnessId/catalog,
GET+PUT /api/chat/preferences/selection. Scope is always server-derived
via scopeFromUser(CurrentUser); selection tuples are validated against the
live catalog with no fallback substitution and persisted in a transitional
owner-scoped in-memory store. HarnessModule is wired into AppModule.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01ESFAnh2t9HmLwng8oW95St
root cause: emitDecoratorMetadata reflected the third constructor parameter as Function and Nest attempted to resolve it
fix: optional HANDOFF_ID_FACTORY injection token, no production provider, preserving undefined -> crypto.randomUUID() default and unchanged positional construction
TDD: real CoordModule red at Function index [2], then green; test overrides only unrelated AuthGuard because its AUTH provider comes from AppModule's global AuthModule context
Closes#1145
Bring the chat experience into the Vite/React-Router SPA on the exact typed
Socket.IO /chat contract from @mosaicstack/types, replacing the /chat
placeholder behind AuthGuard. Surfaces message:ack (with an accessible
status), agent:start, streamed agent:text/agent:thinking, tool start/end
status, agent:end with usage, session:info (thinking controls + routing
decision), commands:manifest, command:result, command:approval (with a
one-time approved-run affordance), system:reload (refreshing the rendered
manifest), and error, and emits message/abort/set:thinking/command:execute/
command:approve with exact payloads.
The gateway does not guarantee message:ack is the first event for a new
conversation (session:info, and error on auth/session-creation failure, can
both arrive first) — conversation-scoped events now adopt the conversation
from whichever scoped event names it first while a send is pending, then
filter everything else against that established conversation. A typed error
stops streaming instead of leaving Stop stuck active; agent:end no longer
appends an empty assistant turn when there is no text or thinking; and a
second message can no longer be sent while a turn is streaming.
Command approval is now integrity-checked end to end: only one
command:approve request may be outstanding at a time (a concurrent request
is ignored rather than overwriting the pending command/args), a stale or
mismatched command:approval response cannot replace active approval state,
and running an approved command clears its approval state immediately (via
a ref, before React re-renders) so a double-click cannot replay
command:execute.
The `/chat` socket is now typed at a single boundary: apps/web/src/lib/
socket.ts narrows socket.io-client's untyped `io()` return value to
`ChatSocket` (Socket<ServerToClientEvents, ClientToServerEvents>) once, at
creation, via the one assertion the library's types force; every consumer
(use-chat-connection.ts) then gets fully checked `on`/`emit` calls with no
further casts. The shared contract types live in the new
apps/web/src/lib/chat-contract.ts (replacing the old spa/chat/types.ts
shim), which re-exports them via type-only imports resolved directly
against packages/types/src (apps/web has no @mosaicstack/types package
dependency, so this stays source-only and is erased at compile time —
no package manifest or lockfile is touched). The two recorded-event test
suites now drive a shared, typed fake socket
(spa/chat/test-support/fake-chat-socket.ts) instead of an untyped
`(event: string, payload: unknown)` harness, so a wrong event name or
malformed payload fails to compile.
First increment of the approved Phase P RFC (webui-mission). Adds a Vite + React
Router SPA scaffold coexisting with the Next app: index.html with the theme
anti-flash script, src/main.tsx entry, the v1 parity route table under Guest/Auth
guard shells, and a dev proxy (/api, /socket.io ws) to the gateway on 14242 so the
SPA is same-origin in dev. vitest bumped to v3 (vite 8 pairing); existing specs
pass unchanged. Next remains the served app until the P5 cutover.
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01ESFAnh2t9HmLwng8oW95St
CaService hard-requires STEP_CA_URL/provisioner config at construction, so an
unconditional FederationModule import makes every standalone/local boot die at
DI time. Gate the module on loadConfig().tier === federated, matching the
documented intent of the federation compose profile (must not start in
non-federated dev).
Verified in mosaic-dev box: standalone tier boots to "Gateway listening on
port 14242" with bootstrap/socket.io/auth surfaces responding; federated tier
path unchanged.