docs: consolidate design documentation and standardize crate English
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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
@@ -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)?;
|
||||
|
||||
@@ -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<()> {
|
||||
|
||||
Reference in New Issue
Block a user