Implements #51 (capture 49b) — the Clean-Architecture spine of the capture capability.
Stacked on #56 (#50). Base is feat/capture-classification so this diff is #51-only. It reuses classification.Level/Target (shared domain type — avoids a duplicate, drift-prone enum), which is the one soft dependency on #50. Merge #56 first, then this retargets cleanly to main.
What landed
ingestion/internal/capture/ — pure use-case, no I/O:
CaptureService.Capture: validate-before-write (fail-closed) → resolve effective classification (stricter of declared vs target-derived) → orchestrate insights (write/supersede) → tickets → summary, best-effort with per-item outcome → emit a request-level audit record of exactly what landed → return a partial-aware receipt. dry_run short-circuits after validation, writes nothing (not even audit).
ingestion/internal/brainstore/ — the BrainStore extraction:
One concrete Store implementing capture.BrainStore, composing the api file primitives (#45 read-after-write) with the wiki upkeep a write needs (wing _index rebuild, cross-wing auto-tunnel, graph re-index).
MCP brain_write/brain_update/brain_get re-pointed at it — one implementation, not two (the DRY payoff #51 is named for). The {id, path, content_hash} response contract and all existing MCP tests are preserved unmodified.
Scope boundaries (deliberate)
I1 origin sovereignty gate → #53 — needs the server-derived principal origin; not here.
Classification-aware audit degradation/refusal → #54 — the AuditSink port exists; the confidential-fails-closed / internal-degrades policy layers on around this core.
Effective folded into the service as classification.Stricter (port stays minimal Derive) — "stricter wins" is use-case policy, and it keeps classification.Config a drop-in ClassificationPolicy.
Unknown-repo pre-validation is structural-only here (repo non-empty + valid action + required fields); true repo-existence validation lands with the real tracker (#52). A bad repo surfaces as a per-item ticket error (partial receipt), not a silent write.
Implements #51 (capture 49b) — the Clean-Architecture spine of the capture capability.
> **Stacked on #56 (#50).** Base is `feat/capture-classification` so this diff is #51-only. It reuses `classification.Level`/`Target` (shared domain type — avoids a duplicate, drift-prone enum), which is the one soft dependency on #50. **Merge #56 first**, then this retargets cleanly to `main`.
## What landed
**`ingestion/internal/capture/`** — pure use-case, no I/O:
- **Ports:** `BrainStore` (#45 Write/Update/Get), `IssueTracker`, `SummaryWriter`, `ClassificationPolicy`, `AuditSink`. **Entities:** `Insight`, `Ticket`, `Summary`, `CaptureContext`, `CaptureInput`, `CaptureReceipt`.
- **`CaptureService.Capture`:** validate-before-write (fail-closed) → resolve effective classification (stricter of declared vs target-derived) → orchestrate insights (write/supersede) → tickets → summary, best-effort with per-item outcome → emit a request-level audit record of exactly what landed → return a partial-aware receipt. `dry_run` short-circuits after validation, writes nothing (not even audit).
**`ingestion/internal/brainstore/`** — the BrainStore extraction:
- One concrete `Store` implementing `capture.BrainStore`, composing the `api` file primitives (#45 read-after-write) with the wiki upkeep a write needs (wing `_index` rebuild, cross-wing auto-tunnel, graph re-index).
- **MCP `brain_write`/`brain_update`/`brain_get` re-pointed at it** — one implementation, not two (the DRY payoff #51 is named for). The `{id, path, content_hash}` response contract and all existing MCP tests are preserved unmodified.
## Scope boundaries (deliberate)
- **I1 origin sovereignty gate → #53** — needs the server-derived principal origin; not here.
- **Classification-aware audit degradation/refusal → #54** — the `AuditSink` port exists; the confidential-fails-closed / internal-degrades policy layers on around this core.
- **`Effective` folded into the service** as `classification.Stricter` (port stays minimal `Derive`) — "stricter wins" is use-case policy, and it keeps `classification.Config` a drop-in `ClassificationPolicy`.
- **Unknown-repo pre-validation** is structural-only here (repo non-empty + valid action + required fields); true repo-existence validation lands with the real tracker (#52). A bad repo surfaces as a per-item ticket error (partial receipt), not a silent write.
## Acceptance criteria
- [x] `capture/` package: ports, entities, `CaptureService`
- [x] `BrainStore` concrete impl (Write/Update/Get); **MCP handlers re-pointed — no duplicate logic**
- [x] #45 read-after-write + (out-of-band) staleness discipline preserved inside the store
- [x] Validate-before-write: malformed input → error, nothing written
- [x] Best-effort orchestration + partial receipt with per-item `ok`/`error`; no rollback
- [x] `dry_run` returns would-be receipt, writes nothing
- [x] Effective-classification via injected `ClassificationPolicy` (stricter wins)
- [x] Unit tests vs fake ports: write+supersede, partial-failure, dry-run, validation-reject, stricter-wins (+ caller-raising-honoured, summary path/fidelity)
- [x] Existing MCP `brain_update`/`brain_get`/`brain_write` tests pass after re-pointing
- [x] `task check` green (lint 0 issues, vet clean, `-race` green)
## BDD scenarios satisfied (against fakes)
supersede-not-duplicate · fail-closed validation · partial-failure receipt · dry-run · stricter-classification-wins · caller-raising-honoured · summary fidelity stamping.
Not auto-merged — review + merge from claude.ai.
Closes #51
🤖 Generated with [Claude Code](https://claude.com/claude-code)
The Clean-Architecture core of the capture capability (#49b). Pure
orchestration over ports — no HTTP, no live Gitea, no audit I/O — fully
unit-tested against fakes before any adapter exists.
- Ports: BrainStore (#45 write/update/get), IssueTracker, SummaryWriter,
ClassificationPolicy (satisfied by #50's classification.Config),
AuditSink. Entities: Insight, Ticket, Summary, CaptureContext,
CaptureInput, CaptureReceipt.
- CaptureService.Capture: validate-before-write (fail-closed), resolve
effective classification (stricter of declared vs target-derived;
under-declaration logged as a security event), orchestrate insights
(write/supersede) → tickets → summary best-effort, emit a request-level
audit record of exactly what landed, return a partial-aware receipt.
- dry_run short-circuits after validation, writes nothing (not even audit).
Out of scope here, layered on later: the I1 origin sovereignty gate (#53,
needs the server-derived principal) and the classification-aware audit
degradation/refusal (#54). "Effective" is folded into the service as
classification.Stricter rather than a port method — the stricter-wins
rule is use-case policy.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extracts the #45 write/update/get logic + the wiki upkeep that must
accompany a write (wing _index rebuild, cross-wing auto-tunnel, graph
re-index) into a single concrete brainstore.Store implementing
capture.BrainStore. The MCP brain_write/brain_update/brain_get handlers
are re-pointed at it, so there is one implementation, not two — the DRY
payoff #51 is named for. capture and MCP now share the exact same brain
write path and read-after-write contract.
The Server gains a *brainstore.Store, constructed in NewServer and given
the graph store in WithGraph. Embedding refresh stays out-of-band
(mtime-driven vectorstore.Sync), unchanged. Existing MCP brain_update/
brain_get/brain_write tests pass unmodified — behaviour and the
{id, path, content_hash} response contract are preserved.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
mathias
merged commit 07fca9ee73 into main2026-06-22 21:31:47 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Implements #51 (capture 49b) — the Clean-Architecture spine of the capture capability.
What landed
ingestion/internal/capture/— pure use-case, no I/O:BrainStore(#45 Write/Update/Get),IssueTracker,SummaryWriter,ClassificationPolicy,AuditSink. Entities:Insight,Ticket,Summary,CaptureContext,CaptureInput,CaptureReceipt.CaptureService.Capture: validate-before-write (fail-closed) → resolve effective classification (stricter of declared vs target-derived) → orchestrate insights (write/supersede) → tickets → summary, best-effort with per-item outcome → emit a request-level audit record of exactly what landed → return a partial-aware receipt.dry_runshort-circuits after validation, writes nothing (not even audit).ingestion/internal/brainstore/— the BrainStore extraction:Storeimplementingcapture.BrainStore, composing theapifile primitives (#45 read-after-write) with the wiki upkeep a write needs (wing_indexrebuild, cross-wing auto-tunnel, graph re-index).brain_write/brain_update/brain_getre-pointed at it — one implementation, not two (the DRY payoff #51 is named for). The{id, path, content_hash}response contract and all existing MCP tests are preserved unmodified.Scope boundaries (deliberate)
AuditSinkport exists; the confidential-fails-closed / internal-degrades policy layers on around this core.Effectivefolded into the service asclassification.Stricter(port stays minimalDerive) — "stricter wins" is use-case policy, and it keepsclassification.Configa drop-inClassificationPolicy.Acceptance criteria
capture/package: ports, entities,CaptureServiceBrainStoreconcrete impl (Write/Update/Get); MCP handlers re-pointed — no duplicate logicok/error; no rollbackdry_runreturns would-be receipt, writes nothingClassificationPolicy(stricter wins)brain_update/brain_get/brain_writetests pass after re-pointingtask checkgreen (lint 0 issues, vet clean,-racegreen)BDD scenarios satisfied (against fakes)
supersede-not-duplicate · fail-closed validation · partial-failure receipt · dry-run · stricter-classification-wins · caller-raising-honoured · summary fidelity stamping.
Not auto-merged — review + merge from claude.ai.
Closes #51
🤖 Generated with Claude Code