Skip to content

fix: silent multi-issuer Claim collisions and align Claim-domain schema [redesign(02)] - #346

Merged
polymesh-bot merged 3 commits into
docs/architecturefrom
redesign/02-claims
Sep 8, 2026
Merged

fix: silent multi-issuer Claim collisions and align Claim-domain schema [redesign(02)]#346
polymesh-bot merged 3 commits into
docs/architecturefrom
redesign/02-claims

Conversation

@prashantasdeveloper

Copy link
Copy Markdown
Contributor

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

  1. feat!: 🎸 include the issuer in the Claim id (defect A12) — Claim's id omitted issuer, so two trusted issuers attesting the same target/type/scope collided on one row. Depending on write order, handleClaimAdded silently lost one issuer's claim, or handleClaimRevoked silently revoked it out from under the surviving issuer. issuer is 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 (a TODO marks it for replacement by an IndexerAnomaly write in Phase 3) instead of silently returning.
  2. feat!: 🎸 remove the ClaimScope entity — duplicated Claim.scope, unqueried by either consumer.
  3. feat!: 🎸 make TrustedClaimIssuer.issuer a relation — brings it in line with Claim.issuer. Escalated from the planned refactor to a breaking feat! after yarn codegen showed the generated filter field moves from issuer to issuerId (see commit body for detail).

Consumer impact

SDK impact. claimsQuery, claimsGroupingQuery, didsWithClaims and issuerDidsWithClaimsByTarget change 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.issuer moves from a plain string filter to issuerId (relation FK) plus a nested issuer { ... } object. No consumer currently filters trustedClaimIssuers by issuer per the consumer-query survey, but flagging the shape change explicitly since it wasn't in the original plan.

ClaimScope removal: unobserved by both consumers (SDK and portal), confirmed against polymesh-sdk origin/develop and polymesh-portal origin/main.

Verification

yarn codegen && yarn typecheck && yarn lint && yarn test:unit

All green. New unit tests in tests/unit/mapClaim.test.ts cover:

  • Two issuers attesting the same target/type/scope produce two distinct Claim rows (regression test for A12).
  • A revocation by issuer B leaves issuer A's row untouched and unrevoked.
  • Re-issue after revocation clears revokeDate.
  • A revocation with no matching row logs an error instead of silently returning.

No db/migrations/* entries — decision D5, full resync from genesis.

`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.
@sonarqubecloud

sonarqubecloud Bot commented Sep 7, 2026

Copy link
Copy Markdown

Quality Gate Passed Quality Gate passed

Issues
0 New issues
1 Accepted issue

Measures
0 Security Hotspots
No data about Coverage
0.0% Duplication on New Code

See analysis details on SonarQube Cloud

@prashantasdeveloper
prashantasdeveloper marked this pull request as ready for review September 7, 2026 12:52
@prashantasdeveloper
prashantasdeveloper requested a review from a team as a code owner September 7, 2026 12:52
Base automatically changed from redesign/01-standalone-fixes to docs/architecture September 7, 2026 12:55
@prashantasdeveloper

Copy link
Copy Markdown
Contributor Author

/fast-forward

@polymesh-bot
polymesh-bot merged commit d8d7f18 into docs/architecture Sep 8, 2026
11 checks passed
@polymesh-bot
polymesh-bot deleted the redesign/02-claims branch September 8, 2026 12:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants