docs: consolidate design documentation and standardize crate English

This commit is contained in:
2026-10-12 14:58:00 +09:00
parent a633a53274
commit 4efb0ace1f
404 changed files with 4556 additions and 11654 deletions
+7 -93
View File
@@ -1,97 +1,11 @@
# subjektiv
Shared Subject-scoped Memory domain and model-facing operations. This crate has
no Workspace Server dependency. `server-api` is the existing transport-neutral
request/response contract crate, not an authentication or Server implementation.
Shared Host domain implementation for per-Subject Memory, change history, candidate dispositions, and derived surfaces.
`feature-storage` manages physical SQLite storage; the calling Host handles authentication and execution authority.
## Storage scope
## Main entry points
The trusted Host creates `feature_storage::FeatureStorage`, selects
`FeatureStorage::scope(scope_id)`, registers `REGISTRATION` (or uses
`SubjektivStore::register`), and opens `SubjektivStore::open`. A trusted managed
`FeatureDatabase` can also be opened with `SubjektivStore::from_database`.
`scope_id` identifies the physical storage sharing boundary. Backend uses its
existing Workspace ID. Standalone uses a persisted random local storage-scope
identity under its operator-managed state directory, **not a fake Workspace**.
Subject IDs are independently issued random domain identities. An ID is a lookup
key, never an authorization credential.
Historical migration plans remain frozen. Migration 7 replaces state counters
with opaque Memory change identities and explicit predecessor history; the
original serialized records are retained in an immutable audit archive.
`store_scope.workspace_id` still stores the physical storage scope, under its
historical column name. Existing Backend placement and snapshot manifest keys
are unchanged. Optional historical `EvidenceOrigin.workspace_id` values continue
to be checked against the physical scope; local provenance need not invent a
Workspace value. `workspace_id()` is a Backend compatibility alias for
`scope_id()`.
## Host operation boundary
`api::execute(store, &HostOperationContext, operation)` is the common dispatcher
used by Backend and local Hosts. It owns query/cursor rules, bounded body and
nested provenance projections, immutable change reads, proposal validation,
explicit staging/receipt rules, resident context, candidate decisions and
surface generation/publication/failure projections.
The Host context is not serializable or model input. Trusted Rust Hosts construct
it only after validating their external execution authority:
- `HostOperationContext::validated_body` binds one Subject and optionally an
existing immutable `SubjectSessionAttribution`. Explicit staging requires the
request Session to match that attribution.
- `HostOperationContext::validated_consolidation` binds one Subject, an optional
immutable candidate batch, and an optional `JobAttemptBinding`. Attenuated Jobs
must supply `Some(batch)`, including an empty batch; `None` is legacy non-Job
Host consolidation authority. Body-only operations are unavailable to
consolidation contexts, and candidate reads/decisions cannot escape the batch.
These constructors validate store/domain bindings, not external authentication.
Backend retains authenticated Runtime/Worker identity, current singleton and
live Job attempt checks, candidate grant checks, and public Session validation.
Local Hosts retain exclusive Subject execution leases, generic Job admission,
Worker/Session ownership, and committed public Session evidence validation.
Before explicit staging, the Host resolves evidence using its committed public
Session projection; the common API does not inspect Session files, hidden
reasoning, system prompts, or arbitrary paths. Lower-level repository/projection
functions are trusted Rust APIs, not model capabilities.
## Common consolidation Job rules
`job::bounded_candidate_batch(ids, max_result_bytes)` selects the exact T-704
ordered-prefix budget (100 candidates maximum, 2,048-byte envelope, twice each
JSON-escaped ID size plus 128 bytes per disposition).
`job::consolidation_required(count, bytes, force, availability)` owns the shared
trigger thresholds and empty-ready/surface-retry rules.
`job::validate_result(store, subject_id, batch, job_id, attempt_id, value)` checks
the strict result shape, exact admitted Subject/batch, durable dispositions, and
the persisted terminal surface outcome from that attempt. After validation,
`job::with_dispositions(store, subject_id, batch, value)` adds the deterministic
compact disposition projection. Hosts retain Job result binding/admission; these
helpers never run or replay a Job. The store's `prepare_job_surface_generation`,
`require_job_surface_generation`, and `validate_job_surface_outcome` are public
trusted-domain methods for both Backend and local Hosts.
## Content conditions and history
Memory updates require the exact `expected_change_id` read by the caller.
Each committed change receives a new opaque ID and retains `previous_change_id`;
IDs are never sorted or incremented to infer history. Body/evidence continuations
pin that immutable ID, and history cursors pin a predecessor-chain head.
Subject behavior updates compare `expected_behavior_md` byte-for-byte and do not
participate in Memory freshness. Surface runs, publications and query cursors use
`memory_fingerprint`, a SHA-256 digest of the length-framed, ordered current
`(memory_id, change_id)` identities. The Subject stores no redundant freshness
counter. Publication remains first-wins for the captured Memory input set.
Old Surface input sets cannot be proven from historical counters. Migration 7
retains those snapshots and runs as archival history with deliberately detached
input identities, marks existing surfaces stale, and requires regeneration.
Old Flow provenance keeps its exact original bytes in the archive; a definition
fingerprint is not fabricated from an old numeric label.
Dependencies: `feature-storage`, `chrono`, `memory`, `manifest`, `rusqlite`,
`serde`, `serde_json`, `server-api`, `sha2`, `thiserror`, `uuid`; tests use `tempfile`.
- [`SubjektivStore`]: scope-bound records and transactions
- [`MemoryRecord`], [`SurfaceSnapshot`]: change history and derived context
- [`api`]: shared Host operations and bounded responses
- [`job`]: consolidation selection rules and result validation
+10 -3
View File
@@ -1,6 +1,13 @@
//! Shared model-facing Memory operations and bounded projections.
//! Hosts retain authentication, execution leases, Job admission and committed
//! Session evidence validation; this module never derives ambient authority.
//! Shared Host Memory operations and bounded responses.
//!
//! Applies the Subject and consolidation candidate batch pinned by [`HostOperationContext`] to operations.
//! The Host owns authentication, execution leases, Job admission, and validation of committed Session evidence.
//! This module reads neither Session files nor arbitrary paths, and never infers authority from model input.
//!
//! Query cursors pin filters and the Memory fingerprint, rejecting intervening changes as stale.
//! Body/provenance continuation pins immutable change IDs; history pages pin the predecessor-chain head.
//! Pages respect the whole-response serialization budget and UTF-8 boundaries, allowing continuation
//! offsets to resume even with JSON escapes or long lines. Returns only stored evidence anchors, never copies raw Session bodies.
mod context;
mod operation;
+10 -13
View File
@@ -19,11 +19,9 @@ enum Authority {
},
}
/// A capability issued by trusted Rust Host code after authentication/lease,
/// delegation, and committed Session evidence checks. Not a request DTO.
/// Constructors check domain binding, not external identity or execution leases.
/// Backend must retain its authenticated Worker and active Job grant checks;
/// standalone must hold the selected Subject lease and attenuate Job batches.
/// Operation context constructed by a Host after checking external authentication, leases, delegation, and Session evidence.
/// Constructors validate only store domain bindings, not external execution authority.
/// Not a request DTO; models never select the Subject or Job batch.
#[derive(Debug, Clone)]
pub struct HostOperationContext {
scope_id: String,
@@ -32,10 +30,9 @@ pub struct HostOperationContext {
}
impl HostOperationContext {
/// The Host has validated body execution authority. Attribution, when supplied,
/// must already be immutable history in this store. Before StageExplicit the
/// Host must resolve/validate all evidence against committed public Session
/// entries; neither this constructor nor the operation reads Session files.
/// Pins the Host-verified execution context for the Subject body.
/// Supplied attribution must match immutable store history. The Host resolves and validates
/// explicit staging evidence against committed public Sessions; neither this constructor nor operations read Session files.
pub fn validated_body(
store: &SubjektivStore,
subject_id: &str,
@@ -62,10 +59,10 @@ impl HostOperationContext {
})
}
/// The Host has validated consolidation authority and (for a Job) its active
/// attempt. `Some(candidate_ids)` is the immutable admitted batch; no candidate
/// outside it is accessible. `None` retains Backend's non-Job consolidation
/// authority, and must not be issued to an attenuated Job.
/// Pins Host-verified consolidation authority and, when needed, the current Job attempt.
/// `Some(candidate_ids)` is an immutable allowed set, including empty batches; candidates outside it cannot be operated on.
/// `None` is for non-Job Host authority, never issued to a restricted Job.
/// This context cannot perform Subject-body-only operations.
pub fn validated_consolidation(
store: &SubjektivStore,
subject_id: &str,
+2 -2
View File
@@ -3,8 +3,8 @@ use server_api::{
SubjektivMemoryBackendOperation as Op, SubjektivMemoryBackendResponse as Response,
};
/// Executes the same domain operations and bounded projections for every Host.
/// The caller authenticates and validates evidence before issuing `context`.
/// Executes the same domain operations and bounded projections on each Host.
/// The caller validates authentication/evidence and issues context; this function checks its scope and operation kind.
pub fn execute(
store: &crate::SubjektivStore,
context: &HostOperationContext,
+15 -14
View File
@@ -1,7 +1,8 @@
//! Common consolidation Job policy and verified result projections.
//! Shared consolidation Job selection rules and validation against stored results.
//!
//! Hosts authenticate/admit Jobs and bind their immutable batch and attempt.
//! These helpers do not dispatch, replay, or grant Job execution authority.
//! The Host owns authentication/admission and pins immutable batches/attempts.
//! This module neither dispatches nor reruns Jobs, and grants no execution authority from Profile names or model claims.
//! Validates Memory candidate processing and surface preparation separately; failure of the latter never revokes the former.
use serde::Deserialize;
use crate::api::{OperationError, OperationResult};
@@ -11,9 +12,9 @@ pub const CANDIDATE_BATCH_LIMIT: usize = 100;
pub const CONSOLIDATION_THRESHOLD_FILES: usize = 5;
pub const CONSOLIDATION_THRESHOLD_BYTES: u64 = 50_000;
/// Selects an ordered prefix using the T-704 complete-result budget. Reserve
/// 2,048 bytes for the Subject/Host surface envelope, then twice the JSON-escaped
/// ID size plus 128 bytes per candidate for its compact verified disposition.
/// Selects an ordered prefix fitting the result budget.
/// Estimates the Subject/surface envelope and JSON-escaped IDs appearing in both
/// batch declarations and validated dispositions. Overflow candidates stay for the next batch; existing batches never expand.
pub fn bounded_candidate_batch(
ids: impl IntoIterator<Item = String>,
max_result_bytes: u32,
@@ -34,8 +35,8 @@ pub fn bounded_candidate_batch(
Ok(selected)
}
/// Empty ready Subjects need no work, even when forced. Missing/stale/failed
/// surfaces bypass candidate thresholds; confirmed Memory is never discarded.
/// With no candidates and a ready surface, skips even when forced; ungenerated/stale/failed surfaces need generation regardless of candidate thresholds.
/// This is not a decision to delete committed Memory.
pub fn consolidation_required(
candidate_count: usize,
total_bytes: u64,
@@ -72,9 +73,9 @@ struct SurfaceResult {
reason_code: Option<String>,
}
/// Verifies the exact Subject/batch, every immutable disposition, and the actual
/// terminal surface outcome begun by this Job attempt. Model claims alone never
/// establish completion. Same admission semantics as the Backend T-704 adapter.
/// Validates the exact Subject/batch, immutable dispositions for all candidates, and that Job attempt's stored terminal surface outcome.
/// Rejects unresolved candidates, stale input, generation from another attempt, and fabricated results.
/// Model final text or Idle is not completion evidence; this validation also does not replace Host Job-result acceptance.
pub fn validate_result(
store: &SubjektivStore,
subject_id: &str,
@@ -117,9 +118,9 @@ pub fn validate_result(
})
}
/// Enriches a previously verified result with compact durable dispositions. Full
/// reasons, affected changes and provenance remain in the receipt store. This
/// deterministic projection neither applies candidates nor creates a new ledger.
/// Adds a compact projection of stored dispositions to a validated result.
/// Reasons, change references, and provenance stay in the receipt store, not duplicated in bounded Job results.
/// Neither reapplies candidates nor creates a new ledger.
pub fn with_dispositions(
store: &SubjektivStore,
subject_id: &str,
+5 -2
View File
@@ -1,5 +1,8 @@
//! Frozen migrations 1–6 and the one-time conversion of their data.
//! Do not expose these old fields through current domain operations.
//! Frozen migrations and one-time conversion of existing data.
//!
//! Preserves original serialized records in an immutable archive; current changes receive independent IDs and predecessors.
//! Legacy counters cannot prove the complete surface input set, so snapshots and runs remain as history,
//! detached from current input identity and stale until regeneration. Legacy representations are not exposed to current operations.
use super::*;
pub(super) fn create_schema(transaction: &Transaction<'_>) -> feature_storage::Result<()> {
+76 -55
View File
@@ -1,10 +1,4 @@
//! Subject-scoped Memory storage with immutable change history.
//!
//! This module owns subjektiv's domain schema and typed repository. Physical
//! SQLite placement, connection configuration, migration execution, backup,
//! restore, and teardown remain owned by [`feature_storage`]. A store is
//! already bound to one Host storage scope; a subject identifier is therefore a lookup
//! key, not an authorization credential.
#![doc = include_str!("../README.md")]
pub mod api;
pub mod job;
@@ -181,9 +175,9 @@ pub fn subject_worker_singleton_key(subject_id: &str) -> Result<String> {
Ok(format!("{SUBJECT_WORKER_SINGLETON_PREFIX}{subject_id}"))
}
/// Immutable host-recorded history that attributes one durable Session to the
/// subject and Worker that produced it. This is archival provenance only: it is
/// deliberately not a current-Worker link or execution ownership record.
/// Immutable historical attribution of a Session to its Subject/Worker, recorded by the Host.
/// Not a current Worker link or execution ownership. Never infer or repair attribution from candidate presence or bodies.
/// Session content stays in Session storage; this store saves only attribution.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SubjectSessionAttribution {
@@ -230,10 +224,10 @@ pub enum ChangeProposalIntent {
Reopen,
}
/// Host-owned metadata describing how a staged candidate is expected to change
/// one existing Memory. It is validated against the current scoped Memory when
/// the candidate is staged; it is not model-authored and does not itself apply
/// the change.
/// Host-owned metadata specifying how a staging candidate proposes to change existing Memory.
/// Checked against current scoped Memory at staging; registering the candidate does not itself change Memory.
/// Application rechecks the same Memory ID, expected change ID, intent, and reason,
/// never retargeting an old proposal to the latest change or inferring the target from claim text.
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ChangeProposal {
@@ -353,15 +347,14 @@ impl SubjectStagingRecord {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
/// Memory recall eligibility, not proof of truth or execution authority.
/// New Memory starts Active; Retracted is terminal.
pub enum MemoryState {
/// Current and eligible for ordinary recall. This does not assert truth and
/// grants no authority.
/// Currently eligible for ordinary recall.
Active,
/// No longer current because its question, condition, or applicability has
/// ended. A later change may reopen it when circumstances change.
/// The question, condition, or applicability period has ended. A later change can restore Active.
Resolved,
/// Invalidated and no longer eligible for recall. Retraction is terminal;
/// corrected follow-up experience is a separate Memory linked by derivation.
/// Invalidated. A distinct experience after correction creates new Memory and may reference the old change through derivation.
Retracted,
}
@@ -374,6 +367,9 @@ pub struct MemoryChangeRef {
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
/// Immutable change record appended for each update to the same Memory.
/// Opaque change IDs and previous change IDs link history; neither ID magnitude nor timestamps imply order.
/// The current projection points to the history head. Candidate/derivation references also pin individual changes.
pub struct MemoryRecord {
pub schema_version: u32,
pub id: String,
@@ -461,6 +457,8 @@ pub struct StagingResolution {
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
/// Immutable startup context derived from committed Memory, not another authoritative copy of Memory.
/// Pins the body, exact change references, and source fingerprint; never used as current context after Memory changes.
pub struct SurfaceSnapshot {
pub schema_version: u32,
pub id: String,
@@ -609,14 +607,21 @@ static MIGRATIONS: &[FeatureMigration] = &[
pub const REGISTRATION: FeatureRegistration =
FeatureRegistration::new(SUBJEKTIV_FEATURE_ID, MIGRATIONS);
/// Subject Memory repository restricted to a Host storage scope.
/// Owns schemas, validation, queries, and domain transactions; delegates physical layout
/// and connection lifecycle to [`feature_storage`]. Synchronous API; async Hosts use a blocking executor.
///
/// Scope and subject IDs select storage boundaries; the Host authenticates before calls.
/// Independent of the legacy Workspace Memory DB, with no implicit import or dual writes.
/// This store saves no current Worker links; the Host manages execution ownership.
#[derive(Clone)]
pub struct SubjektivStore {
database: FeatureDatabase,
}
impl SubjektivStore {
/// Registers the trusted schema with the neutral Host storage manager.
/// One registration can be reused across all scopes served by that manager.
/// Registers the schema with neutral Host storage.
/// Registration is separate from opening the DB and is reusable across scopes in the same manager.
pub fn register(storage: &ScopedFeatureStorage) -> Result<RegisteredFeature> {
storage.register(REGISTRATION).map_err(Into::into)
}
@@ -631,10 +636,10 @@ impl SubjektivStore {
Self::from_database(storage.open(registration)?)
}
/// Opens a trusted managed database. `store_scope.workspace_id` retains its
/// historical SQL name: it stores the physical Host storage-scope identity.
/// Backend uses its Workspace ID; local Hosts use a persisted random scope,
/// without creating a Workspace. Subject IDs are separate domain identities.
/// Validates the managed DB's Feature ID and stored scope match.
/// `store_scope.workspace_id` is a compatibility SQL name denoting the physical storage scope.
/// Backends use Workspace IDs; local Hosts use persistent independent scopes.
/// Subjects have separate identities within a scope; a local scope creates no Workspace.
pub fn from_database(database: FeatureDatabase) -> Result<Self> {
if database.feature_id() != SUBJEKTIV_FEATURE_ID {
return Err(SubjektivError::InvalidRecord(
@@ -736,8 +741,8 @@ impl SubjektivStore {
})
}
/// Replaces behavior only if its exact previously-read content still matches.
/// Memory changes do not participate in this condition.
/// Updates only when the observed behavior body matches byte-for-byte.
/// Behavior is a document independent of Memory, excluded from Memory-change and surface-freshness comparison conditions.
pub fn update_subject_behavior(
&self,
subject_id: &str,
@@ -759,8 +764,9 @@ impl SubjektivStore {
})
}
/// Derived content condition over all current Memory identities, not stored
/// on the Subject and never incremented.
/// SHA-256 hashes the sorted, length-delimited current `(memory_id, change_id)` set across all states.
/// Content/state/provenance changes issue new change IDs, preventing equality even in ABA returns to prior content.
/// Saves no redundant Subject counter; staging alone does not change this fingerprint.
pub fn memory_fingerprint(&self, subject_id: &str) -> Result<String> {
self.database.try_transaction(|tx| {
require_subject(tx, subject_id)?;
@@ -847,8 +853,8 @@ impl SubjektivStore {
})
}
/// Subject retirement is distinct from stopping any current Worker. It does
/// not edit or delete Memory and cannot be reversed by this baseline schema.
/// Retires a Subject independently of stopping Workers. Memory/history remain unedited, undeleted, and readable.
/// Retirement is irreversible; new candidates and Memory changes require an active Subject.
pub fn retire_subject(&self, subject_id: &str) -> Result<SubjectRecord> {
self.database.try_transaction(|transaction| {
let mut subject = require_subject(transaction, subject_id)?;
@@ -868,6 +874,9 @@ impl SubjektivStore {
})
}
/// Saves an immutable candidate without changing committed Memory.
/// Replay with the same candidate identity and payload returns the original record; different content conflicts.
/// Extraction failure after saving never revokes the candidate. The Host commits the extraction pointer separately.
pub fn stage_candidate(&self, record: SubjectStagingRecord) -> Result<SubjectStagingRecord> {
validate_staging_record(self.workspace_id(), &record)?;
validate_staging_admission(&record)?;
@@ -878,9 +887,8 @@ impl SubjektivStore {
})
}
/// Atomically verifies immutable Host attribution already completed at the
/// Session lifecycle boundary and stages its extracted candidate. Candidate
/// staging never creates or repairs Session attribution.
/// Validates immutable Host attribution already recorded by the Session lifecycle and saves the candidate in the same transaction.
/// Candidate registration never creates or repairs Session attribution.
pub fn stage_candidate_with_attribution(
&self,
mut record: SubjectStagingRecord,
@@ -1154,6 +1162,9 @@ impl SubjektivStore {
})
}
/// Appends an immutable change only when the observed expected change ID matches the current head.
/// Comparison, history append, and current projection update share a transaction, preventing late stale decisions from overwriting.
/// This direct update cannot add candidate provenance; use [`Self::apply_candidates`].
pub fn revise_memory(
&self,
subject_id: &str,
@@ -1173,8 +1184,8 @@ impl SubjektivStore {
})
}
/// Atomically writes a Memory change and one candidate's immutable
/// resolution. Any conflict or invalid reference rolls both changes back.
/// Saves a Memory change and one candidate's immutable disposition together.
/// Conflicts or invalid references roll back both.
pub fn apply_candidate(
&self,
subject_id: &str,
@@ -1189,9 +1200,11 @@ impl SubjektivStore {
Ok((memory, resolutions.remove(0)))
}
/// Atomically applies one or more candidates to exactly one new Memory
/// change_id. The repository derives `source_candidate_ids` from this list so
/// every applied resolution has a reciprocal immutable provenance edge.
/// Atomically applies unresolved candidates from the same Subject to one new Memory change.
/// Derives provenance from the candidate set, committing the change, reference edges, current projection,
/// surface staleness, and each immutable disposition in one transaction. Standalone close cannot record Applied.
/// Mismatched comparison conditions or proposal intents roll back everything, leaving candidates unresolved.
/// Atomicity covers only this Feature DB, not the Host DB or subsequent surface generation.
pub fn apply_candidates(
&self,
subject_id: &str,
@@ -1223,6 +1236,10 @@ impl SubjektivStore {
})
}
/// Saves candidate decisions with stable request IDs and immutable receipts.
/// Exact replay returns the committed receipt; ID reuse with different input is rejected.
/// Applied Memory is never revoked by subsequent model-execution or surface-generation failure.
/// Failures/empty responses are not themselves candidate dispositions; only explicit decisions are saved.
pub fn decide_candidate(
&self,
subject_id: &str,
@@ -1502,9 +1519,11 @@ impl SubjektivStore {
})
}
/// Captures one deterministic, bounded generation input from only current
/// active Memory changes. Kind order is fixed and selection is round-robin
/// across kinds; within each kind, updated_at desc then memory id asc.
/// Selects generation materials only from current active Memory, with fixed ordering and limits.
/// Uses round-robin across kinds; within each kind, descending update time then ascending Memory ID.
/// Reserves equal budgets for nonempty kinds before redistributing surplus, preventing large kinds from dominating.
/// Uses neither staging, Session history, nor old surfaces. The material budget leaves room for fixed prompts, etc.;
/// the caller checks the complete editor-request budget.
pub fn prepare_surface_generation(&self, subject_id: &str) -> Result<SurfaceGeneration> {
self.prepare_job_surface_generation(subject_id, None)
}
@@ -1547,9 +1566,9 @@ impl SubjektivStore {
})
}
/// Validate a Job's structured surface claim against the existing generation
/// and publication/failure records. This does not create another receipt or
/// roll back Memory. The generation must have begun inside this attempt.
/// Checks a structured Job surface result against generation started by the same attempt and the stored terminal outcome.
/// Rejects mismatches in the current Memory fingerprint or ready/failed content.
/// Creates no new receipt and never rolls back committed Memory.
#[allow(clippy::too_many_arguments)]
pub fn validate_job_surface_outcome(
&self,
@@ -1596,8 +1615,10 @@ impl SubjektivStore {
})
}
/// Validates structured editor output against the exact material snapshot and
/// atomically publishes it only while the subject store generation is current.
/// Checks editor output shape, budget, and exact material references, publishing in a transaction where the source fingerprint matches current input.
/// Reference validation checks existence/freshness, not semantic correctness of generated text.
/// Replay with the same fingerprint/output returns the same snapshot; different concurrent output never replaces the first committed result.
/// If active Memory exists but no material fits, fails rather than publishes a false empty result.
pub fn publish_surface_generation(
&self,
subject_id: &str,
@@ -1665,8 +1686,9 @@ impl SubjektivStore {
})
}
/// Records a bounded generator failure without changing confirmed Memory. A
/// late failure cannot replace a ready surface or dirty a newer generation.
/// Saves a bounded failure reason without changing committed Memory.
/// Late failures never replace a ready surface or the state of newer Memory input.
/// Separate from candidate application, so generation failure never revokes committed decisions.
pub fn fail_surface_generation(
&self,
subject_id: &str,
@@ -1718,9 +1740,9 @@ impl SubjektivStore {
})
}
/// Returns only a current ready snapshot for injection. Other states are
/// explicit so callers never report missing/stale/failed generation as empty
/// confirmed Memory.
/// Returns only ready snapshots matching the current fingerprint for resident injection.
/// Ready with an empty body is a normal empty result, distinct from ungenerated/stale/failed.
/// Returns availability separately so an absent injected body implies neither absent committed Memory nor successful generation.
pub fn resident_surface(&self, subject_id: &str) -> Result<ResidentSurface> {
self.database.try_with_connection(|connection| {
let subject = require_subject_in_connection(connection, subject_id)?;
@@ -1728,8 +1750,7 @@ impl SubjektivStore {
})
}
/// Atomically reads user-managed behavior and the independently-fresh
/// generated Memory surface from one SQLite connection snapshot.
/// Returns independent behavior and the generated Memory surface as observations from the same SQLite transaction.
pub fn resident_context(&self, subject_id: &str) -> Result<SubjectResidentContext> {
self.database.try_transaction(|transaction| {
let subject = require_subject(transaction, subject_id)?;
+5 -1
View File
@@ -1,4 +1,8 @@
//! Current schema creation. Frozen data conversion lives with the old migrations.
//! Creates the current schema and connects frozen migrations.
//!
//! Foreign keys for Memory history and candidate/derivation/surface references include the Subject,
//! rejecting cross-Subject references. Seals/triggers prevent adding references after immutable JSON and edges commit;
//! retention triggers reject deletion of history and referenced records. The repository updates current projections and search columns together.
use super::*;
pub(super) fn migrate(tx: &Transaction<'_>) -> feature_storage::Result<()> {