/** * capture.ts, proposing an occasion, confirming it once, and removing one. * * Split out of `service.ts` when that file reached the repo's 800-line cap. * Nothing here has state: each function takes the profile source it reads and * the writer it writes through, so the service keeps the sequence and this file * keeps the shape of a capture. * * ## Confirm once, at the time * * *"Noted your anniversary as 12 September, right?"* One line, at the moment * the owner can still catch a mishearing, and silent afterwards, no * re-confirmation at nudge time. The reason is arithmetic rather than * politeness: for an annual date a silent write means they discover the * error up to eleven months later, when it is far too late to matter. * * ## The kind is asked in the same interaction, and never inferred * * {@link confirmOccasion} refuses without one. A parent's death anniversary is * worth remembering, and a cheerful "you'll probably want to sort something" * against it would be genuinely bad; there is no rule that reads a label and * gets that right, so there is no rule. */ import type { AuthoritySurface } from '../security/untrusted-content.js'; import type { ProfileSurface } from '../owner-profile/types.js'; import { type OccasionProfileSource } from './reader.js'; /** The profile writes capture and removal need. Satisfied by OwnerProfileStore. */ export interface OccasionProfileWriter { append(input: { readonly section: string; readonly text: string; readonly surface: ProfileSurface; readonly said: string; readonly authority: AuthoritySurface; }): Promise; forget(input: { readonly section?: string | undefined; readonly text?: string | undefined; readonly authority: AuthoritySurface; }): Promise; } /** What a capture proposes, before anything is written. */ export interface OccasionProposal { readonly ok: boolean; /** Why the proposal cannot be confirmed as it stands. */ readonly reason: string | null; /** The line that would be written, exactly. */ readonly line: string; /** The one-line confirmation to put to the owner. Empty when `ok` is false. */ readonly confirmation: string; /** True when the owner still has to choose the kind. Never guessed. */ readonly needsKind: boolean; /** Dates already recorded for this title that disagree with the new one. */ readonly conflictsWith: readonly string[]; } /** The result of a confirmed capture or a removal. */ export interface OccasionWriteOutcome { readonly ok: boolean; readonly reason: string | null; readonly occasionId: string; readonly disclosure: string; /** Records dropped because their occasion is gone. Removals only. */ readonly droppedRecords: number; } export interface ProposeOccasionInput { readonly title: string; readonly date: string; readonly kind?: string | undefined; readonly person?: string | undefined; readonly recurrence?: string | undefined; readonly leadDays?: number | undefined; /** * True when the occasion is about the OWNER's own occasion. * * Written onto the line as `for me`, and the reason it is captured rather * than worked out later: the owner knows when their own birthday is, so an * occasion about them that they only have to remember is never pushed at * them. Stating it at capture is the reliable path; the reader can also * resolve a possessive title against the owner's declared name, but only * they can settle the ambiguous ones. */ readonly self?: boolean | undefined; } export interface ConfirmOccasionInput extends ProposeOccasionInput { readonly kind: string; readonly surface: ProfileSurface; readonly said: string; readonly authority: AuthoritySurface; } /** * Work out what would be written, and the one line to put to the owner. * * Nothing is written. A conflict with something already recorded is REPORTED * here rather than resolved: the owner said two different things, only they * know which was right, and silently taking the newer value is the one * behaviour ruled out. */ export declare function proposeOccasion(source: OccasionProfileSource, input: ProposeOccasionInput): OccasionProposal; /** Write the confirmed occasion. Refuses without a kind rather than choosing one. */ export declare function confirmOccasion(source: OccasionProfileSource, writer: OccasionProfileWriter, input: ConfirmOccasionInput): Promise; export interface ProposePlanInput { readonly title: string; readonly from: string; readonly to: string; readonly away?: boolean | undefined; readonly destination?: string | undefined; /** * Everything else the owner said about it, one detail per entry: a * confirmation number, a flight and its times, who is travelling, why * they are going. * * Kept because a trip stripped to its dates answers "am I away" and nothing * else, and the itinerary was pasted precisely so the details would be * there later. Each entry is normalised to survive the line grammar * (`normalizePlanDetail`) and then the whole line is re-read to prove it * round-trips before anything is written. */ readonly details?: readonly string[] | undefined; } export interface ConfirmPlanInput extends ProposePlanInput { readonly surface: ProfileSurface; readonly said: string; readonly authority: AuthoritySurface; } /** The same two-step capture, for a plan. Plans never prompt; they are known. */ export declare function proposePlan(input: ProposePlanInput): OccasionProposal; export declare function confirmPlan(writer: OccasionProfileWriter, input: ConfirmPlanInput): Promise; /** * Remove an occasion, and everything the machine remembered about it. * * One confirmation, carried by the caller. Not unquestioned and not an argument: * people divorce and people die, and removing an occasion should take one * sentence and one confirm. * * The profile line goes first and the machine state second. That order matters, * if the state drop failed after the line was gone, the next sweep reaps it as * an orphan anyway, whereas the other order could leave the line in place with * its history removed and nothing to explain why. */ export declare function removeOccasion(source: OccasionProfileSource, writer: OccasionProfileWriter, dropState: (occasionId: string) => Promise, input: { readonly occasionId: string; readonly confirmed: boolean; readonly authority: AuthoritySurface; }): Promise; //# sourceMappingURL=capture.d.ts.map