Function: planDigest()
function planDigest(view: RawPlanView): string;Defined in: src/batteries/orchestration/encoding.ts:228
Compute the canonical, LOSSLESS digest of a RawPlanView.
Parameters
| Parameter | Type | Description |
|---|---|---|
view | RawPlanView | The 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.