Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 115 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ All notable changes to `coven-threads-core` are documented here.

## [0.2.0] — unreleased

Compatibility note: `AuditEventType::ApplyAudit` and `WardAuditRecord::detail`
expand public exhaustive APIs. Downstream `match` arms and struct literals can
break under Cargo `^0.1`, so this ships on the `0.2.x` line rather than as a
`0.1.x` patch release.

### Added

- `AuditEventType::ApplyAudit` variant + `"apply_audit"` tag in `WARD_AUDIT_SCHEMA_SQL`'s CHECK constraint (coven-threads#5).
Expand All @@ -21,14 +26,121 @@ All notable changes to `coven-threads-core` are documented here.
- **Wire envelope** — validated daemon-to-client label round-trip.
- Deterministic compilation of the retired Ward identity declarations for name, person, pronouns, purpose, and Coven membership.
- RFC-0001 provenance events with persistence-level required-detail constraints.
- `ward_audit_migration_sql` — transaction SQL builder that preserves existing detail payloads while also supporting pre-detail legacy tables. Schema ownership is tracked in `ward_schema_meta`, not SQLite's database-global `PRAGMA user_version`.
- `WARD_AUDIT_SCHEMA_STATE_SQL` plus stable state tags (`missing`,
`legacy_v013`, `current_v020`, `unknown`) — reusable table-local schema
fingerprint contract for daemon callers, using exact stored
`main.sqlite_master.sql` fingerprints for the durable `main.ward_audit`
table, explicit main indexes, and append-only main triggers, plus ordered
durable column metadata discovered through schema-qualified PRAGMAs.
Across all durable states, the reserved main-schema `ward_audit` /
`ward_audit_*` namespace is whitelisted to exactly `main.ward_audit`, its two
explicit indexes, its two append-only triggers, and all four Phase-5
authority triggers attached to `main.ward_audit`; every other reserved main
object (including `ward_audit_new`, views, backup/shadow tables, or
reserved-name indexes/triggers attached elsewhere) fail-closes as `unknown`.
`missing` means `main.ward_audit` is absent, no unexpected durable reserved
object exists, **and** no temp shadow/reserved temp object exists; any
temp-schema table/view/index/trigger named `ward_audit` or `ward_audit_*`
also fail-closes as `unknown`. The controlled fresh/repaired-migration
v0.2.0 table SQL variants and the exact comment-free quoted variant emitted
by the daemon-pinned predecessor preserve the Phase-5 proposal-window and
memory-admission CHECK clauses, so exact deployed stores remain current.
Those variants and the shipped v0.1.3 table SQL are the only accepted shapes;
no whitespace-destroying normalization is applied.
- `WARD_AUDIT_SCHEMA_SQL` — atomic, self-guarding schema initialization for the
exact `current_v020` fingerprint. It now begins with `BEGIN IMMEDIATE`, so
the main-database write reservation is acquired before any guard read or
classification. That makes concurrent initializers serialize cleanly: the
second caller waits, re-runs the guard against the winner's committed schema,
and idempotently sees exact `current_v020` instead of racing into
`sqlite_master` lock errors. The SQL still permits only `missing` or exact
`current_v020` before any mutation, uses idempotent `IF NOT EXISTS` DDL for
daemon compatibility, targets durable schema objects in `main` wherever
SQLite syntax permits, then requires exact `current_v020` before `COMMIT`.
Exact `legacy_v013`, every drifted `unknown` shape, any unexpected durable
reserved object, and any temp shadow/reserved temp object fail closed;
callers must `ROLLBACK` after any init error.
- `WARD_AUDIT_MIGRATION_V020_SQL` — transaction SQL for a detail-preserving
rebuild of `main.ward_audit` guarded by the exact `legacy_v013` fingerprint
plus durable-whitelist and temp-shadow rejection. The daemon should query
`WARD_AUDIT_SCHEMA_STATE_SQL`, initialize when missing, migrate only
`legacy_v013`, continue on `current_v020`, and fail closed on `unknown`. The
migration independently re-checks `legacy_v013` before any `ALTER`, now
inside `BEGIN IMMEDIATE` so concurrent migrators serialize before the guard
read. After the rebuild, a distinct TEMP postcondition guard reruns the same
shared schema-state CTE/predicates and requires exact `current_v020` before
`COMMIT`, so a caller cannot durably commit a self-unknown rebuilt schema if
unexpected `main.ward_audit` / `main.ward_audit_*` drift appears mid-
transaction. After the first caller upgrades, a second caller waits, re-runs
the guard against exact current, and fails only at the legacy guard instead
of racing into `sqlite_master` lock errors. The SQL otherwise uses the same
exact stored-table + column/index/trigger predicate plus durable namespace
whitelist and temp-shadow rejection, then copies rows into the replacement
table without discarding evidence.
- Exhaustiveness test `schema_names_all_event_tags` extended to cover `ApplyAudit`.
- New tests: `for_apply_produces_correct_shape`, `for_apply_roundtrips_json`, `migration_sql_contains_apply_audit_and_detail_column`.
- New tests: `for_apply_produces_correct_shape`,
`for_apply_roundtrips_json`,
`schema_state_query_returns_missing_on_empty_db`,
`fresh_schema_sql_initializes_current_schema_atomically_and_enforces_append_only`,
`fresh_schema_rejects_invalid_proposal_window_detail`,
`fresh_schema_rejects_invalid_memory_admission_detail`,
`exact_legacy_fixture_returns_legacy_v013`,
`exact_current_schema_returns_current_v020`,
`pre_fingerprint_migrated_schema_remains_current_v020`,
`current_schema_sql_reruns_idempotently_and_preserves_rows_and_objects`,
`fresh_and_migrated_current_schemas_use_controlled_exact_sql_variants`,
`current_schema_with_spaced_event_type_literal_is_unknown`,
`legacy_schema_with_spaced_event_type_literal_is_unknown_and_guard_preserves_state`,
`legacy_schema_sql_rejects_and_rollback_preserves_state`,
`fresh_schema_preserves_user_version_zero_and_creates_current_shape`,
`fresh_schema_preserves_user_version_ninety_nine_and_creates_current_shape`,
`legacy_plus_extra_column_and_data_is_unknown_and_guard_preserves_state`,
`legacy_schema_with_extra_table_check_is_unknown_and_guard_preserves_constraint`,
`legacy_schema_with_extra_unique_is_unknown_and_guard_preserves_constraint`,
`current_schema_with_extra_table_check_is_unknown`,
`current_schema_with_extra_unique_is_unknown`,
`current_schema_missing_append_only_trigger_is_unknown_and_update_succeeds`,
`current_schema_with_extra_index_is_unknown`,
`current_schema_with_desc_index_is_unknown`,
`current_schema_with_collated_index_is_unknown`,
`current_schema_with_extra_reserved_main_view_or_table_is_unknown_and_schema_sql_preserves_state`,
`current_schema_with_altered_trigger_error_literal_is_unknown`,
`current_schema_with_altered_trigger_body_is_unknown`,
`schema_and_migration_sql_use_begin_immediate`,
`migration_sql_uses_distinct_pre_and_post_guards_before_commit`,
`absent_ward_audit_with_reserved_index_collision_is_unknown_and_schema_sql_preserves_other_objects`,
`absent_ward_audit_with_reserved_trigger_collision_is_unknown_and_schema_sql_preserves_other_objects`,
`unknown_partial_current_schema_rejects_schema_sql_and_preserves_state`,
`migration_rejects_current_schema_rows_with_detail_and_preserves_state`,
`legacy_schema_with_preexisting_main_ward_audit_new_is_unknown_and_guard_rejects_before_alter`,
`legacy_schema_upgrade_passes_post_guard_and_preserves_append_only_behavior`,
`post_guard_rejects_durable_drift_and_rollback_restores_exact_legacy_state`,
`rerunning_migration_after_legacy_upgrade_errors_and_preserves_rows`,
`concurrent_schema_initialization_serializes_without_locked_errors`,
`concurrent_legacy_migration_waits_then_rejects_current_at_guard`,
`sqlite_post_alter_failure_rollback_restores_legacy_schema_for_production_migration_contract`,
`schema_qualified_table_valued_pragmas_resolve_main_and_temp_separately`,
`current_main_with_temp_shadow_is_unknown_and_guards_preserve_main_and_temp_rows`,
`missing_main_with_temp_ward_audit_shadow_is_unknown_and_schema_sql_rejects_without_creating_main`,
`missing_main_with_reserved_temp_objects_is_unknown_and_schema_sql_preserves_temp_objects`,
`legacy_main_with_temp_shadow_is_unknown_and_migration_rejects_before_mutating_either_schema`, and
`unqualified_insert_targets_temp_shadow_while_schema_state_stays_unknown`.

### Design notes

- **Column approach (Option A):** `diff_hash` carries `next_sha256`; `prev_sha256` + `bytes_written` ride in the new `detail` TEXT column as JSON. This avoids a second table rebuild by not adding typed hash columns while keeping the data query-accessible via SQLite JSON functions.
- **Migration approach (Option B-lite):** exports a migration builder and schema-action classifier so the daemon can perform the correct guarded rebuild for the observed source schema; component metadata avoids colliding with unrelated tables in `coven.sqlite3`.
- **Migration approach (Option B-lite):** exports
`WARD_AUDIT_SCHEMA_STATE_SQL`, `WARD_AUDIT_SCHEMA_SQL`, and
`WARD_AUDIT_MIGRATION_V020_SQL` so the daemon can classify `missing` /
`legacy_v013` / `current_v020` / `unknown`, perform a quiet fail-closed init
or upgrade, and recover cleanly with explicit rollback after any init or
migration error. The durable contract is explicitly `main.ward_audit`, and
the reserved durable namespace is valid only for that table plus its two
explicit indexes and two append-only triggers; any extra durable reserved
object or TEMP shadow/reserved temp object keeps the contract fail-closed as
`unknown`. Production dependencies stay unchanged, while `coven-threads-core`
now carries bundled `rusqlite` as a dev-dependency for executable migration
tests.

## [0.1.3] — prior release

Expand Down
112 changes: 112 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ uuid = { version = "1", features = ["v4", "serde"] }
blake3 = "1"
sha2 = "0.10"
time = { version = "0.3", features = ["serde", "serde-well-known", "macros"] }
rusqlite = { version = "0.31", features = ["bundled", "hooks"] }

[workspace.lints.rust]
unsafe_code = "forbid"
Expand Down
3 changes: 3 additions & 0 deletions crates/coven-threads-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,6 @@ uuid.workspace = true
blake3.workspace = true
sha2.workspace = true
time.workspace = true

[dev-dependencies]
rusqlite.workspace = true
Loading
Loading