Skip to content

refactor(sync): unify durable-_meta IRI term classifier (#1940) - #1944

Merged
Jurij89 merged 2 commits into
testnet-canaryfrom
fix/1940-iri-classifier
Jul 23, 2026
Merged

refactor(sync): unify durable-_meta IRI term classifier (#1940)#1944
Jurij89 merged 2 commits into
testnet-canaryfrom
fix/1940-iri-classifier

Conversation

@Jurij89

@Jurij89 Jurij89 commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Summary

  • The durable-_meta IRI term classifier existed as two near-identical copies: isIriMetaSubject in packages/agent/src/sync/durable-integrity.ts (requester ingest) and isIriTerm in packages/agent/src/sync/responder/graph-plan.ts (responder read). This unifies them into one shared module, packages/agent/src/sync/iri-term.ts, imported by both.
  • Behavior-preserving refactor. The two predicates share one private core (!term.startsWith('_:') && !term.startsWith('"')) but keep a deliberate, load-bearing divergence on empty/non-string input, so they are exported as two named functions, not collapsed into one or a defaulted flag:
    • isIriTerm (responder read) is the bare core — lenient: isIriTerm('') stays true, and no typeof/length guard is added, so the protocol responder read/paging path is byte-identical.
    • isIriMetaSubject (requester ingest) is typeof term === 'string' && term.length > 0 && <core>strict / fail-closed: isIriMetaSubject('') stays false and a non-string subject is false rather than throwing, preserving the Enforce IRI-only durable-meta subjects at unverified peer-ingest (root fix behind #1788 fail-loud) #1921 peer-ingest hardening.
  • The shared module carries jsdoc documenting why the divergence is intentional. Both call sites in durable-integrity.ts (4) and graph-plan.ts (2) are unchanged — only the definition moved.

Related

Diagrams

  • No flow changes

Files changed

File What
packages/agent/src/sync/iri-term.ts New shared module: private isIriTermCore + two named exports (isIriTerm lenient, isIriMetaSubject strict), jsdoc documenting the intentional empty/non-string divergence.
packages/agent/src/sync/durable-integrity.ts Deleted the local isIriMetaSubject; import it from ./iri-term.js. 4 call sites unchanged.
packages/agent/src/sync/responder/graph-plan.ts Deleted the local isIriTerm; import it from ../iri-term.js. 2 call sites unchanged (protocol read path).
packages/agent/test/iri-term.test.ts New test: IRI / blank-node (_:) / literal (") / empty-string / non-string inputs for both exports; asserts the divergence explicitly (isIriMetaSubject('') === false, isIriTerm('') === true).
packages/agent/vitest.unit.config.ts Register the new test in the explicit unit-config include list (the default test/**/*.test.ts glob already auto-discovers it).

Test plan

  • tsc --noEmit clean for @origintrail-official/dkg-agent.
  • pnpm exec vitest run --config vitest.unit.config.ts over durable / sync-responder / sync-verify / sync-control suites + the new iri-term test — 24 files, 308 tests green.
  • New test/iri-term.test.ts — 8 tests green; locks the shared behavior and the intentional empty/non-string divergence.
  • Behavior byte-identical: the Enforce IRI-only durable-meta subjects at unverified peer-ingest (root fix behind #1788 fail-loud) #1921 durable-integrity suites and the graph-plan responder suites pass unchanged after the import swap.

Extract the two duplicated durable-`_meta` IRI term predicates into one
shared module, packages/agent/src/sync/iri-term.ts, imported by both
callers. Behavior-preserving: the intentional, load-bearing divergence on
empty/non-string input is kept as two named exports over one private core
predicate, not collapsed to a single function or a defaulted flag.

- isIriTerm (responder read, graph-plan.ts): the bare core predicate,
  LENIENT — isIriTerm('') stays true; no typeof/length guard added, so the
  protocol responder read/paging path is byte-identical.
- isIriMetaSubject (requester ingest, durable-integrity.ts): STRICT,
  fail-closed — isIriMetaSubject('') stays false and a non-string subject is
  false rather than throwing, preserving the #1921 peer-ingest hardening.

durable-integrity.ts and graph-plan.ts drop their local copies and import
the shared exports; all 6 call sites are unchanged. Adds
test/iri-term.test.ts covering IRI / blank-node / literal / empty-string /
non-string inputs for both exports and asserting the divergence explicitly
(and registers it in vitest.unit.config.ts).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread packages/agent/src/sync/iri-term.ts Outdated
Address the bot's 🟡 on iri-term.ts:58 — the strict ingest helper is
documented as the fail-closed boundary for raw peer-ingest values (including
non-string), but its param type was `string`, forcing untyped/unknown-valued
ingest callers (and the test) to cast. Widen ONLY isIriMetaSubject to
`(term: unknown): boolean`; the existing `typeof term === 'string' &&
term.length > 0` guard narrows before isIriTermCore, so tsc stays clean and
runtime is byte-identical (the 4 durable-integrity call sites pass a
`string` `quad.subject` and are unaffected).

isIriTerm stays `(term: string): boolean` — the lenient responder helper runs
on trusted store rows and calls core directly (widening would break the
guardless startsWith). The string-vs-unknown split now honestly reflects
trusted-input vs fail-closed-untrusted-input in the type system.

Drops the `as unknown as string` cast from the non-string test case (it now
delegates directly) and keeps the non-string→false assertion.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@otReviewAgent otReviewAgent left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Agent completed this review and found no issues.

@Jurij89
Jurij89 merged commit 7f78233 into testnet-canary Jul 23, 2026
4 checks passed
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.

2 participants