Repository navigation
Description
Problem
Normal graph publication calls participant_snapshots before filtering replaced families, reading every participant into memory, including discarded ones. stage_participant_files writes every memory participant into the next generation. Thus a small graph write rereads and rewrites unchanged knowledge and other sibling participants, including provenance only when it is not modified. Compact graph CAS payload reuse does not remove this participant-level work.
Source inspection at 172dee39902ff2afac86232725223dd0bdff155a; no runtime speedup or measured severity is claimed. Reconfirm against current main before implementing.
- crates/graphforge-api/src/graph_publication.rs:523
- crates/graphforge-storage/src/project_generation.rs:696
- crates/graphforge-storage/src/project_publication/participants.rs:277
Outcome and acceptance criteria
Carry forward unchanged immutable participants by trustworthy identity/reference without materializing and rewriting their payloads.
- Select replacement families before reading payloads; unchanged siblings are retained/reused without full in-memory materialization and rewriting solely to carry them forward.
- Generation manifests remain complete and independently readable with existing lifetime/retention/GC guarantees; do not introduce an unbounded parent-manifest chain or another storage authority.
- Preserve checksum and control identity validation at existing trust boundaries, isolation, idempotent retry, cross-process conflicts, atomic CURRENT and corruption refusal.
- Public graph and knowledge mutation tests grow an unchanged sibling ledger while holding changed bytes fixed; carry-forward read/write/hash counters distinguish unavoidable validated boundary work and prove removal of repeated copying.
- Failure injection, old pinned readers, reopen, retention/GC and portable round-trip tests prove reused participants remain available and exact.
Completion scenarios
- Given a small graph update beside a large unchanged knowledge ledger, when a generation publishes, then the ledger's payload is reused without a full read/write carry-forward cycle and values are unchanged.
- Given retained old generations and failures around publication, when readers reopen and GC runs, then only unreachable payloads are reclaimed and the selected generation is complete.
Validation and implementation boundaries
Quality regime A: exact semantics plus deterministic work counters. Add a regression that exposes this cost on the baseline and exercises real Rust-facade execution; unit-only or logical-plan proof is insufficient. Attribute the named phase so another child's cost cannot hide or invalidate the result. Reuse existing I/O, hash, decoded-row and allocation instrumentation; counters must measure actual work, not merely fewer helper calls. Record bounded fixture sizes, commands and results on this issue/PR. No wall-clock CI threshold, new certification lane or S26 run is required to close this leaf.
Run the narrow relevant make test-rust ARGS="-p graphforge-api" / -p graphforge-exec / -p graphforge-storage suites, format after final edits, and make check before pushing. Rebuild native bindings before any required binding tests. The ordinary exact-head CI Gate and squash merge queue remain the merge gate.
Preserve corruption refusal, path/handle validation, cancellation, memory limits, atomicity and structured errors. Diagnostics expose counts/bytes only, not graph values, UUID inventories or sensitive paths. Update the nearest existing architecture/development documentation only where the implementation changes its contract or reproduction method; results stay on the issue/PR, not in a new per-issue document.
Scope and relationships
#1691/#1657 reduced repeated payload hashing; this is the distinct remaining unchanged-participant materialization/write cost. #1617 defines checksum policy.
Non-goals: Changing ledger merge semantics, optimizing changed ledger encoding, weakening durability, cross-generation manifest DAGs or new release gates.
Native child and blocker of #1803, in M5. This finite repair batch must finish before the S26 run under #900/#745. The epic owns aggregate acceptance; this child owns only the cost described above.
Activity
perf(storage): reuse unchanged generation participants (CurateLabs#1816)