Skip to content

feat(data): follow corpus refs across accounts and fix $type decoding when reading from a PDS#5

Merged
aaronstevenwhite merged 3 commits into
mainfrom
feat/cross-account-materialization
Jun 29, 2026
Merged

feat(data): follow corpus refs across accounts and fix $type decoding when reading from a PDS#5
aaronstevenwhite merged 3 commits into
mainfrom
feat/cross-account-materialization

Conversation

@aaronstevenwhite

@aaronstevenwhite aaronstevenwhite commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Summary

Make reading a dataset from a PDS actually work, and follow its references across accounts. Surfaced while verifying the discovery/TUI commands against the live repo.layers.pub: lairs pull reported "0 records" and materialize wrote empty Parquet even though the accounts held data. Two things:

  1. Fix a bug where every real PDS record was silently dropped on read (the wire $type failed model validation).
  2. Add cross-account reference following, so a Layers dataset that fans out across many single-purpose accounts (corpus, expressions, segmentations, annotations) materializes its component records, not just the corpus shell.

Changes

  • fix(data): decode by stripping the wire $type. _decode_envelope (lairs/data/corpus.py) and pull (lairs/author/publish.py) validated each record with the $type field the generated models do not declare, so every real PDS record raised ValidationError and was skipped. Both now go through the shared decode helper, which strips $type (and normalizes blob refs) first. This is the same $type bug fixed in _latest_published_versions in 0.2.0; these two read paths still had it, and the existing tests missed it because their fixtures omitted $type.
  • feat(data): cross-account ref following. load_corpus now walks every AT-URI a loaded record references, transitively and across account boundaries, fetching each referenced record by exact AT-URI (the PDS resolves handle or DID authorities). A dataset's expressions and the records those reference are pulled into the pool even though they live in separate accounts; only the records the corpus actually cites are fetched, not whole shared accounts. A dangling reference is skipped gracefully.
  • feat: follow_refs control. New follow_refs parameter on load_corpus (default True), exposed on the CLI as lairs materialize --follow-refs / --no-follow-refs (and the same on lairs inspect), for reading only the corpus's own account when the components are already materialized.
  • tests. Unit: $type is stripped on decode (corpus and pull), the ref helpers, cross-account follow on and off, transitive following two hops out, and dangling-ref skip. Live integration: a cross-account materialize across two real accounts on the dockerized PDS, plus a strengthened corpus-load live test that now asserts the $type-bearing record decodes.
  • docs. Note cross-account following in the reading-from-a-PDS guide.
  • release. 0.3.0 -> 0.4.0.

Verified live against repo.layers.pub: materialize <UD_Nayini> --follow-refs pulls its 10 cross-account expressions (was 0); --no-follow-refs reads only the corpus account; pull of a corpus account now pulls 12 records (was 0).

Type of change

  • Bug fix (a non-breaking change that fixes an issue)
  • New feature (a non-breaking change that adds functionality)
  • Breaking change (a fix or feature that changes existing behaviour)
  • Documentation
  • Refactor, test, or chore (no user-visible change)

Checklist

  • uv run ruff format --check lairs tests passes
  • uv run ruff check lairs tests passes (including ANN401; no Any in annotations)
  • uv run ty check --error-on-warning passes
  • uv run pytest passes (1087 passed, 72 skipped)
  • uv run pytest --run-integration -m integration passes against the Docker test PDS (72 passed)
  • uv run --group docs mkdocs build --strict passes
  • Tests added or updated for new behaviour, mirrored 1:1 under tests/
  • CHANGELOG.md updated (a new ## [0.4.0] section with Added and Fixed)
  • No @dataclass, pydantic, Any, or object annotation introduced; no em-dashes
  • Commits follow Conventional Commits, imperative mood, with no AI-assistant mentions

Related

No tracked issue. Found during live verification of the discovery/TUI commands against repo.layers.pub.

Note

This branch also carries one pre-existing dependency commit that was on its base, build: require glazing 0.2.3 for the WordNet and FrameNet fidelity fixes in the lexical extra (a one-line pyproject.toml bump), riding along to main.

@aaronstevenwhite aaronstevenwhite force-pushed the feat/cross-account-materialization branch from e04051a to 54291d0 Compare June 29, 2026 15:49
@aaronstevenwhite aaronstevenwhite merged commit 7f26dfb into main Jun 29, 2026
2 checks passed
@aaronstevenwhite aaronstevenwhite deleted the feat/cross-account-materialization branch June 29, 2026 17:24
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.

1 participant