1import type { SchemaCompatState } from "./schema-compat.ts";2import type { RuntimeStartupFailure } from "./startup-recovery.ts";3import type { RuntimeStoreStatus } from "./store-status.ts";4import type { WriteDurability } from "./table-store.ts";56/** One structured entry recorded by the built-in `s.log` effect unit. */7export type EffectLogEntry = {8 /** The author-chosen log label. */9 label: string;10 /** The declaring verb's name, or `null` for writes without a verb. */11 verb: string | null;12 /** The written table's name. */13 table: string;14 /** The written row's id. */15 rowId: string;16 /** Which fate settled the write. */17 fate: "synced" | "rejected";18 /** Epoch milliseconds when the entry was recorded. */19 at: number;20};2122// Recent-entry window for the s.log diagnostic feed.23const maxEffectLogEntries = 20;2425/** Appends one `s.log` entry, keeping the bounded recent window. */26export function recordEffectLogEntry(27 diagnostics: RuntimeDiagnostics,28 entry: EffectLogEntry,29): void {30 diagnostics.effectLog = [...diagnostics.effectLog.slice(-(maxEffectLogEntries - 1)), entry];31}3233/**34 * Runtime-owned observability counters. These describe the framework's storage,35 * subscription, and write machinery and never reference any application schema.36 *37 * For a device-status widget the load-bearing fields are38 * {@link RuntimeDiagnostics.storeStatus}, {@link RuntimeDiagnostics.schemaCompat},39 * `journaledPendingWrites` (the "N changes waiting to sync" count), and40 * `lastWriteDurability`; the remaining counters exist for the development41 * inspector and bug reports.42 */43export type RuntimeDiagnostics = {44 /** Persistent-storage posture: requested, granted with the driver open, or failed. */45 storageState: "persistent-requested" | "persistent-driver-open" | "failed";46 /** The boot failure the runtime is stuck on, or `null` when boot succeeded. */47 startupFailure: RuntimeStartupFailure | null;48 /** Whether the configured store answered; feeds connected/unreachable UI. */49 storeStatus: RuntimeStoreStatus;50 /** This boot's sync-owner verdict; `mismatch` means transport was suppressed. */51 syncOwner: SyncOwnerDiagnostic;52 /** The schema gate's verdict; `data-ahead` means this device is read-only. */53 schemaCompat: SchemaCompatState;54 /** Storage clients opened since page load, runtime recreations included. */55 clientsCreated: number;56 /** Storage clients currently open; one for a healthy runtime. */57 activeClients: number;58 /** Mounted subscribers across live-query and table stores. */59 activeConsumers: number;60 /** Engine subscriptions currently open to feed those consumers. */61 activeVendorSubscriptions: number;62 /** Engine subscriptions opened since page load. */63 totalVendorSubscriptions: number;64 /** Engine mutation-error listeners currently attached. */65 activeMutationListeners: number;66 /** Engine mutation-error listeners attached since page load. */67 totalMutationListeners: number;68 /** Engine subscription teardowns performed since page load. */69 unsubscribeCalls: number;70 /** Writes whose local-durability wait completed. */71 localWaitCalls: number;72 /** Writes currently awaiting local durability. */73 pendingLocalWrites: number;74 /** Writes currently awaiting the store's confirmation. */75 pendingGlobalWrites: number;76 /**77 * The most recent write's deepest durability: `local` is saved on this78 * device, `global` is confirmed by the store. See {@link WriteDurability}.79 */80 lastWriteDurability: WriteDurability;81 /** Writes refused or rejected since boot, including guard refusals. */82 mutationErrors: number;83 /** Journaled writes that have not yet settled as synced or rejected. */84 journaledPendingWrites: number;85 /** Pending writes whose declared intent lifespan has passed (surfacing only). */86 expiredPendingWrites: number;87 /** Effect handler invocations that threw; failed obligations re-arm at boot. */88 effectHandlerFailures: number;89 /** Obligations retired because their delivery window closed undelivered. */90 expiredObligations: number;91 /** Obligations quarantined after repeated handler failures. */92 quarantinedObligations: number;93 /** Recent structured entries recorded by the built-in `s.log` effect unit. */94 effectLog: readonly EffectLogEntry[];95 /** Recent shared-field key alerts: substitutions, forged wraps, self-key96 * conflicts. A non-empty list is the detection surface the threat model97 * promises — render it, never swallow it. */98 sharedFieldAlerts: readonly SharedFieldAlert[];99};100101/**102 * The boot-time sync-owner adjudication. `unchecked` means the runtime was103 * not connecting (nothing to adjudicate); `self` means the account in hand104 * owns the election; `mismatch` means sync on this device was elected by a105 * different account, so the runtime booted with transport suppressed rather106 * than writing into the owner's store. The remediation is explicit: stop107 * sync (releasing the election) or restore the owning account.108 */109export type SyncOwnerDiagnostic =110 | { state: "unchecked" }111 | { state: "self" }112 | { state: "mismatch"; owner_user_id: string | null };113114/** One detected shared-field key anomaly. */115export type SharedFieldAlert = {116 /**117 * The anomaly kind: `peer-key-changed` — a sender's published key no longer118 * matches its pin, so its wraps are refused until the user re-trusts the119 * key; `self-key-conflict` — the directory holds a different key for this120 * account than the one derived locally; `wrap-invalid` — a wrapped key121 * failed authentication or validation and was not installed.122 */123 code: "peer-key-changed" | "self-key-conflict" | "wrap-invalid";124 /** The account the anomaly concerns (the sender or self). */125 userId: string;126 /** Human-readable description for the alert surface. */127 detail: string;128 /** ISO-8601 timestamp of when the anomaly was detected. */129 at: string;130};131132export function createDiagnostics(): RuntimeDiagnostics {133 return {134 storageState: "persistent-requested",135 startupFailure: null,136 storeStatus: { state: "unchecked", reason: "sync-not-connected" },137 syncOwner: { state: "unchecked" },138 schemaCompat: { state: "unchecked", reason: "inactive" },139 clientsCreated: 0,140 activeClients: 0,141 activeConsumers: 0,142 activeVendorSubscriptions: 0,143 totalVendorSubscriptions: 0,144 activeMutationListeners: 0,145 totalMutationListeners: 0,146 unsubscribeCalls: 0,147 localWaitCalls: 0,148 pendingLocalWrites: 0,149 pendingGlobalWrites: 0,150 lastWriteDurability: "none",151 mutationErrors: 0,152 journaledPendingWrites: 0,153 expiredPendingWrites: 0,154 effectHandlerFailures: 0,155 expiredObligations: 0,156 quarantinedObligations: 0,157 effectLog: [],158 sharedFieldAlerts: [],159 };160}161162const maxSharedFieldAlerts = 20;163164/** Appends a shared-field alert, keeping the most recent entries. */165export function recordSharedFieldAlert(166 diagnostics: RuntimeDiagnostics,167 alert: SharedFieldAlert,168): void {169 diagnostics.sharedFieldAlerts = [170 ...diagnostics.sharedFieldAlerts.slice(-(maxSharedFieldAlerts - 1)),171 alert,172 ];173}