/** * E2 customer_note first-class object - the LAST E2 object * (docs/plans/2026-06-01-e2-customer-note-object.md). * * A `customer_note` is a discrete note recorded against an account/customer entity: * a `note` body scoped to a `customer`, evolving via the supersede delta lifecycle. * Entity-scoping is a free-form `customer` column (the `entities` table is unbuilt - * E3.1 planned - so an FK is deferred). Unlike project_brief's one-summary-per-repo, * a customer accrues MANY discrete notes over time, each with its own supersede chain * (correct a note -> a new version preserving history; close retires it). * * Reuses the project_brief/skill supersede machinery verbatim (superseded_by self-FK * + CAS + INSERT-preflight + server-derived version + change_summary + supersede * tenant-match trigger). It has NO assembler/renderer (the simplest E2 object): the * contribution is purely the entity-scoping dimension. * * The `customer_notes` table is the source of truth (survives memory decay); the * memory mirror is for recall. memory_id is NULLABLE with ON DELETE SET NULL. * * Lifecycle: active -> superseded (a corrected version) or active -> closed (retired). */ export type NoteStatus = 'active' | 'superseded' | 'closed'; export declare const VALID_NOTE_STATES: ReadonlySet; /** Field caps (untrusted at the HTTP/SDK boundary). note is a body, so a larger cap * than the 4096 short-field convention. */ export declare const MAX_CUSTOMER_LEN = 256; export declare const MAX_NOTE_LEN = 8192; export declare const MAX_CHANGE_SUMMARY_LEN = 4096; export interface CustomerNote { id: number; /** Nullable: ON DELETE SET NULL lets memory deletion proceed without breaking * the note row. */ memoryId: string | null; tenantId: string; /** The account/customer entity this note is scoped to (free-form identifier). */ customer: string; /** The note body. */ note: string; /** Server-derived: 1 on a fresh create, predecessor.version + 1 on supersede. */ version: number; status: NoteStatus; supersededBy: number | null; supersededAt: string | null; /** The per-version delta note; set on a successor row only (NULL on a v1). */ changeSummary: string | null; closedAt: string | null; createdAt: string; } export interface SaveCustomerNoteOpts { customer: string; note: string; /** The delta note for a supersession; ignored (stored NULL) on a fresh create. */ changeSummary?: string; /** Table id of an ACTIVE note this new version supersedes. */ supersedesNoteId?: number; /** Extra memory tags merged after ['customer_note', 'customer:']. */ extraTags?: string[]; } export interface ListCustomerNotesOpts { status?: NoteStatus; /** Filter to a single customer. */ customer?: string; limit?: number; } /** * Create a customer_note (or a new version that supersedes an existing one). Writes * the memory mirror + the customer_notes row atomically inside writeEntry's SAVEPOINT. * When supersedesNoteId is given, the referenced ACTIVE row is preflighted (status + * version) BEFORE the INSERT, then CAS-UPDATEd -> superseded in the same SAVEPOINT; * the new version = predecessor.version + 1 (server-derived). * * The memory mirror carries a `customer:` tag (in addition to ['customer_note'] * + caller extraTags) so scope-aware recall treats the note as entity-local - the * project_brief codex-P2 recall-locality lesson applied to entity scoping. There is * no self-recursion path (customer_note has no receipt-query/refresh). */ export declare function saveCustomerNote(hippoRoot: string, tenantId: string, opts: SaveCustomerNoteOpts, actor?: string): CustomerNote; /** * Close (retire) an active note. CAS guard WHERE status='active'; 0 changes * distinguishes not-found from not-active. A superseded row is terminal. */ export declare function closeCustomerNote(hippoRoot: string, tenantId: string, id: number, actor?: string): CustomerNote; export declare function loadCustomerNoteById(hippoRoot: string, tenantId: string, id: number): CustomerNote | null; export declare function loadCustomerNotes(hippoRoot: string, tenantId: string, opts?: ListCustomerNotesOpts): CustomerNote[]; /** * All ACTIVE notes for a customer, newest first. Returns a LIST (a customer accrues * MANY notes) - this deliberately DIVERGES from project_brief's * loadActiveBriefForRepo, which returns a single brief-or-null because a repo has one * evolving summary. A future caller cloning the project_brief shape by analogy must * not assume a single-return here; the plural name signals the list contract. */ export declare function loadActiveNotesForCustomer(hippoRoot: string, tenantId: string, customer: string, opts?: { limit?: number; }): CustomerNote[]; //# sourceMappingURL=customer-notes.d.ts.map