Skip to content

Test: cross-implementation interop corpus (jsonld.js RDFC-1.0 goldens) - #40

Merged
skydudie merged 1 commit into
mainfrom
test/interop-corpus
Aug 13, 2026
Merged

Test: cross-implementation interop corpus (jsonld.js RDFC-1.0 goldens)#40
skydudie merged 1 commit into
mainfrom
test/interop-corpus

Conversation

@skydudie

@skydudie skydudie commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Proposed changes

Problem

  • The PR Bugfix: Type-scoped context lookup ignores the active context #38 bug (type-scoped context lookup ignoring the active context) shipped despite a green W3C conformance suite, because the suite has zero fixtures where an embedded node @context defines a type carrying a scoped @context — verified by scanning every input fixture in the expand/toRdf/compact/flatten suites.
  • Internal round-trips can't catch this class of bug either: signer and verifier recompute the same wrong quads, so it only surfaces when a conformant external implementation rejects the signature (exactly how the VP interop incident was found).

Fix: a third safety net — pin the reference implementation's output

  • tests/Interop/: 5 realistic VC 2.0 / OBv3 documents whose RDF output is compared against goldens generated by jsonld.js 8.3.3 with RDFC-1.0 canonicalization — the exact quad set an eddsa-rdfc-2022 proof is computed over.
case exercises
embedded-node-type-scoped-context minimal PR #38 shape
minimal-vc-v2 VC 2.0 credential, subject claims in a type-scoped context
vp-embedded-vc-scoped-claims the incident shape: VP-embedded credentials with own @context, type-scoped Individual/CorporateEntity claims, @container:@graph blank-node graphs
obv3-achievement Open Badges v3 over the real 1EdTech context
vc-context-null-isolation VC 2.0 verifiableCredential @context:null isolation — known deviation, see below

Design properties

  • Offline-deterministic: remote contexts (W3C credentials/v2, 1EdTech OBv3) are vendored; both the PHP loader and the golden generator resolve only from the vendored index and error on any other URL — a mismatch can only mean the two processors disagree.
  • Goldens are regenerable and auditable: ./tests/Interop/regenerate.sh (pinned node container). jsonld.js safe mode stays on unless a case opts out via case.json with a mandatory note — so a fixture that silently drops terms cannot produce a golden by accident. Generator version + quad counts recorded in fixtures/goldens.json.
  • Label-independent comparison: dataset equality up to blank-node isomorphism (goldens use _:c14n0, the processor emits _:b0), with a +/- quad diff on failure. Same approach as the W3C toRdf harness.

Known deviation (guards the next fix)

  • vc-context-null-isolation documents a residual spec deviation, empirically confirmed by this corpus: Expansion::activateTypeScopedContexts() still falls back to the document-level context, so a presentation-level type-scoped context leaks into a credential isolated by the VC 2.0 @context:null reset — one extra fullName quad vs jsonld.js (which drops the undefined term, per spec).
  • The case uses the same xfail semantics as KnownBlockers: skipped while it diverges, fails the suite the moment it matches, so removing the documentBase fallback (planned follow-up) must also delist it.

Tests

  • Runs in the existing Unit testsuite (no CI changes): 341 passed, 1 skipped (the documented deviation).
  • W3C suite unchanged: 1287 passed, 15 xfail-skipped. Pint + PHPStan (max) clean.

Jira ticket link

  • N/A

Checklist

  • Goldens generated offline from jsonld.js 8.3.3 (RDFC-1.0), cross-checked case by case
  • Unit suite green incl. corpus; W3C suite unchanged
  • Pint + PHPStan pass

Further comments

  • W3C suite not runnable on case-insensitive APFS (tests/W3c vs tests/w3c collide); verified in a Linux container — CI covers it as usual.
  • All personal data in fixtures is fabricated; example-individual-v1.jsonld is an invented context modelling the incident's credential shape.
  • Follow-up planned: remove the documentBase fallback + findTypeDefRecursive() from activateTypeScopedContexts(); this corpus (specifically vc-context-null-isolation) is the regression guard for that change.

…dens)

Compares this processor's RDF output against goldens generated by
jsonld.js over realistic VC 2.0 / OBv3 documents — the safety net the
W3C suite does not provide. The suite has no fixture where an embedded
node @context defines a type-scoped context (the PR #38 bug shape), and
internal round-trips cannot catch a consistently-wrong processor: both
sides recompute the same wrong quads and only an external verifier sees
the signature break.

- 5 cases: minimal PR #38 shape, VC 2.0 credential, the incident
  VC-in-VP shape, OBv3 achievement, and VC 2.0 @context:null isolation.
- Goldens are RDFC-1.0 canonical N-Quads (the bytes eddsa-rdfc-2022
  signs), regenerated offline-deterministically via
  tests/Interop/regenerate.sh (pinned node container; vendored contexts
  only; jsonld.js safe mode on unless a case opts out with a note).
- Comparison is dataset equality up to blank-node isomorphism (goldens
  use _:c14n0, we emit _:b0), with a quad-level diff on failure.
- vc-context-null-isolation is a documented KNOWN deviation with xfail
  semantics: activateTypeScopedContexts() falls back to the document
  context, leaking a presentation-level type-scoped context into a
  credential isolated by @context:null (one extra fullName quad vs
  jsonld.js). The case starts failing the suite the moment the fallback
  is removed, so the entry cannot outlive the fix.

Runs as part of the Unit testsuite (341 passed, 1 skipped). W3C suite
unchanged (1287 passed, 15 xfail-skipped). Pint + PHPStan (max) clean.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
@skydudie skydudie self-assigned this Aug 13, 2026
@skydudie skydudie added the enhancement New feature or request label Aug 13, 2026
@skydudie
skydudie merged commit 69e4e9c into main Aug 13, 2026
5 checks passed
@skydudie
skydudie deleted the test/interop-corpus branch August 13, 2026 07:10
skydudie added a commit that referenced this pull request Aug 13, 2026
…ntext only (#41)

## Proposed changes

**Problem** — the two spec deviations left in
`Expansion::activateTypeScopedContexts()` after #38:

1. **`documentBase` fallback**: a type-scoped context defined only at
the *document* level could still activate inside a node whose context
had been reset. In the VC 2.0 `verifiableCredential` `@context: null`
isolation shape, a presentation-level type-scoped context (`Individual`)
leaked into the isolated embedded credential and emitted a quad no
conformant processor derives:
   ```
+ <did:example:subject-456>
<https://vocab.example.org/credentials#fullName> "Jane Citizen" _:b0 .
   ```
(the exact diff vs jsonld.js from the `vc-context-null-isolation`
interop case, which was listed as a known deviation in #40). Notably the
`Individual` type quad itself was *not* emitted — the scoped context
activated for a type that couldn't even expand to an IRI there.
2. **`findTypeDefRecursive()`**: the recursive walk through nested
`@context` entries in the document context. Dead weight since #38 — its
stated use case (a type like `DataIntegrityProof` defined inside an
imported security context) is handled by property-scoped context
activation, which has already made the type visible in the active
context by the time the typed node is entered.

**Fix** — per JSON-LD 1.1 exp. §5.5 step 11, the type resolves against
the **active context** (the type-scoped snapshot) and nothing else. Both
fallbacks removed; net −54/+? lines in `Expansion.php`.

**Evidence the fallbacks were dead weight (beyond the fixed case)**
- W3C suite unchanged: 1287 passed, 15 xfail-skipped — zero regressions
across expand/compact/flatten/toRdf/fromRdf/frame.
- Full unit suite: the only test that changed behavior was the interop
xfail guard, which **flipped green the moment the fallback was removed**
— exactly as designed in #40 — and is delisted here
(`INTEROP_KNOWN_DEVIATIONS` is now empty).

**Tests**
- New expansion unit test pinning the isolation behavior (document-level
type-scoped context must NOT activate after a property-scoped `@context:
null` reset; the unexpandable type stays `"Thing"`, `label` drops).
Expectations generated with jsonld.js 8.3.3; confirmed red against
`main`'s `Expansion.php`.
- `vc-context-null-isolation` interop case now passes for real: our
canonical N-Quads for the VC 2.0 isolation shape are dataset-identical
to jsonld.js.
- Unit: 343 passed, 0 skipped (was 341 + 1 skip). Pint + PHPStan (max)
clean.

## Jira ticket link

- N/A

## Checklist

- [x] New unit test red on `main`, green with the fix
- [x] W3C conformance suite unchanged (verified in Linux container; CI
covers it)
- [x] Interop corpus fully green, known-deviations list emptied
- [x] Pint + PHPStan pass

## Further comments

- Behavior change is quad-*removing* for the isolation shape (quads that
only this library emitted, never conformant verifiers). Standard VC/VP
documents — where embedded credentials carry their own `@context`, as
the VC 2.0 spec requires — are unaffected: the active-context lookup
(#38) already resolves their types. Same caveat as #38 applies to any
artifacts signed by pre-fix versions of this library over the
(non-conformant) isolation shape.
- Completes the follow-up plan from #40, which was merged specifically
to guard this change.
skydudie added a commit that referenced this pull request Aug 13, 2026
## Proposed changes

Prepares the repo for submission to the [W3C JSON-LD 1.1 implementation
report](https://w3c.github.io/json-ld-api/reports/) (we'd be the first
PHP implementation listed) and retires the development-phase framing
from the README.

**README rewrite**
- Removed the scaffolding: placeholder badge comments ("wired up in PR
1.2 / Phase 6"), the `Scope (delivered)` checkbox tracker (its content
is the conformance matrix), and the v0.42.0 comparison-methodology
footnotes.
- Restructured as: highlights → installation → usage → conformance →
interoperability → development notes. New sections for the interop
corpus (#40) and the implementation report.
- Fixed the install instruction (`composer require
accredifysg/php-json-ld:^1.0` → unconstrained; we're on 2.1.0).
- Conformance matrix refreshed against the current W3C suite: fromRdf is
now 50/54 (upstream added `#t0028`, which we pass), totals
**1,287/1,302**.

**W3C EARL implementation report**
- `reports/php-json-ld-earl.ttl` — generated from the v2.1.0 W3C suite
run: **1,275 assertions, 1,262 passed, 13 failed** (failures reported
explicitly, unlike most submissions). `specVersion: json-ld-1.0` tests
are excluded to match the consolidated report's JSON-LD 1.1 scope (same
as prior submissions, e.g. w3c/json-ld-api#652).
- `scripts/generate-earl-report.php` — regenerates the report from a
`--log-junit` run of the W3C testsuite, so future releases refresh it
with two commands (documented in the README). Verified: the report
ingests into the W3C `earl-report` toolchain with zero warnings and
renders as *PHP-JSON-LD / Accredify / PHP*.

**Supporting changes**
- `tests/w3c` submodule bumped to current upstream head, so `composer
test:w3c` reproduces the README/report numbers exactly (previously
pinned pre-`#t0028`: 1,286/1,301).
- CHANGELOG: backfilled the missing **2.1.0** section (the tag was cut
without one — it covers #38/#39/#40/#41) and recorded this PR's
additions under Unreleased.

## Jira ticket link

- N/A

## Checklist

- [x] Unit suite green (346 passed, includes interop corpus)
- [x] W3C suite green against the bumped submodule (1,287 passed / 15
xfail-skipped)
- [x] Pint + PHPStan pass
- [x] EARL report validates (rdflib) and ingests into the earl-report
gem with zero warnings

## Further comments

- Once this merges, the identical `.ttl` gets submitted as a PR to
`w3c/json-ld-api` (branch already prepared), giving the submission a
canonical in-repo artifact to reference.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant