fix: silent multi-issuer Claim collisions and align Claim-domain schema [redesign(02)] - #346
Merged
Merged
Conversation
`Claim`'s id omitted `issuer`, so two trusted issuers attesting the same
target/type/scope collided on one row. `handleClaimAdded` silently
overwrote the first issuer's claim, and `handleClaimRevoked` silently
revoked it out from under the surviving issuer — both invisible to the
SDK's `issuerId: { in: $trustedClaimIssuers }` / `revokeDate: { isNull:
true }` filters (defect A12).
`issuer` is now the second id component, after `target` and before
`claimType`: `(target, issuer, claimType, …)`, one row per issuer per
claim. This is current-state semantics, matching how the SDK already
queries claims — block/eventIdx are deliberately not part of the id.
`handleClaimRevoked` now logs an error instead of silently returning
when the issuer-scoped lookup finds no row. A TODO marks this for
replacement by an `IndexerAnomaly` write once Phase 3 lands that entity.
A fresh `Claim.create` on re-issue after revocation clears `revokeDate`.
BREAKING CHANGE: Claim ids now include issuerId. Claims from multiple
trusted issuers over the same target, type and scope are now distinct
rows, so claim counts increase where they previously collided.
`ClaimScope` duplicated `Claim.scope` (a jsonField the SDK already
filters with `scope: { contains: $scope }`) and is unobserved by both
consumers — verified against `polymesh-sdk` origin/develop and
`polymesh-portal` origin/main. Removed the entity, `handleScopes`, and
its two call sites. `processClaimScope` is kept: it still resolves
ticker→assetId for `Claim.scope` and has nothing to do with the removed
table.
`handleDidRegistered` (`AssetDidRegistered`) only ever fed a
`ClaimScope` row; with that table gone it has no remaining side effect
and is now a documented no-op. Its subscription in project.ts is left
in place, unchanged.
BREAKING CHANGE: The ClaimScope entity is removed. Query Claim.scope
with { contains: … } instead, which is what the SDK already does.
TrustedClaimIssuer.issuer was a String! while Claim.issuer is already an Identity! relation — inconsistent within the same domain. Changed it to issuer: Identity!. This was planned as a non-breaking refactor, on the assumption that the generated filter field would stay `issuerId`. That assumption doesn't hold: TrustedClaimIssuer.issuer generated a plain `issuer` property/ filter field before this change (unlike Claim.issuer, which already generated `issuerId`), so converting it to a relation moves the filterable field from `issuer` to `issuerId` and turns `issuer` into a nested Identity filter. Escalated to a breaking feat, per the plan's own escalation rule, after confirming the shift in the regenerated src/types/models/TrustedClaimIssuer.ts. BREAKING CHANGE: TrustedClaimIssuer.issuer changes from a String scalar to an Identity relation. The generated filter field for the trusted issuer's DID moves from `issuer` to `issuerId`; querying the related Identity object now goes through the `issuer` field instead.
|
prashantasdeveloper
marked this pull request as ready for review
September 7, 2026 12:52
Base automatically changed from
redesign/01-standalone-fixes
to
docs/architecture
September 7, 2026 12:55
F-OBrien
approved these changes
Sep 7, 2026
Contributor
Author
|
/fast-forward |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Phase 2 — Claims
PR 3 of 10 in the indexer redesign series. Based on
redesign/01-standalone-fixes(PR #345).This is the only defect found in the whole review that silently returns wrong answers to a compliance code path. Everything else in the redesign is a missing capability or a recoverable error.
What's in this PR
feat!: 🎸 include the issuer in the Claim id(defect A12) —Claim's id omittedissuer, so two trusted issuers attesting the same target/type/scope collided on one row. Depending on write order,handleClaimAddedsilently lost one issuer's claim, orhandleClaimRevokedsilently revoked it out from under the surviving issuer.issueris now the second id component:(target, issuer, claimType, …), current-state semantics, one row per issuer per claim. A revocation that finds no row now logs an error (aTODOmarks it for replacement by anIndexerAnomalywrite in Phase 3) instead of silently returning.feat!: 🎸 remove the ClaimScope entity— duplicatedClaim.scope, unqueried by either consumer.feat!: 🎸 make TrustedClaimIssuer.issuer a relation— brings it in line withClaim.issuer. Escalated from the plannedrefactorto a breakingfeat!afteryarn codegenshowed the generated filter field moves fromissuertoissuerId(see commit body for detail).Consumer impact
SDK impact.
claimsQuery,claimsGroupingQuery,didsWithClaimsandissuerDidsWithClaimsByTargetchange behaviour, not shape — no query needs to change. They will start returning claims that were previously silently lost to the issuer collision. Claim counts will go up after the resync — that is the fix landing, not a regression.TrustedClaimIssuer.issuermoves from a plain string filter toissuerId(relation FK) plus a nestedissuer { ... }object. No consumer currently filterstrustedClaimIssuersby issuer per the consumer-query survey, but flagging the shape change explicitly since it wasn't in the original plan.ClaimScoperemoval: unobserved by both consumers (SDK and portal), confirmed againstpolymesh-sdkorigin/develop andpolymesh-portalorigin/main.Verification
All green. New unit tests in
tests/unit/mapClaim.test.tscover:Claimrows (regression test for A12).revokeDate.No
db/migrations/*entries — decision D5, full resync from genesis.