Skip to content

feat: complete Psyche W2 contract foundation - #4

Merged
BunsDev merged 66 commits into
mainfrom
feat/psyche-g2-foundation
Aug 9, 2026
Merged

feat: complete Psyche W2 contract foundation#4
BunsDev merged 66 commits into
mainfrom
feat/psyche-g2-foundation

Conversation

@BunsDev

@BunsDev BunsDev commented Aug 8, 2026

Copy link
Copy Markdown
Member

Completes the approved W2/G2 foundation plan: typed canonical records, forward-only SQLite migrations, immutable storage, quarantine/retention, behavior-level fake ports, and migration/property/crash evidence. Includes maintainer-approved Rust 1.88 MSRV and fail-closed local-storage/schema authentication. No W3-W9 behavior and no G4+ capability is enabled.

Closes #3.

BunsDev and others added 30 commits August 8, 2026 16:59
- Add workspace members: psyche-store, psyche-coven, psyche-surfaces,
  psyche-test-support with minimal src/lib.rs and inherited manifest fields.
- psyche-test-support sets publish = false directly; the other three use
  publish.workspace = true.
- Add workspace dependencies for new crates plus async-trait, rusqlite,
  serde_json_canonicalizer, sha2, time, ulid, proptest.
- Add scripts/check-g2-workspace.sh to verify all four crates are present
  in cargo metadata and that psyche-test-support.publish == [] and no
  manifest mixes publish.workspace with a bare publish key.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add contracts::RecordKind (15 kinds, stable 4-char prefixes: ids_, int_,
  grf_, nod_, att_, dlg_, bud_, apr_, evd_, vrd_, rcv_, adn_, sev_, sfx_,
  del_) and contracts::SchemaKind (16 kinds incl. Error), with
  SchemaKind::record_kind() as the sole SchemaKind -> RecordKind mapping.
  ExecutionBinding maps to Attempt; Error maps to None. No RecordKind
  variant exists for ExecutionBinding, keeping RecordKind::ALL at 15.
- Add contracts::SchemaVersion { kind, major }, parsed from and displayed
  as psyche.<kind>.v<major>, serde try_from/into String. Accepts exactly
  the 16 canonical major-1 strings the registry defines; an unrecognised
  kind is ContractError::UnknownSchema, a recognised kind at any other
  (or malformed) major is ContractError::UnsupportedMajor.
- Add id::RecordId: opaque newtype validated as <RecordKind prefix><26
  char canonical uppercase ULID>, with RecordId::parse (exact requested
  kind) and RecordId::parse_any/TryFrom<String> (kind derived from the
  matched prefix) for serde. Rejects wrong-kind prefixes (e.g. dly_ where
  Delivery requires del_, or del_ where Delegation requires dlg_),
  lowercase or non-canonical ULID characters, and trailing data.
- Add id::RequestId: separate req_-prefixed newtype, not a RecordKind,
  with the same canonical-ULID validation.
- Add digest::canonical_bytes (delegates to serde_json_canonicalizer, so
  object key order does not affect the output) and digest::digest,
  returning a validated digest::Sha256Digest (sha256: + 64 lowercase hex
  chars, serde try_from/into String, rejects wrong prefix, wrong length,
  uppercase hex, and trailing content).
- Add contracts::ContractError covering schema, record/request id, digest,
  and canonicalization validation failures.
- Document schema.rs's existing CONFIG_SCHEMA_VERSION as a deliberately
  separate authority from contracts::SchemaVersion (config-file loading
  vs. the domain contract registry) rather than integrating it, since
  psyche.config.v1 is not one of the registry's 16 defined kinds.
- Records, CanonicalDocument, decode_document, store validation, and
  QuarantineId are explicitly out of scope for this task.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the lossy serde_value traversal with a type-preserving Serde validation pass that covers numeric keys, i128/u128, every compound branch, and non-finite floats before canonicalizing the original value.

Redact schema and serializer-controlled error text, and add boundary, collision, traversal, and near-1MiB input regressions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Defect 1 — map keys bypass safe-number validation:
validate_serialized_domain (serde_value era) iterated only Map values,
letting unsafe u64 keys like 9007199254740992 and 9007199254740993
reach serde_json_canonicalizer unchecked; those two values round through
f64 to the same 9007199254740992.0, silently collapsing two distinct
entries to one JSON key. The DomainValidator serializer (landed in
a74266f) already calls serialize_key on every map key, ensuring the
same validate_unsigned / validate_signed guard that applies to values
is applied to keys. This commit adds the missing TDD evidence:
- map_key_collision_pair_rejected_before_f64_canonicalization: both
  unsafe keys together in one BTreeMap<u64,_> must return
  NonInteroperableNumber before canonicalization.
- map_key_u64_boundary_accepted_and_one_over_rejected: MAX_SAFE_INTEGER
  as a u64 key is accepted; MAX_SAFE_INTEGER+1 is rejected.
- nested_map_unsafe_u64_keys_are_rejected: an unsafe u64 key nested
  inside a struct field is also rejected; a safe key is accepted.

Defect 2 — serializer error text leaks:
canonicalization_failed(error) previously stored error.to_string() in
ContractError::CanonicalizationFailed { reason }, leaking arbitrary
custom-serializer messages into Debug/Display. CanonicalizationFailed
is now a payload-free unit variant (a74266f); canonicalization_failed
discards the error argument. The existing test
canonicalization_errors_do_not_retain_custom_serializer_messages covers
both defects: it injects a 900 000-char sentinel and asserts that
Debug/Display exclude it and remain under 256 bytes.

No other crates match on CanonicalizationFailed { reason }.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
decode_document ran numeric interoperable-domain validation
(validate_json_domain) before the schema_version probe and typed
enum prevalidation. An unsupported-major or unknown-enum document
that also contained an unsafe number was misclassified as
NonInteroperableNumber instead of surfacing UnsupportedMajor,
UnknownSchema, or UnknownEnumValue as Task 4 requires.

Reorder decode_document so classification runs strictly in the
required precedence: recursive syntactic parse (duplicate-key and
nesting-depth rejection) -> schema_version probe / SchemaVersion
classification -> recognized schema's typed enum prevalidation ->
numeric domain validation -> typed deserialization / CanonicalDocument
validation.

Add mixed-error regression tests covering unsupported major, unknown
schema, and unknown enum value each paired with a nested unsafe
integer, confirm known-good documents still reject unsafe nested
integers, and confirm duplicate keys are rejected ahead of an
unsupported major. All errors stay redacted with no raw value
retained.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Route embedded JSON values through the strict duplicate-aware deserializer so direct typed decoding cannot erase conflicting object fields before contract validation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Restore the reviewed read-only recovery preflight, private storage boundary, and complete migration rollback fixtures after the concurrent persistence work removed them.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Exercise every cancellation correlation, evidence, reason, identifier, and window mutation from valid baselines and prove direct insert leaves no durable state.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
BunsDev and others added 18 commits August 8, 2026 16:59
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings August 8, 2026 23:51

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review this pull request because it exceeds the maximum number of lines (20,000). Try reducing the number of changed lines and requesting a review from Copilot again.

@BunsDev
BunsDev merged commit c9fcafb into main Aug 9, 2026
7 checks passed
@BunsDev
BunsDev deleted the feat/psyche-g2-foundation branch August 9, 2026 03:26
@BunsDev BunsDev self-assigned this Aug 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: complete Psyche W2 contract foundation

2 participants