|
|
@@ -10,7 +10,9 @@
|
|
|
* heuristic price of the exact replaced range, so the fold keeps a running
|
|
|
* total plus at most one pending claim and never retains per-node prices.
|
|
|
* The counts are exact by construction: producers derive them from the same
|
|
|
- * fixed estimator this module prices appends with.
|
|
|
+ * fixed estimator this module prices appends with. A replacement without an
|
|
|
+ * armed claim folds with zero delta because bounded state cannot reconstruct
|
|
|
+ * the replaced range; this preserves replay at the cost of possible drift.
|
|
|
*
|
|
|
* @module @deepseek-ai/dsh-token-meter/surface-projection
|
|
|
*/
|
|
|
@@ -47,16 +49,19 @@ export interface SurfaceTokensFold {
|
|
|
* Fold one committed event onto a running surface-token total.
|
|
|
*
|
|
|
* A shadow-price event arms a claim; any other event expires it, and a
|
|
|
- * surface `replace` must consume a claim naming its exact range — the
|
|
|
+ * surface `replace` consumes the claim naming its exact range — the
|
|
|
* producers append the metering event and the replacement synchronously
|
|
|
* adjacent, so a surviving claim always prices the very next event.
|
|
|
+ * A replace with no claim folds with zero delta because the bounded state
|
|
|
+ * cannot reconstruct the replaced range. An armed claim for another range
|
|
|
+ * still fails because the adjacent events contradict each other.
|
|
|
* @param claim - the claim armed by the immediately preceding event, if any.
|
|
|
* @param event - the next committed session event.
|
|
|
* @returns the signed token delta and the claim state after this event.
|
|
|
- * @throws when a replacement arrives without a claim for its exact range —
|
|
|
- * every in-repo replace producer meters its replacement, so an unpriced
|
|
|
- * replacement is a shadow-price contract violation and must fail loud
|
|
|
- * rather than let the total drift.
|
|
|
+ * @throws when a replacement arrives with an armed claim for a different
|
|
|
+ * range — the metering event was adjacent, so this is a live producer's
|
|
|
+ * shadow-price contract violation, not historical data, and must fail
|
|
|
+ * loud rather than let the total drift.
|
|
|
*/
|
|
|
export function foldSurfaceProjection(
|
|
|
claim: ShadowPriceClaim | undefined,
|
|
|
@@ -74,10 +79,15 @@ export function foldSurfaceProjection(
|
|
|
const tokens = message === null ? 0 : estimateMessage(message)
|
|
|
const op = event.surfaceOp
|
|
|
if (op === 'append') return { deltaTokens: tokens, claim: undefined }
|
|
|
- if (claim === undefined || claim.start !== op.start || claim.end !== op.end) {
|
|
|
+ // Sessions recorded before the shadow-price protocol log replacements with
|
|
|
+ // no adjacent metering event; the bounded state cannot reconstruct the
|
|
|
+ // replaced range's price, so fold those neutrally — historical replay
|
|
|
+ // degrades to drift instead of failing.
|
|
|
+ if (claim === undefined) return { deltaTokens: 0, claim: undefined }
|
|
|
+ if (claim.start !== op.start || claim.end !== op.end) {
|
|
|
throw new Error(
|
|
|
`token surface: replace at seq ${event.seq} over range ${op.start}-${op.end} has no adjacent shadow price`
|
|
|
- + (claim === undefined ? '' : ` (armed claim covers ${claim.start}-${claim.end})`),
|
|
|
+ + ` (armed claim covers ${claim.start}-${claim.end})`,
|
|
|
)
|
|
|
}
|
|
|
return { deltaTokens: tokens - claim.tokens, claim: undefined }
|