Mjp/include more in grant hash - #1089
Conversation
grantContentHash64 previously hashed only the grant's identity tuple plus the set of source-entitlement ids, deliberately excluding all annotations and source-map values as sync-transient noise. But GrantImmutable and per-source is_direct are stable per-grant facts (not bookkeeping that churns every sync) that the SDK already uses elsewhere to distinguish otherwise-identical grants (rollback_expansion's suspect-grant check, topological_merge's direct-wins-over-indirect upgrade) — so two grants differing only in immutability or provenance were silently digesting as identical. Bumps GrantDigestABIVersion to 2 since this changes what "the same grant" hashes to. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
| if err == nil { | ||
| current := len(val) == 4 && binary.BigEndian.Uint32(val) == GrantDigestABIVersion | ||
| closer.Close() | ||
| if current { |
There was a problem hiding this comment.
🟡 Suggestion: a current stamp certifies the whole node keyspace, but only wholesale destroyers erase the stamp — the partial invalidation paths (InvalidateGrantDigestPartitions at grant_digest_repair.go:105, stageGrantDigestInvalidation at rawdb/records.go:239) delete partition nodes plus the global root and deliberately leave the stamp. So a pre-stamp SDK that partially invalidates + repairs a file this SDK stamped v2 rebuilds only the touched partitions at v1 framing, recomputes the global root, and leaves the v2 stamp intact — this check then trusts a mixed v1/v2 rollup, which is the silent multi-version digest the PR body says it avoids. Consider tying the certificate to something a partial invalidation already destroys (e.g. carrying the ABI version in the global-root value, which every invalidation path deletes), or gating pre-stamp writers out via the engine schema version.
| // silently turned either back into a no-op (regressing to v1's blind | ||
| // spot — see grantContentHash64's ABI doc) would otherwise go | ||
| // unnoticed. | ||
| func TestGrantContentHashDistinguishesImmutabilityAndDirectness(t *testing.T) { |
There was a problem hiding this comment.
🟡 Suggestion: every immutable case in this file makes GrantImmutable the only annotation, so the multi-entry branch of scanGrantContentFactsRawBytes's case 8 loop is untested. Add a case where the annotation list carries another annotation (e.g. GrantMetadata) before/after GrantImmutable, plus one where a GrantExpandable was stripped by V2GrantToV3 — a raw-scan bug that stopped at the first Any would silently regress isImmutable to false for real connector grants, and the seal path has no oracle that would notice.
General PR Review: Mjp/include more in grant hashBlocking Issues: 0 | Suggestions: 3 | Threads Resolved: 0 Review SummaryScanned the full PR diff for security and correctness. The change folds Risk triage (per Security IssuesNone found. Correctness IssuesNone found. Suggestions
Prompt for AI agents |
grantContentHash64 previously hashed only the grant's identity tuple
plus the set of source-entitlement ids, deliberately excluding all
annotations and source-map values as sync-transient noise. But
GrantImmutable and per-source is_direct are stable per-grant facts
(not bookkeeping that churns every sync) that the SDK already uses
elsewhere to distinguish otherwise-identical grants (rollback_expansion's
suspect-grant check, topological_merge's direct-wins-over-indirect
upgrade) — so two grants differing only in immutability or provenance
were silently digesting as identical. Bumps GrantDigestABIVersion to 2
since this changes what "the same grant" hashes to.
Also adds real support and detection for the version upgrade mechanism, forcing us to re-build all indexes if the SDK version is newer than the hashes written to the file, avoiding a silent, incorrect multi-version rollup digest