Skip to main content

Investigations — delivery report

Phases 01–07 of docs/architecture/investigations/, one commit per phase, on claude/architecture-investigations-phases-bnaoaz.

SHAs

origin/staging was merged into the branch after Phase 6 (clean, no conflicts), so the base above is staging’s head at that merge, not the branch point.

Commits by phase

No phase was merged into another, and nothing was merged into staging.

Migrations and rollback

One migration: libs/db/src/migrations/1715200000150-AddInvestigations.ts — creates investigations, investigation_events, investigation_candidates, with unique (investigation_id, candidate_id) and (investigation_id, seq) and CHECK constraints on the kind/status vocabularies.
The migration is additive: no existing table is altered, so reverting it cannot affect another surface. Chat, Collections and the Radar do not reference these tables. Connection budget: the queue’s pool slot was RE-ALLOCATED from the retired workSessionWorkflow, not added. DATABASE_CONNECTION_BUDGET_TOTAL is still 8, and database-connection-budget.spec.ts fails if that stops being true.

Rollout switches

On by default. A deployment that sets nothing gets the whole surface, for every workspace whose plan includes it. These are KILL SWITCHES — they take something away in a hurry; they are not an enrolment gate. An enable-gate was rejected deliberately: it makes the shipped path the one nobody runs, so the feature works in the deployment where it is on and rots in every other, and the first real user is also the first integration test. The two ceilings are operational — they refuse to start work. They are not credits, not entitlements and not pricing: the plan gate (entitlements.assertFeature) still runs unchanged in front of every mutation, and Phase 7 explicitly defers the pricing decision until real cost is measured.

Test totals

Focused suites, this branch, all green: scripts/harness/check.sh: PASS — 19 passed, 0 failed, 3 skipped. Build and per-package typecheck (14 packages) clean.

Skipped and NOT RUN — exactly what was not verified

The live-staging runner’s credentialed path — booting the Nest context and driving a real run — has never executed in the authoring environment. It is delivered unverified and the runner says so in its own output.

Live staging report

NOT RUN. No staging database and no credentials were available. corepack pnpm run evals:investigation-live prints:
It captures Phase 7’s eleven metrics when it does run. Every metric is nullable and null means NOT MEASURED — a zero is only ever a measured zero.

Known limitations

  1. No live evidence of usefulness. Whether the open web actually yields useful Mexican commercial candidates for the five frozen objectives is unmeasured. The hermetic suites prove the rules; they cannot prove this.
  2. No integration-test run. Tenant isolation, the compare-and-set affected count and the unique constraints are proven by the database, and the spec that exercises them has not been executed here.
  3. No firmographics provider, no second web executor. Both are documented decisions with their contract questions marked NOT DETERMINED — see docs/architecture/source-expansion-data-rights.md.
  4. The credentialed half of the live runner is unverified.
  5. Browser-level scenarios (rows arriving progressively, a real deploy interrupting a real run) are named in investigation-acceptance.spec.ts rather than proven.
  6. Operational views are unexecuted SQL. Every column they reference is checked against the entity definitions by investigation-operations.spec.ts, which is the strongest guarantee available without a database — it is not the same as having run them.

Security and privacy review

  • Tenant isolation. Every service read carries the workspace predicate; a cross-workspace id is 404, never 403. The rollout gate uses 404 for the same reason — a workspace outside the rollout cannot learn the feature exists.
  • Authorization. Every mutation calls assertHasIdentity and entitlements.assertFeature(ws, 'assistant'); the architecture guard asserts the counts match the number of @Post handlers. No new public route. WorkspaceGuard remains the global APP_GUARD and is not bypassed.
  • Credentials. Resolved server-side through the existing encrypted provider-credential path. Never logged, never returned, never in model context. The web adapter now redacts its key from upstream error bodies before they become error messages.
  • Personal data. None is collected or stored. The candidate schema has no person, contact, email, phone or title column, and the discovery contract refuses contact vocabulary — including linkedin — as query material.
  • Prompt injection. Third-party text stays inside the branded UntrustedText fields; the conformance suite asserts at runtime that it never reaches a title, a control field or a structural position.
  • Curated activity stream. Numbers, booleans and a closed string vocabulary only. Adapter ids and internal costs are dropped by key — a vendor name would make provider choice product-visible and an internal cost would read as a price.
  • Supply chain. minimumReleaseAge is untouched and no package exclusion was added. No new runtime dependency was introduced by any phase.
  • Retired shapes stay retired. WorkSession*, gtm_research_runs and the deleted generic patch reducer are absent, enforced by the architecture guard.

Bundle and database impact

  • Bundle. Four new dashboard modules (Investigations, InvestigationDetail, DataArtifactRenderer, InvestigationPromote) plus a label dictionary and a stylesheet. No new npm dependency; the artifact renderer is local React rather than @assistant-ui/react-generative-ui (decision recorded in docs/architecture/data-artifact-views.md).
  • Database. Three tables and one pool connection, re-allocated rather than added. Reads are indexed on (workspace_id, created_at) and (status, heartbeat_at); candidate and event reads are bounded (500 rows / 200 events) and the API pages them.

Evidence that Chat remained independent

investigations.architecture.spec.ts fails the build on:
  • any file under investigations/ importing from chat/;
  • any file under chat/ importing from investigations/;
  • InvestigationsModule importing ChatModule or anything under chat/;
  • any investigation file reaching ChatStreamHub or opening an @Sse stream.
The two surfaces share the model seam and the two ledgers (agent_runs, model_usage) — the same seams every other surface uses — and nothing else.

Rollback rehearsal

Proven in investigation-rollout.spec.ts, each switch independently:

What must happen before general availability

  1. Run the integration spec against a real database.
  2. Run evals:investigation-live on staging and attach the report.
  3. Read the measured cost, then make the pricing/credits decision separately — Phase 7 deliberately does not make it.
  4. Decide retention and support posture.
  5. If anything looks wrong on the way, pin INVESTIGATIONS_WORKSPACES or pull INVESTIGATIONS_DISABLED — the feature is on for everyone until told otherwise.