Skip to content
1 min read · 257 words

Function: planDigest()

ts
function planDigest(view: RawPlanView): string;

Defined in: src/batteries/orchestration/encoding.ts:228

Compute the canonical, LOSSLESS digest of a RawPlanView.

Parameters

ParameterTypeDescription
viewRawPlanViewThe folded plan content at a revision.

Returns

string

A hex sha256 digest over the canonical, lossless encoding of view.

Remarks

Every approval binds to this digest, so it must be a faithful fingerprint of the plan content the operator actually saw — never a lossy one that could authorise a plan they did not see.

The strategy: recursively sort PLAIN-OBJECT keys only (leaving every encoder-owned value — Date, RegExp, Map, Set, typed arrays, ArrayBuffer, DataView, bigint, luxon values, and NodeRef/ParamRef instances — untouched), then sha256(encode(sortedSkeleton)). The encoder is the authority on how each value serialises, and it round-trips the reference classes and the whole EncodableValue domain losslessly, so the digest is stable across key order while remaining collision-free across semantically-different plans.

This is the second of the two digest strategies considered. The first — canonicalStringify from src/lib/utils/canonical_json.ts — was rejected because it walks objects with Object.keys, and Date, RegExp, Map and Set have no enumerable own keys, so it collapses each to {}. That is proven to collide: {pattern: /^inv-\d+$/i, when: <date A>, m: Map{k=>1}} and {pattern: /^cust-\d+$/, when: <date B>, m: Map{z=>9}} both canonicalise to {"m":{},"pattern":{},"when":{}}. An approval bound to that digest would authorise a plan the operator never saw. This implementation sorts only plain-object keys and never replaces the encoder's representation of the values it owns.