// Package capture is the Clean-Architecture use-case for the uniform // capture capability (issue #49/#51): persist a finished session's // valuable output — insights → brain, action items → Gitea tickets, // optional summary → ai-sessions — with one invocation, identical core // behaviour across every harness. // // This package is pure orchestration. It depends only on ports // (interfaces) and plain entities — no HTTP, no live Gitea, no embedding // or audit I/O. The real adapters are wired in #52 (Gitea tracker), #53 // (REST + I1 origin gate), and #54/#55 (audit path + relay). The I1 // sovereignty refusal and the classification-aware audit degradation are // deliberately NOT here — those need the server-derived principal origin // (#53) and the loki/buffer machinery (#54). What lives here is everything // testable against fakes: validation, effective-classification resolution // (stricter wins), best-effort orchestration, and the partial receipt. package capture // Zone is the trust zone a capture originates from, server-derived from // the authenticated principal (spec §4.2 / I1). It is NEVER taken from // caller input — context.Harness is descriptive telemetry only. type Zone int const ( // ZoneUnknown means the origin was not set. The REST adapter always // sets a concrete zone; the service treats Unknown as "not gated" (only // an explicit ZoneUSNexus triggers the I1 refusal) so the gate can // never fire on a caller-controllable default. ZoneUnknown Zone = iota // ZoneSovereign is sovereign soil (homelab / Tailscale CLI callers). ZoneSovereign // ZoneUSNexus is a non-sovereign US-jurisdiction surface (e.g. // claude.ai). Confidential captures through it are refused (I1). ZoneUSNexus ) // String renders the zone for audit/refusal messages. func (z Zone) String() string { switch z { case ZoneSovereign: return "sovereign-soil" case ZoneUSNexus: return "us-nexus" default: return "unknown" } } // CaptureContext is the per-session metadata accompanying a capture. // // Classification is the caller-declared sensitivity (model C, spec §4.1): // the server independently derives the target's classification and gates // on the stricter of the two. Principal and Origin are server-derived from // the authenticated identity (the REST adapter populates them); they are // never caller-asserted. Harness is descriptive telemetry only — never a // gate input. type CaptureContext struct { Harness string SessionRef string Fidelity string Actor string Classification string // caller-declared level token ("" = unspecified) Principal string // server-derived (auth); audit identity Origin Zone // server-derived trust zone; the I1 gate input } // Insight is one piece of session knowledge bound for the brain. A // non-empty SupersedeSlug routes to Update (revise in place); otherwise // Write (create). type Insight struct { Text string Wing string Hall string SupersedeSlug string } // Ticket is one action item bound for a Gitea repo. Owner is always the // operator (set by the tracker adapter), never carried here. type Ticket struct { Repo string Action string // create | close | comment Number int // required for close/comment Title string // required for create Body string } // Summary is an optional session summary bound for ai-sessions. type Summary struct { Title string Body string ReposTouched []string } // CaptureInput is the whole capture request. type CaptureInput struct { Context CaptureContext Insights []Insight Tickets []Ticket Summary *Summary DryRun bool } // InsightResult is the per-insight outcome in the receipt. type InsightResult struct { ID string `json:"id,omitempty"` Path string `json:"path,omitempty"` ContentHash string `json:"content_hash,omitempty"` Superseded bool `json:"superseded"` OK bool `json:"ok"` } // TicketResult is the per-ticket outcome in the receipt. type TicketResult struct { Repo string `json:"repo"` Number int `json:"number,omitempty"` Action string `json:"action"` URL string `json:"url,omitempty"` OK bool `json:"ok"` } // SummaryResult is the summary outcome in the receipt. type SummaryResult struct { Path string `json:"path,omitempty"` OK bool `json:"ok"` } // ItemError pins a failure to a specific request item for the partial // receipt. Item is a stable locator like "insight[1]" or "ticket[0]". type ItemError struct { Item string `json:"item"` Error string `json:"error"` } // CaptureReceipt is the structured, partial-aware result. Per-item ok // flags plus a flat Errors list make partial success explicit; the // caller never has to infer what landed. type CaptureReceipt struct { Insights []InsightResult `json:"insights"` Tickets []TicketResult `json:"tickets"` Summary *SummaryResult `json:"summary,omitempty"` Errors []ItemError `json:"errors"` EffectiveClassification string `json:"effective_classification,omitempty"` DryRun bool `json:"dry_run"` }