diff --git a/docs/development/storage-and-history-boundary.md b/docs/development/storage-and-history-boundary.md index 569ed10..ba6710b 100644 --- a/docs/development/storage-and-history-boundary.md +++ b/docs/development/storage-and-history-boundary.md @@ -41,6 +41,38 @@ ownership statement here is a contract direction for a later implementation; the (bitty-plugins-docs), `W-139` (bitty-plugin-sdk), and `W-146` (bitty / `CTX-0939`). +## Erratum (2026-10-05) + +This erratum amends the per-plugin KV ceiling contract to the implemented +values. Accepted status is kept; this is a correction, not a normative +weakening. + +- Corrected ceilings: 64 KiB total per plugin + (`STORE_MAX_TOTAL_BYTES = 64 * 1024`), 8 KiB per value (unchanged, + `STORE_MAX_VALUE_BYTES = 8 * 1024`), JSON depth 16 + (`JSON_MAX_DEPTH = 16`), 256 entries per plugin + (`STORE_MAX_ENTRIES = 256`); atomic commit with no partial write is + unchanged. +- Provenance: `bitty-storage` `src/ceiling.rs` ceiling constants, + byte-for-byte with the Core + `crates/bitty-runtime/src/plugin_runtime/store.rs` bounds, confirmed by + the `bitty-storage` CTX-0003/0004 parity evidence. +- Superseded prose: the budget table below previously stated 256 KiB total + per plugin, depth 8, and 1024 nodes. Read the corrected ceilings above + wherever that prose appears. +- Why this is not a weakening: the implemented size and entry bounds are + strictly tighter than the superseded prose. Depth 16 versus 8 is the one + documented exception: it carries Core's pre-existing JSON depth, already + enforced by Core. +- Dual-layer scope note (resolved 2026-10-05): the Lua marshalling layer + previously enforced the older 256 KiB / depth 8 / 1024-node ceilings; its + alignment to the ceilings above landed in bitty CTX-0970 (PR #1713, + `a3dbf9d`). Both layers now enforce 64 KiB total, 8 KiB per value, and + depth 16; the count bound is 256 in each layer's native unit (entries in + `bitty-runtime`, nodes in `bitty-lua`). +- No silent changes: any future bound change is a new security-reviewed + decision, not an edit to this erratum. + ## Purpose and scope This specification is the accepted reconciliation of the history and storage scope. It fixes one ownership and @@ -159,12 +191,12 @@ sensitivities, and budgets. They must not be merged into one universal database or one shared store, and none may become the default sink for raw terminal output. -| Storage object | Owner (contract and policy) | Store mechanics | Lifecycle | Retention and deletion | Persists by default | Budget or bound | -| -------------------- | ------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | -| Segmented transcript | History plugin (`history`, candidate) under plugin-facing policy fixed by `W-137`; Core owns the event source | Proposed extracted `bitty-storage` mechanics under the accepted reconciliation | Created only while opt-in capture is enabled for a panel; segments seal and roll; active capture ends at purging or uninstalling the plugin or at panel close per policy, and retained history is not deleted merely by closing a panel | Retention by age and size with sealed-segment deletion; explicit purge is authoritative and must remove bytes; no resurrect from a derived index | No; raw stdout and PTY bytes are not persisted without explicit opt-in | Per-panel and per-store byte and age caps; bounded sealed-segment size; compression; no unbounded growth | -| Command history | History plugin under policy fixed by `W-137`; Core owns the event source, and `OSC 133` is advisory only | Proposed extracted `bitty-storage` mechanics for the bytes and rebuildable index | Appended per command boundary while capture is enabled; small and long-lived; external links may dangle after provider removal | Explicit purge or uninstall; age and count bounds; dangling external references surface as typed unavailability, never as silent gaps | No; capture is opt-in, and recording input is a separate opt-in | Command-count, age, and byte bounds; commands-only is the least sensitive tier; optional output tier stays bounded and separate | -| Session snapshots | Core, under the Restore/Persistence separation in ADR 0013 and the Panel Runtime save/restore rules | Core save/restore path; storage mechanics may move only if restore re-derivation stays Core | Written by Core on normal interactive exit; read on restore; volatile structure is re-derived on load and fails closed to a clean start | Replaced by the next exit save; deletion removes the session file; safe mode never reads it; restore never writes | Yes for bounded scrollback and cwd on normal interactive exit (open point below); never under safe or headless mode | Versioned whole-file bound; bounded per-pane scrollback and captured metadata; atomic temp-plus-rename | -| Per-plugin KV | `bitty-plugin-host` Lua host service (Core); SDK surface fixed by `W-139`, plugin-facing policy by `W-137` | Core host service today; mechanics may move to `bitty-storage` while the contract stays with the SDK | Plugin-scoped and generation-independent: values committed before a generation is disposed remain readable by the next generation | Deleted only by uninstall, an explicit `nil` write, or an explicit user purge; no expiry and no eviction by construction | Only plugin-authored writes; not a default sink for terminal output or secrets | Published plugin-store ceilings: 256 KiB total per plugin, 8 KiB per value, depth 8, 1024 nodes; atomic commit with no partial write | +| Storage object | Owner (contract and policy) | Store mechanics | Lifecycle | Retention and deletion | Persists by default | Budget or bound | +| -------------------- | ------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | +| Segmented transcript | History plugin (`history`, candidate) under plugin-facing policy fixed by `W-137`; Core owns the event source | Proposed extracted `bitty-storage` mechanics under the accepted reconciliation | Created only while opt-in capture is enabled for a panel; segments seal and roll; active capture ends at purging or uninstalling the plugin or at panel close per policy, and retained history is not deleted merely by closing a panel | Retention by age and size with sealed-segment deletion; explicit purge is authoritative and must remove bytes; no resurrect from a derived index | No; raw stdout and PTY bytes are not persisted without explicit opt-in | Per-panel and per-store byte and age caps; bounded sealed-segment size; compression; no unbounded growth | +| Command history | History plugin under policy fixed by `W-137`; Core owns the event source, and `OSC 133` is advisory only | Proposed extracted `bitty-storage` mechanics for the bytes and rebuildable index | Appended per command boundary while capture is enabled; small and long-lived; external links may dangle after provider removal | Explicit purge or uninstall; age and count bounds; dangling external references surface as typed unavailability, never as silent gaps | No; capture is opt-in, and recording input is a separate opt-in | Command-count, age, and byte bounds; commands-only is the least sensitive tier; optional output tier stays bounded and separate | +| Session snapshots | Core, under the Restore/Persistence separation in ADR 0013 and the Panel Runtime save/restore rules | Core save/restore path; storage mechanics may move only if restore re-derivation stays Core | Written by Core on normal interactive exit; read on restore; volatile structure is re-derived on load and fails closed to a clean start | Replaced by the next exit save; deletion removes the session file; safe mode never reads it; restore never writes | Yes for bounded scrollback and cwd on normal interactive exit (open point below); never under safe or headless mode | Versioned whole-file bound; bounded per-pane scrollback and captured metadata; atomic temp-plus-rename | +| Per-plugin KV | `bitty-plugin-host` Lua host service (Core); SDK surface fixed by `W-139`, plugin-facing policy by `W-137` | Core host service today; mechanics may move to `bitty-storage` while the contract stays with the SDK | Plugin-scoped and generation-independent: values committed before a generation is disposed remain readable by the next generation | Deleted only by uninstall, an explicit `nil` write, or an explicit user purge; no expiry and no eviction by construction | Only plugin-authored writes; not a default sink for terminal output or secrets | Published plugin-store ceilings: 64 KiB total per plugin, 8 KiB per value, depth 16, 256 entries; atomic commit with no partial write | ### Segmented transcript