Skip to content

Commit 9338bac

Browse files
perf(code-index): memoize generation validation
1 parent a2e9fb6 commit 9338bac

2 files changed

Lines changed: 37 additions & 3 deletions

File tree

crates/tracedecay-code-index/src/production/mod.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,10 @@ pub struct CodeIndexPublishedGenerationV1 {
451451
coverage: CoverageSummaryV1,
452452
capability: CodeIndexCapabilityManifestV1,
453453
projection: ProjectionPublicationHandoffV1,
454+
/// Set only after a complete fail-closed integrity sweep. All generation
455+
/// evidence is private and immutable after construction, so successful
456+
/// validation remains authoritative for every clone and serving read.
457+
integrity_validated: bool,
454458
}
455459

456460
/// Immutable test-attribution reader derived from one sealed production code
@@ -839,8 +843,9 @@ impl CodeIndexPublishedGenerationV1 {
839843
coverage: envelope.generation.coverage,
840844
capability: envelope.generation.capability,
841845
projection,
846+
integrity_validated: false,
842847
};
843-
generation.validate()?;
848+
let generation = generation.validate_new()?;
844849
Ok(generation)
845850
}
846851

@@ -855,6 +860,22 @@ impl CodeIndexPublishedGenerationV1 {
855860
}
856861

857862
fn validate(&self) -> Result<(), CodeIndexProductionErrorV1> {
863+
if self.integrity_validated {
864+
Ok(())
865+
} else {
866+
Err(CodeIndexProductionErrorV1::Contract(
867+
"published generation has not passed integrity validation".to_owned(),
868+
))
869+
}
870+
}
871+
872+
fn validate_new(mut self) -> Result<Self, CodeIndexProductionErrorV1> {
873+
self.validate_fresh()?;
874+
self.integrity_validated = true;
875+
Ok(self)
876+
}
877+
878+
fn validate_fresh(&self) -> Result<(), CodeIndexProductionErrorV1> {
858879
self.manifest
859880
.validate()
860881
.map_err(|error| CodeIndexProductionErrorV1::Contract(error.to_string()))?;
@@ -1250,8 +1271,9 @@ where
12501271
coverage,
12511272
capability,
12521273
projection,
1274+
integrity_validated: false,
12531275
};
1254-
candidate.validate()?;
1276+
let candidate = candidate.validate_new()?;
12551277

12561278
let expected = active
12571279
.as_ref()

crates/tracedecay-code-index/tests/code_index_suite/production_orchestration.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,18 +333,30 @@ fn production_owner_publishes_complete_generation_and_restores_it_after_restart(
333333
}
334334

335335
#[test]
336-
fn sealed_generation_rejects_file_authority_from_foreign_project() {
336+
fn sealed_generation_validation_is_memoized_but_decode_stays_fail_closed() {
337337
let store = SharedPublicationStore::default();
338338
let mut owner = CodeIndexProductionOwnerV1::new(config(), store, ApplyingProjectionSink)
339339
.expect("production owner");
340340
let generation = owner
341341
.build_and_publish(request("file.project-binding", 1_200_000), &ActiveControl)
342342
.expect("valid generation publishes");
343343
let sealed = generation.encode_sealed().expect("valid generation seals");
344+
assert_eq!(
345+
generation
346+
.encode_sealed()
347+
.expect("memoized generation seals again"),
348+
sealed
349+
);
344350

345351
let restored =
346352
CodeIndexPublishedGenerationV1::decode_sealed(&sealed).expect("valid generation restores");
347353
assert_eq!(restored.manifest().project_id, config().project_id);
354+
assert_eq!(
355+
restored
356+
.encode_sealed()
357+
.expect("restored generation retains successful validation"),
358+
sealed
359+
);
348360

349361
let mut envelope: serde_json::Value =
350362
serde_json::from_slice(&sealed).expect("sealed generation JSON");

0 commit comments

Comments
 (0)