pi-fold 1.0.3 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1159,11 +1159,45 @@ export function registerActiveContext(pi: any, options: {
1159
1159
  const operation = persistence.persistenceQueue.then(async () => {
1160
1160
  if (!persistence.state || !persistence.persisted) return;
1161
1161
  let next = clone(persistence.state);
1162
+ // Which folds this write is TRYING to add, read before the projection can drop them.
1163
+ const persistedFoldIds = new Set(persistence.persisted.folds.map((fold) => fold.id));
1164
+ const arrivingFoldIds = next.folds.filter((fold) => !persistedFoldIds.has(fold.id)).map((fold) => fold.id);
1165
+ const foldsInMemory = next.folds.length;
1162
1166
  if (ctx && lifecycle.latestSnapshot?.sessionId === next.sessionId) {
1163
1167
  next = persistenceProjection(next, authoritativeSnapshotFor(ctx));
1164
1168
  }
1165
1169
  next.folds = normalizeFoldsForPersistedRecords(next.folds, persistence.persistedFoldRecords);
1166
1170
  if (sameStateProjection(next, persistence.persisted)) {
1171
+ // A COMMIT THAT VANISHES IS NOT A NO-OP.
1172
+ //
1173
+ // `persistenceProjection` keeps only folds whose refs still resolve against the
1174
+ // snapshot, which is correct when evidence has genuinely left the branch. But when
1175
+ // a write ARRIVES with new folds and projects down to something byte-equal to what
1176
+ // is already durable, every one of those folds was dropped, and returning quietly
1177
+ // here also resets `persistence.state` BACKWARDS to the persisted revision. The
1178
+ // marks that produced them go back to pending, the same commit is attempted again
1179
+ // on the next boundary, and it vanishes again.
1180
+ //
1181
+ // Measured, sol-20260812 rep 3: a band-top commit ran at revision 143 and folded
1182
+ // 579,489 source chars into 85,463 of placeholder. Durable state never passed 134.
1183
+ // None of its 11 folds reached a record, no receipt was written, occupancy climbed
1184
+ // 237,682 -> 253,423 against a 251,520 budget unopposed, and the next request was
1185
+ // refused. Nothing was reported, because a silent return is not a failure. Rep 2
1186
+ // ran 15 commits on the same plan with no gap at all, so this is rare rather than
1187
+ // systemic, which is exactly why it has to announce itself the first time.
1188
+ //
1189
+ // Raising it routes through the automatic-failure path, which restores the state
1190
+ // this call was about to discard, suspends automatic folding and tells the user.
1191
+ // Suspending is the safe direction: continuing to compute folds that are thrown
1192
+ // away is what turned one dropped commit into a dead session.
1193
+ if (arrivingFoldIds.length) {
1194
+ throw new Error(
1195
+ `Active-context commit discarded at persistence: ${arrivingFoldIds.length} arriving fold(s) ` +
1196
+ `did not survive the persistence projection (${foldsInMemory} in memory, ` +
1197
+ `${next.folds.length} durable) at revision ${persistence.state.revision}; ` +
1198
+ `first ${arrivingFoldIds[0]}`,
1199
+ );
1200
+ }
1167
1201
  persistence.state = clone(persistence.persisted);
1168
1202
  return;
1169
1203
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-fold",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "Lossless, agent-governed context folding for the Pi coding agent: only the oldest context changes; the fresh window stays untouched.",
5
5
  "keywords": [
6
6
  "pi-package",