Files
hyperguild/specs/capture-implementation-report.md
T
mathiasandClaude Sonnet 5 1cea2c9f78
CI / Lint / Test / Vet (push) Successful in 29s
CI / Mirror to GitHub (push) Successful in 4s
feat(capture): remove summary->ai-sessions write path
ai-sessions#13: capture's `summary` field wrote a frontmatter shape
(title/harness/fidelity/captured_at/repos_touched) that ai-sessions'
own extract/audit pipeline can't parse -- silently invisible to that
repo's own audit tooling, and bypassing its redaction + Stage2
completeness gates by construction. Only 5 files ever landed this way
over 3 weeks; the summary capability's whole value (speed) fights
ai-sessions' whole value (redacted, audited, complete), so kill it
rather than build a second parse branch.

capture now persists insights -> brain and action items -> Gitea
tickets only. Removes Summary/SummaryResult/SummaryWriter and all
wiring (service, REST body, MCP tool schema); close-session updated to
stop assembling a summary payload. specs/capture-*.md kept as historical
record with a superseded note -- the feature shipped and is documented,
just no longer current behaviour.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WPdSHbp9Utb2hPFm9wDG59
2026-07-27 23:17:56 +02:00

8.7 KiB
Raw Blame History

Capture capability — implementation report (as-built)

Status: Shipped 2026-06-23, tagged v0.11.0. Epic hyperguild #49 (sub-issues #50#55) closed. Superseded 2026-07-27: the summaryai-sessions write path documented below was removed. It wrote a frontmatter shape (title/harness/fidelity/captured_at/repos_touched) that ai-sessions' own extract/audit pipeline couldn't parse — invisible to that repo's own audit tooling and bypassing its redaction/completeness gates (ai-sessions#13). capture now persists insights → brain and action items → Gitea tickets only. This document is kept as the historical as-built record of what shipped in #49; treat every summary/ai-sessions reference below as retired, not current behaviour. Spec: specs/capture-bdd-spec.md (the design contract this implements). Governed by: infra/docs/architecture/01-invariants.md (I1I5) + the I2 acceptance ledger entry in infra/docs/security-baseline.md.

This document records what was actually built, where it lives, how it maps to the spec, and what was deferred — for onboarding and future audit. It does not restate the design rationale (see the spec and the linked brain entries).


1. Outcome

One uniform capture capability — insights → brain, action items → Gitea tickets, optional summary → ai-sessions — reachable identically from every harness:

  • In-process / direct-REST harnesses (Claude Code CLI, Agentsquad, claude.ai Code, headless): POST /capture on the brain server.
  • MCP-native harnesses (claude.ai Chat/Cowork/Design, Crush, Pi, LLM Council): the capture MCP tool, reached over the existing /mcp OAuth connector.

Both doors call the same CaptureService; only the transport and credential assembly differ. The persistence behaviour (validation, classification, I1 gate, orchestration, I5 audit, partial receipt) is written once.


2. Architecture (as-built)

                 POST /capture (REST)          capture MCP tool
                 capturehttp.Handler           mcp.Server.brainCapture
                          \                          /
                           \   (auth → principal →  /
                            \   origin; decode)    /
                             v                    v
                        capture.CaptureService (use-case, pure)
            ┌───────────────┬───────────────┬──────────────┬───────────────┐
        BrainStore     IssueTracker    SummaryWriter   ClassificationPolicy  AuditSink
        brainstore.    gitea.Client    (nil today)     classification.Config  audit.Degrading
        Store          (REST)                                                 Sink / SlogSink
        │                                                                     │
        api.WriteNote/UpdateNote/ReadNote (#45)                       LokiCentral + FileBuffer
        + wing index + auto-tunnel + graph re-index                  + NtfyNotifier + Reconcile
  • internal/capture/ — the use-case + ports + entities. Pure; no I/O. Owns validation (fail-closed), effective-classification resolution (stricter wins), the I1 sovereignty gate, best-effort orchestration, the two-phase I5 audit (Reserve before writes / Record after), and the partial-aware receipt.
  • internal/brainstore/ — concrete BrainStore wrapping the #45 api primitives + wiki upkeep (wing _index, auto-tunnel, graph re-index). The MCP brain_write/brain_update/brain_get handlers were re-pointed at it: one implementation, not two.
  • internal/classification/public < internal < confidential taxonomy + per-wing/repo tags from an optional classification.yaml; fail-safe to confidential.
  • internal/gitea/IssueTracker over the Gitea REST API; owner forced to mathias; token only in the Authorization header.
  • internal/capturehttp/ — the REST adapter + the shared Authenticate / DecodeRequest / OriginResolver (also used by the MCP tool).
  • internal/audit/SlogSink (default) and the DegradingSink (loki + durable FileBuffer + NtfyNotifier + Reconcile).
  • internal/mcp/ — the capture relay tool + principal threading (re-derives the caller's principal from the Bearer header the chassis middleware discards).

3. Sub-issue → PR map

Sub Issue PR(s) Delivered
49a #50 #56 classification taxonomy + per-wing/repo tags (fail-safe to confidential)
49b #51 #57 CaptureService use-case + ports + entities; BrainStore extraction (MCP re-pointed)
49c #52 #58 Gitea IssueTracker (owner forced mathias; token never logged)
49d #53 #59 POST /capture REST + OAuth2 + I1 sovereignty gate (server-derived origin)
49e #54 #60 I5 audit path + classification-aware degradation (loki + buffer + reconcile)
49f #55 #61, infra #151 (ledger), #152 (deploy) MCP capture relay tool + I2 ledger + I3 deploy

Predecessor: #45 (brain_update/brain_get verbs, PR #46) — the read-after-write contract capture reuses.


4. Invariant compliance

Inv How satisfied
I1 sovereign containment Effective classification = stricter(caller-declared, target-derived #50). Origin is server-derived from the authenticated principal, never context.harness. Confidential + us-nexus origin → refused before any write; refusal audited. Asserted-vs-derived mismatch → security event.
I2 deliberate acceptance The relay's cross-harness reach is recorded in infra/docs/security-baseline.md with six containment properties + Revisit-if, merged before relay code shipped (infra #151).
I3 GitOps reconcilability Env + gitea-api-token ExternalSecret under infra/k3s/apps/supervisor/, Flux-reconciled; image bumped by CD. No untracked runtime.
I5 auditability Every capture emits a request-level audit record. Classification-aware degradation: confidential + sink-down → hard-refuse; internal/public + sink-down → durable local buffer + ntfy + reconcile-on-recovery; floor → refuse if nothing can record.

5. Operational reference (env)

Set on the ingestion deployment (infra/k3s/apps/supervisor/ingestion-deployment.yaml):

Env Purpose Notes
BRAIN_GITEA_URL / BRAIN_GITEA_TOKEN enables the IssueTracker → gates /capture + the MCP tool token from 1P DMABE_GITEA_API_TOKEN via ESO; unset ⇒ capture disabled
BRAIN_LOKI_URL activates the DegradingSink unset ⇒ SlogSink (audit to stdout → alloy → loki; no refuse/buffer semantics)
BRAIN_NTFY_URL / BRAIN_NTFY_TOKEN degraded-state alerts optional
BRAIN_CAPTURE_SOVEREIGN_PRINCIPALS JWT subjects treated as sovereign-soil comma-separated; static-token caller is always sovereign; unknown JWT ⇒ us-nexus (fail safe)
BRAIN_AUDIT_RECONCILE_INTERVAL buffer→loki replay tick default 60s

The audit buffer lives at <brain>/.audit-buffer/capture.jsonl on the brain hostPath (nodeSelector-pinned to koala) — durable across restart without a separate PV.


6. Tests

67 test functions across the six packages. Coverage maps to the spec's Gherkin: happy path, supersede-not-duplicate, fail-closed validation, partial-failure receipt, dry-run, stricter-classification-wins, I1 confidential-via-us-nexus-refused / via-sovereign-allowed / asserted-label-ignored / caller-cannot-forge-origin, I5 confidential-refuse / internal-buffer / floor-refuse / reconcile / buffer-survives-restart, and the MCP relay tool (forwards, preserves principal, unauth rejected). task check green.


7. Deferred (not in this epic)

Tracked here so they aren't lost; file as issues when picked up:

  • SKILL veneer — the close-session SKILL becomes the claude.ai trigger/harvest layer that calls capture.
  • Per-harness token provisioning for Crush / Pi / LLM Council (claude.ai is done via the existing /mcp connector).
  • Harvest adapters — transcript-parse vs chat-memory-reconstruct vs agent-runlog, each assembling capture args at its own fidelity.
  • SummaryWriter impl — ai-sessions summary persistence (the port + path logic exist; the concrete writer is nil today, so a request with a summary fails that one item).

8. Brain learnings

  • wiki/hyperguild/decisions/capture-classification-taxonomy
  • wiki/hyperguild/decisions/gate-on-server-derived-signals-fail-safe
  • wiki/hyperguild/decisions/two-phase-reserve-record-audit-gate
  • wiki/hyperguild/failures/mcp-bearer-middleware-discards-principal
  • wiki/hyperguild/facts/brain-mcp-embeddings-out-of-band-sync (from #45, the predecessor)