/** * Principle Tree Ledger — file-based ledger for principle entries. * * Lives in principles-core so pd-cli can read/write the ledger without * importing openclaw-plugin private code. * * M8: Single-path ledger. The ledger file is at: * {stateDir}/principle_training_state.json * * PRI-459: This module is the SINGLE source of truth for parsing, * serialization, AND mutation of principle_training_state.json. Every * read-modify-write acquires a cross-process file lock (see withLock above), * eliminating the dual-writer lost-update class. The openclaw-plugin ledger * is now a thin re-export adapter over this module. */ import type { Principle, Rule, Implementation, PrincipleValueMetrics, LedgerPrinciple, LedgerRule, LedgerTreeStore, LegacyPrincipleTrainingState, LegacyPrincipleTrainingStore, HybridLedgerStore } from './runtime-v2/types/ledger-store.js'; import { TREE_NAMESPACE } from './runtime-v2/types/ledger-store.js'; import type { ImplementationLifecycleState } from './runtime-v2/types/principle-enums.js'; import type { ReuseEvidenceActor, ReuseEvidenceEntry } from './runtime-v2/types/principle-schema.js'; export type { Principle, Rule, Implementation, PrincipleValueMetrics, LedgerPrinciple, LedgerRule, LedgerTreeStore, LegacyPrincipleTrainingState, LegacyPrincipleTrainingStore, HybridLedgerStore, ImplementationLifecycleState, }; export type { ReuseEvidenceActor, ReuseEvidenceEntry }; export { TREE_NAMESPACE }; /** * PRI-915: why a ledger read produced the store it produced. `empty` is a * design-legal TRUE empty ledger (no file yet — fresh workspace — or a * zero-byte file): membership answers against it are real "non-member" * verdicts. `unreadable` is a transient outage (the file exists but cannot be * read/parsed): membership answers against it are NOT decidable, and callers * that would report "data drift" must consult availability first. */ export type LedgerFileAvailability = { status: 'ok'; } | { status: 'empty'; } | { status: 'unreadable'; problem: string; }; export declare function readLedgerFileState(stateDir: string): { ledger: HybridLedgerStore; availability: LedgerFileAvailability; }; export declare class LockAcquisitionError extends Error { readonly filePath: string; readonly lockPath: string; constructor(message: string, filePath: string, lockPath: string); } interface LockOptions { maxRetries?: number; baseRetryDelayMs?: number; maxRetryDelayMs?: number; lockStaleMs?: number; lockSuffix?: string; } /** * Run `fn` while holding the ledger file lock. FAIL-LOUD: if the lock cannot * be acquired after bounded retries, throws LockAcquisitionError carrying the * file + lock path (EP-03 / ERR-009 — no silent fallback). * * Exported so tests and integrators can drive the lock primitive directly with * a tight retry budget (the public mutators use production defaults). */ export declare function withLock(filePath: string, fn: () => T, options?: LockOptions): T; export declare function loadLedger(stateDir: string): HybridLedgerStore; export declare function saveLedger(stateDir: string, store: HybridLedgerStore): void; export declare function addPrincipleToLedger(stateDir: string, principle: LedgerPrinciple): LedgerPrinciple; export declare function updatePrinciple(stateDir: string, principleId: string, updates: Partial): LedgerPrinciple; /** * Thrown when the ledger file's content is not trustworthy for a read-modify- * write: it parses to an empty principles tree while the file exists on disk * (empty, corrupt, or truncated), or an entity's persisted reuseEvidence field * has a malformed shape. Unlike a missing principle, writing over this state * would DESTROY ledger content, so the write refuses instead of proceeding * (rc-3). */ export declare class LedgerIntegrityError extends Error { readonly filePath: string; constructor(message: string, filePath: string); } /** * Append one reuse-evidence entry to an existing Principle (PRI-917 PR3B, * SPEC v0.2.1 §12). The ONLY writer for `Principle.reuseEvidence`. * * Runs entirely inside the single-writer file lock via mutateLedger — * read → integrity guard → dedupe → append → save — so a concurrent append * can never be lost, and there is no load-then-update window between * processes. Do NOT implement appends via addPrincipleToLedger (it replaces * the whole entry and would silently drop existing evidence) or via an * external loadLedger+updatePrinciple sequence (read outside the lock). * * Fail-closed: * - a ledger file that exists but parses to an empty principles tree (empty * file, corrupt, or truncated) is treated as corruption — the write * refuses rather than risk replacing the corpus with the new entry alone * (LedgerIntegrityError); * - a missing principleId refuses the write (no file mutation); * - a malformed entry refuses the write. * * Idempotency key: (principleId, entry.candidateId). A candidate resolves * into a principle at most once, so replaying the same decision is a no-op * that returns the existing evidence with `appended: false` (INV-R07). A * no-op still goes through the normal save path, so the tree's lastUpdated * is refreshed; the ledger content itself is unchanged. * * Append-only (INV-R05): no other Principle field — including updatedAt — * is touched, and existing entries are never rewritten or removed. Target * eligibility (e.g. archived/deprecated exclusion, SPEC §15) is the * retrieval/proposal layer's invariant; this writer records the decision * verbatim. */ export declare function appendReuseEvidence(stateDir: string, principleId: string, entry: ReuseEvidenceEntry): { principleId: string; reuseEvidence: ReuseEvidenceEntry[]; appended: boolean; }; export declare function updatePrincipleValueMetrics(stateDir: string, principleId: string, metrics: PrincipleValueMetrics): PrincipleValueMetrics; export declare function getLedgerFilePathPublic(stateDir: string): string; export interface PrincipleSubtree { principle: LedgerPrinciple; rules: { rule: LedgerRule; implementations: Implementation[]; }[]; } export declare function createRule(stateDir: string, rule: LedgerRule): LedgerRule; export declare function createImplementation(stateDir: string, implementation: Implementation): Implementation; export declare function updateRule(stateDir: string, ruleId: string, updates: Partial): LedgerRule; export declare function deleteRule(stateDir: string, ruleId: string): LedgerRule | undefined; export declare function updateImplementation(stateDir: string, implementationId: string, updates: Partial): Implementation; export declare function deleteImplementation(stateDir: string, implementationId: string): Implementation | undefined; export declare function listImplementationsForRule(stateDir: string, ruleId: string): Implementation[]; export declare function getPrincipleSubtree(stateDir: string, principleId: string): PrincipleSubtree | undefined; export declare function isValidLifecycleTransition(from: ImplementationLifecycleState, to: ImplementationLifecycleState): boolean; export declare function getAllowedTransitions(from: ImplementationLifecycleState): ImplementationLifecycleState[]; /** * Transition an implementation's lifecycle state with validation. * Throws on invalid transitions or missing implementation (fail loud). */ export declare function transitionImplementationState(stateDir: string, implementationId: string, newState: ImplementationLifecycleState): Implementation; export declare function listRuleImplementationsByState(stateDir: string, ruleId: string, state: ImplementationLifecycleState): Implementation[]; export declare function findActiveImplementation(stateDir: string, ruleId: string): Implementation | null; export declare function saveLedgerAsync(stateDir: string, store: HybridLedgerStore): Promise; export declare function updateTrainingStore(stateDir: string, mutate: (store: LegacyPrincipleTrainingStore) => void): void; //# sourceMappingURL=principle-tree-ledger.d.ts.map