feat: replace revision counters with domain state contracts

Use content digests, key fingerprints, binding and trust identities, immutable mutation receipts, and observation tokens instead of state-update ordinals across API, storage, Runtime, and Web contracts.

Preserve historical replay and secret material through frozen migrations; preflight uncertain removals before cutover, resume provisioning receipts, validate encrypted migration candidates, and align fresh/upgraded schemas. Include reviewed regression fixes, generated artifacts, and validation evidence.
This commit is contained in:
2026-10-10 15:55:20 +09:00
parent 72ce817de4
commit c78f7f7d80
358 changed files with 25965 additions and 12069 deletions
+113 -14
View File
@@ -391,8 +391,7 @@ impl ResolvedMemoryFeatureConfig {
return Err("disabled Memory feature must not carry Workspace settings");
}
if let Some(settings) = &self.workspace_settings
&& (settings.settings_revision == 0
|| !is_normalized_workspace_memory_language(&settings.language))
&& !is_normalized_workspace_memory_language(&settings.language)
{
return Err("Memory Workspace settings snapshot metadata is invalid");
}
@@ -473,8 +472,7 @@ impl ResolvedSubjektivFeatureConfig {
return Err("disabled subjektiv feature must not carry Workspace settings");
}
if let Some(settings) = &self.workspace_settings
&& (settings.settings_revision == 0
|| !is_normalized_workspace_memory_language(&settings.language))
&& !is_normalized_workspace_memory_language(&settings.language)
{
return Err("subjektiv Workspace settings snapshot metadata is invalid");
}
@@ -705,10 +703,8 @@ pub fn is_normalized_workspace_memory_language(language: &str) -> bool {
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct WorkspaceMemorySettingsSnapshot {
/// Workspace that owns the settings revision.
/// Workspace that owns these settings.
pub workspace_id: String,
/// Monotonic Workspace Memory settings revision.
pub settings_revision: u64,
/// Normalized language used for Memory extraction and consolidation output.
pub language: String,
}
@@ -1155,7 +1151,8 @@ struct LegacyMemoryConfig {
consolidation_threshold_bytes: Option<u64>,
}
const RESOLVED_MANIFEST_SNAPSHOT_SCHEMA_VERSION: u64 = 3;
const RESOLVED_MANIFEST_SNAPSHOT_SCHEMA_VERSION: u64 = 4;
const MEMORY_COUNTER_MANIFEST_SNAPSHOT_SCHEMA_VERSION: u64 = 3;
const PREVIOUS_RESOLVED_MANIFEST_SNAPSHOT_SCHEMA_VERSION: u64 = 2;
/// Serialize a resolved Worker Manifest for durable Worker-specific storage.
@@ -1187,6 +1184,7 @@ pub fn read_persisted_worker_manifest_snapshot(
))
})?;
if version != RESOLVED_MANIFEST_SNAPSHOT_SCHEMA_VERSION
&& version != MEMORY_COUNTER_MANIFEST_SNAPSHOT_SCHEMA_VERSION
&& version != PREVIOUS_RESOLVED_MANIFEST_SNAPSHOT_SCHEMA_VERSION
{
return Err(serde_json::Error::io(std::io::Error::new(
@@ -1218,6 +1216,9 @@ pub fn read_persisted_worker_manifest_snapshot(
if version == PREVIOUS_RESOLVED_MANIFEST_SNAPSHOT_SCHEMA_VERSION {
migrate_legacy_manifest_authority(&mut manifest)?;
}
if version != RESOLVED_MANIFEST_SNAPSHOT_SCHEMA_VERSION {
migrate_legacy_memory_settings_counter(&mut manifest)?;
}
return validate_persisted_worker_manifest(serde_json::from_value(manifest)?);
}
@@ -1240,6 +1241,32 @@ fn validate_persisted_worker_manifest(
Ok(manifest)
}
/// Frozen migration for schema 2/3 resolved manifests. Keep actual Workspace
/// identity and language unchanged; the obsolete sequence has no execution role.
fn migrate_legacy_memory_settings_counter(
manifest: &mut serde_json::Value,
) -> Result<(), serde_json::Error> {
for namespace in ["memory", "subjektiv"] {
let Some(settings) = manifest
.get_mut("feature")
.and_then(|feature| feature.get_mut(namespace))
.and_then(|feature| feature.get_mut("workspace_settings"))
.and_then(serde_json::Value::as_object_mut)
else {
continue;
};
if let Some(counter) = settings.remove("settings_revision")
&& !counter.as_u64().is_some_and(|counter| counter > 0)
{
return Err(serde_json::Error::io(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"legacy Workspace Memory settings snapshot has an invalid counter",
)));
}
}
Ok(())
}
fn migrate_legacy_manifest_authority(
manifest: &mut serde_json::Value,
) -> Result<(), serde_json::Error> {
@@ -1385,6 +1412,7 @@ fn migrate_legacy_resolved_manifest_snapshot(
}
feature.insert("memory".to_string(), resolved);
migrate_legacy_manifest_authority(&mut snapshot)?;
migrate_legacy_memory_settings_counter(&mut snapshot)?;
validate_persisted_worker_manifest(serde_json::from_value(snapshot)?)
}
@@ -1749,7 +1777,6 @@ model_id = "claude-sonnet-4-20250514"
worker_max_turns = 2\n\n\
[feature.memory.workspace_settings]\n\
workspace_id = \"workspace-1\"\n\
settings_revision = 7\n\
language = \"日本語\"\n"
);
let manifest = WorkerManifest::from_toml(&toml).unwrap();
@@ -1774,7 +1801,6 @@ model_id = "claude-sonnet-4-20250514"
fn resolved_memory_execution_validation_fails_closed() {
let snapshot = WorkspaceMemorySettingsSnapshot {
workspace_id: "workspace-1".to_string(),
settings_revision: 1,
language: "English".to_string(),
};
let mut enabled = ResolvedMemoryFeatureConfig::default();
@@ -1793,7 +1819,6 @@ model_id = "claude-sonnet-4-20250514"
fn resolved_subjektiv_policy_is_inert_until_trusted_attachment() {
let snapshot = WorkspaceMemorySettingsSnapshot {
workspace_id: "workspace-1".to_string(),
settings_revision: 1,
language: "English".to_string(),
};
let mut enabled = ResolvedSubjektivFeatureConfig::default();
@@ -1816,6 +1841,80 @@ model_id = "claude-sonnet-4-20250514"
assert!(WorkerManifest::from_toml(&toml).is_err());
}
#[test]
fn persisted_counter_migration_preserves_memory_and_subject_settings() {
for version in [2, 3] {
let mut manifest = WorkerManifest::from_toml(MINIMAL_REQUIRED).unwrap();
manifest.feature.memory.profile.enabled = true;
manifest.feature.subjektiv.profile.enabled = true;
let settings = WorkspaceMemorySettingsSnapshot {
workspace_id: "workspace-1".into(),
language: "日本語".into(),
};
manifest
.feature
.memory
.bind_workspace_settings(settings.clone())
.unwrap();
manifest
.feature
.subjektiv
.bind_workspace_settings(settings.clone())
.unwrap();
let mut snapshot = write_persisted_worker_manifest_snapshot(&manifest).unwrap();
snapshot["schema_version"] = version.into();
for namespace in ["memory", "subjektiv"] {
snapshot["manifest"]["feature"][namespace]["workspace_settings"]["settings_revision"] =
99.into();
}
let restored = read_persisted_worker_manifest_snapshot(snapshot).unwrap();
assert_eq!(
restored.feature.memory.workspace_settings(),
Some(settings.clone())
);
assert_eq!(
restored.feature.subjektiv.workspace_settings(),
Some(settings)
);
let current = write_persisted_worker_manifest_snapshot(&restored).unwrap();
assert_eq!(current["schema_version"], 4);
for namespace in ["memory", "subjektiv"] {
assert_eq!(
current["manifest"]["feature"][namespace]["workspace_settings"],
serde_json::json!({
"workspace_id": "workspace-1", "language": "日本語"
})
);
}
read_persisted_worker_manifest_snapshot(current).unwrap();
}
}
#[test]
fn current_settings_snapshot_rejects_removed_counter_outside_migration() {
assert!(
serde_json::from_value::<WorkspaceMemorySettingsSnapshot>(serde_json::json!({
"workspace_id": "workspace-1", "language": "English", "settings_revision": 9
}))
.is_err()
);
let settings: WorkspaceMemorySettingsSnapshot = serde_json::from_value(serde_json::json!({
"workspace_id": "workspace-1", "language": "English"
}))
.unwrap();
let mut memory = ResolvedMemoryFeatureConfig::default();
memory.profile.enabled = true;
memory.bind_workspace_settings(settings.clone()).unwrap();
assert!(memory.validate_execution().is_ok());
for language in ["", " English", "English\n"] {
memory.workspace_settings = Some(WorkspaceMemorySettingsSnapshot {
language: language.into(),
..settings.clone()
});
assert!(memory.validate_execution().is_err());
}
}
#[test]
fn persisted_manifest_adapter_migrates_legacy_memory_authority() {
let mut manifest =
@@ -1863,7 +1962,7 @@ model_id = "claude-sonnet-4-20250514"
"Français"
);
let current = write_persisted_worker_manifest_snapshot(&migrated).unwrap();
assert_eq!(current["schema_version"], 3);
assert_eq!(current["schema_version"], 4);
assert!(current["manifest"].get("memory").is_none());
let mut disabled =
@@ -1905,7 +2004,7 @@ model_id = "claude-sonnet-4-20250514"
let restored = read_persisted_worker_manifest_snapshot(versioned).unwrap();
let current = write_persisted_worker_manifest_snapshot(&restored).unwrap();
assert_eq!(current["schema_version"], 3);
assert_eq!(current["schema_version"], 4);
assert!(current["manifest"].get("plugins").is_none());
assert!(current["manifest"]["feature"].get("plugins").is_none());
assert!(current["manifest"]["feature"].get("workers").is_none());
@@ -1976,7 +2075,7 @@ model_id = "claude-sonnet-4-20250514"
assert!(
read_persisted_worker_manifest_snapshot(serde_json::json!({
"schema_version": 4,
"schema_version": 5,
"manifest": manifest,
}))
.is_err()
-1
View File
@@ -1489,7 +1489,6 @@ mod tests {
let tmp = TempDir::new().unwrap();
for (field, value) in [
("workspace_id", serde_json::json!("workspace-a")),
("settings_revision", serde_json::json!(2)),
("language", serde_json::json!("Japanese")),
] {
let artifact = serde_json::json!({