/** * memory-file-projection.ts, standing memory records as GIT-BACKED markdown * files that ROUND-TRIP through the confirmation-gated mutation path. * * The platform's memory should be inspectable and correctable as files. This * module projects standing (project/team-scope) MemoryRecords to one markdown * file per record under a git-backed directory, and reads user edits BACK, but * never as a silent write to the store. A user edit or deletion becomes a * PROPOSAL (a review-queue entry); the store is mutated only for proposals the * caller explicitly confirms, through the registry's own update/delete. The disk * file is a correction surface, not a second source of truth. * * Temporal validity (validFrom/validUntil) is projected into each file's * front-matter and its live status is labelled, an expired record is shown as * `status: expired`, not silently dropped from the projection. */ import type { MemoryClass, MemoryRecord, MemoryScope, MemoryTemporalStatus } from './memory-store.js'; export interface MemoryProjectionOptions { /** Restrict projection to these scopes; defaults to project + team. */ readonly scopes?: readonly MemoryScope[]; } /** * Optional git seam: durably commit the projected files. Injectable for tests. * * The projection only ever commits to a repository it OWNS, one whose * toplevel is the projection directory itself, never a repository the * directory merely sits inside. `projectMemoryToFiles` enforces that policy: * it resolves the enclosing toplevel first, and when the directory is not its * own repository root (nested in a foreign checkout, or not in any repository * at all) it initializes a repository AT the projection directory before * staging. Without this, a projection under a temp/scratch path nested inside * a real checkout would commit into that checkout. */ export interface MemoryProjectionGit { /** * The toplevel directory git would resolve for `dir` (`git -C * rev-parse --show-toplevel`), or null when no repository encloses it. */ resolveToplevel(dir: string): string | null; /** Initialize a repository whose root is exactly `dir`. */ init(dir: string): void; add(dir: string): void; commit(dir: string, message: string): void; } /** * Production git seam for the memory projection: shells out to the system * git, commits with a neutral projection identity (never the operator's), * and tolerates an unchanged tree. */ export declare function createMemoryProjectionGit(): MemoryProjectionGit; /** The parsed content of one projected markdown file. */ export interface MemoryProjectionFile { readonly id: string; readonly path: string; readonly scope?: MemoryScope | undefined; readonly cls?: MemoryClass | undefined; readonly summary: string; readonly detail?: string | undefined; readonly tags: readonly string[]; readonly validFrom?: number | undefined; readonly validUntil?: number | undefined; } export interface MemoryProjectionWriteReport { readonly dir: string; readonly written: readonly string[]; readonly committed: boolean; } /** Render one record as a markdown file body (front-matter + content). */ export declare function projectMemoryRecordToMarkdown(record: MemoryRecord, now?: number): string; /** * Project standing records to `/.md`. Writes one file per in-scope * record. When a git seam is supplied, stages + commits the directory so the * projection is durable and diffable. Never deletes existing files (a store * deletion is surfaced as a proposal on the next diff, not a silent unlink). */ export declare function projectMemoryToFiles(records: readonly MemoryRecord[], dir: string, options?: MemoryProjectionOptions & { readonly now?: number; readonly git?: MemoryProjectionGit; }): MemoryProjectionWriteReport; /** Parse one projected markdown file. Returns null when it has no `id` front-matter key. */ export declare function parseProjectedMemoryFile(path: string, content: string): MemoryProjectionFile | null; /** * One entry in the LIVE memory projection (computed from the store's standing * records, not read from disk), the shape the memory.projections.* wire verbs * expose. `status` is the record's live temporal status (active / expired / * pending), so an expired record is visibly labelled rather than silently * dropped, exactly as the file projection labels it. */ export interface MemoryProjectionEntry { readonly id: string; /** The `.md` filename the file projection would use, a stable per-record handle. */ readonly filename: string; readonly scope: MemoryScope; readonly cls: MemoryClass; readonly summary: string; readonly tags: readonly string[]; readonly confidence: number; readonly reviewState: string; readonly validFrom?: number | undefined; readonly validUntil?: number | undefined; readonly status: MemoryTemporalStatus; } /** * The live projection of standing (project/team) memory records, one metadata * entry per record, oldest first. Does not touch disk. Session-scope records are * excluded (they are not standing memory), matching the file projection's own * scope selection. */ export declare function listMemoryProjections(records: readonly MemoryRecord[], options?: MemoryProjectionOptions & { readonly now?: number; }): MemoryProjectionEntry[]; /** * The live projection of ONE standing record by id: its metadata entry plus the * exact markdown the file projection would write. Returns null when no standing * record has that id (a session-scope or unknown id is an honest miss, not an * empty projection). */ export declare function getMemoryProjection(records: readonly MemoryRecord[], id: string, options?: MemoryProjectionOptions & { readonly now?: number; }): { readonly entry: MemoryProjectionEntry; readonly markdown: string; } | null; /** Read + parse every `*.md` in the projection directory. */ export declare function readProjectedMemoryFiles(dir: string): MemoryProjectionFile[]; export type MemoryProjectionProposalKind = 'update' | 'delete'; /** * A proposed change from the projection round-trip, a review-queue entry, NOT a * write. The caller confirms (or not) each entry; only confirmed entries mutate * the store, through the registry's own update/delete. */ export interface MemoryProjectionProposal { readonly kind: MemoryProjectionProposalKind; readonly id: string; /** Honest human-readable reason this change is proposed. */ readonly reason: string; /** For 'update': the record fields the edited file would change. */ readonly changedFields?: readonly string[] | undefined; /** The desired field values parsed from the file (for 'update'). */ readonly desired?: { readonly scope?: MemoryScope | undefined; readonly summary?: string | undefined; readonly detail?: string | undefined; readonly tags?: readonly string[] | undefined; readonly validFrom?: number | null | undefined; readonly validUntil?: number | null | undefined; } | undefined; } /** * Diff the current record set against the projected files and produce proposals. * PURE, no I/O. An edited file whose fields differ from its record yields an * `update` proposal; an in-scope record with NO file yields a `delete` proposal * (the user removed the file); a file with no matching record is ignored (its * record is already gone). Nothing here mutates the store. */ export declare function diffProjectionToProposals(records: readonly MemoryRecord[], files: readonly MemoryProjectionFile[], options?: MemoryProjectionOptions): MemoryProjectionProposal[]; /** The registry surface the projection apply needs, update + delete only. */ export interface MemoryProjectionRegistry { update(id: string, patch: { scope?: MemoryScope; summary?: string; detail?: string; tags?: string[]; validFrom?: number | null; validUntil?: number | null; }): MemoryRecord | null; delete(id: string): boolean; } export interface MemoryProjectionApplyReceipt { readonly applied: readonly MemoryProjectionProposal[]; /** Proposals the caller did NOT confirm, left untouched (the gate). */ readonly skipped: readonly MemoryProjectionProposal[]; /** Proposals confirmed but whose store mutation returned no record / false. */ readonly failed: readonly MemoryProjectionProposal[]; } /** * Apply ONLY the proposals the caller confirms, through the registry's own * update/delete. This is the confirmation gate: a proposal is never applied * unless `confirm(proposal)` returns true, a file edit can never become a * silent store write. Unconfirmed proposals are recorded as skipped. */ export declare function applyMemoryProjectionProposals(registry: MemoryProjectionRegistry, proposals: readonly MemoryProjectionProposal[], options: { readonly confirm: (proposal: MemoryProjectionProposal) => boolean; }): MemoryProjectionApplyReceipt; //# sourceMappingURL=memory-file-projection.d.ts.map