package capture import ( "context" "time" "github.com/mathiasbq/hyperguild/ingestion/internal/classification" ) // Ref is the read-after-write handle returned by a brain write/update — // the #45 contract. ContentHash lets the caller confirm what landed // without a re-query; for an Update, Superseded is true. type Ref struct { ID string Path string ContentHash string Superseded bool } // StoredNote is a brain note fetched by Get: the read-after-write // confirmation primitive (a direct fetch, never a semantic query). type StoredNote struct { ID string Path string ContentHash string Frontmatter map[string]string Body string } // Note is the brain-write payload. It carries both the wing/hall taxonomy // and the legacy type/domain fields so a single BrainStore serves both // capture insights and the existing MCP brain_write surface. Reason is // the supersede rationale, used only by Update. type Note struct { Content string Filename string Wing string Hall string Type string Domain string Reason string } // BrainStore is the brain persistence port — the shared implementation of // the #45 write/update/get verbs that both the MCP handlers and capture // call, so there is one implementation, not two. The read-after-write + // staleness discipline lives behind this interface so no caller carries // the rule. type BrainStore interface { Write(ctx context.Context, n Note) (Ref, error) Update(ctx context.Context, slug string, n Note) (Ref, error) Get(ctx context.Context, id string) (StoredNote, error) } // IssueRef identifies a ticket touched by the tracker. type IssueRef struct { Repo string Number int URL string } // IssueTracker is the Gitea ticket port. The implementation (#52) always // scopes to owner "mathias"; the port deliberately omits owner. type IssueTracker interface { CreateIssue(ctx context.Context, repo, title, body string) (IssueRef, error) // CloseIssue closes an issue, optionally posting a closing comment // first (empty comment ⇒ close only). CloseIssue(ctx context.Context, repo string, number int, comment string) (IssueRef, error) CommentIssue(ctx context.Context, repo string, number int, body string) (IssueRef, error) } // SummaryWriter is the ai-sessions summary port. type SummaryWriter interface { WriteFile(ctx context.Context, repo, path, content string) error } // ClassificationPolicy derives a target's sensitivity (model C). The // "stricter wins" combination of declared vs derived is use-case policy // and lives in the service, so the port stays minimal. Satisfied by // classification.Config (#50). type ClassificationPolicy interface { Derive(target classification.Target) classification.Level } // AuditEntry is the request-level audit record (I5): who/what captured // what, when, via which principal. SecurityEvents carries anomalies such // as a caller under-declaring sensitivity relative to the target floor. type AuditEntry struct { Timestamp time.Time Principal string Actor string Harness string SessionRef string EffectiveClassification string Items []string SecurityEvents []string } // AuditSink records the audit entry. The classification-aware // degradation/refusal policy (confidential fails closed, internal // degrades) is the caller's concern in #54; this port just records. type AuditSink interface { Record(ctx context.Context, e AuditEntry) error }