Skip to content

Add did2 v2 scaffold: document and schema cache classes#120

Merged
stevevanhooser merged 32 commits into
V2from
claude/start-v2-development-tA41P
May 12, 2026
Merged

Add did2 v2 scaffold: document and schema cache classes#120
stevevanhooser merged 32 commits into
V2from
claude/start-v2-development-tA41P

Conversation

@stevevanhooser
Copy link
Copy Markdown
Contributor

Summary

Implements the initial scaffold for DID v2 development (step 1 of PLAN.md §9), introducing the +did2 namespace with core document handling and schema infrastructure. This establishes the API surface and basic functionality for working with V_gamma documents directly, without translation to the legacy V_alpha nesting structure.

Key Changes

  • src/did/+did2/document.m — New V_gamma document class providing:

    • Construction from JSON text, structs, or (className, valueStruct) tuples
    • Dot-path get() / set() for nested field access with automatic intermediate struct creation
    • iterate() for array-of-structure traversal (supporting [*] semantics)
    • toJSON() / toStruct() serialization and round-trip support
    • className() / classVersion() convenience accessors
    • validate() method delegating to schema cache
    • Static factory methods: fromJSON(), fromStruct(), blank()
  • src/did/+did2/+schema/cache.m — Schema cache singleton providing:

    • Lazy loading of V_gamma schema files from configurable path (env var DID_SCHEMA_PATH or sibling did-schema/schemas/V_gamma checkout)
    • getClass() for schema lookup with caching
    • superclasses() traversal with cycle detection
    • Placeholder methods (fieldsFor, queryablePaths, buildBlankDocument, validateDocument) that throw did2:notImplemented for iterative completion
    • CURIE registry loading from CURIE_lookups_meta.json
  • src/did/+did2/Contents.m — Package overview documenting namespace conventions and planned subpackages

  • tests/+did2/testDocumentScaffold.m — Function-based unit tests covering:

    • Construction from struct and JSON
    • Dot-path get/set with nested path creation
    • JSON round-trip serialization
    • Array-of-structure iteration
    • Error handling for missing fields and invalid paths
  • Workflow updates — Extended CI/CD to include the V2 branch alongside main

Implementation Details

  • Dot-path access (get/set) is fully implemented; array iteration via [*] is handled separately through iterate() to maintain the scalar/array distinction
  • The schema cache uses a process-wide singleton pattern with optional override capability for testing
  • Error handling uses specific error IDs (did2:document:*, did2:schema:*, did2:notImplemented) to surface incomplete pieces loudly
  • Provisional namespace decision: +did2 for the scaffold phase, with final naming deferred until v2 reaches main (PLAN.md §10)
  • Next steps logged in PLAN.md §12: implement remaining schema cache methods, then the in-memory query evaluator (step 2)

https://claude.ai/code/session_013i5twd1zXsZyFXRjb4hbwL

…ep 1

Begins step 1 of docs/v2/PLAN.md: introduce the parallel-namespace
+did2 package that holds V_gamma documents in their flat JSON shape
(no V_alpha base.* / document_class.* nesting).

Added:
  - src/did/+did2/document.m: V_gamma document object. Settled API
    surface (fromJSON/fromStruct/blank, get/set/iterate, toJSON,
    className/classVersion, validate). Dot-path get/set is
    implemented; [*] array iteration is exposed via iterate().
    Construct-from-blank and validate delegate to the schema cache.
  - src/did/+did2/+schema/cache.m: V_gamma schema cache class.
    Singleton bootstrap, schema-path resolution (DID_SCHEMA_PATH env
    override or sibling did-schema checkout), getClass, and
    superclass traversal are implemented; fieldsFor, queryablePaths,
    buildBlankDocument, and validateDocument are stubs that throw
    did2:notImplemented and will be filled in next.
  - src/did/+did2/Contents.m: package overview.
  - tests/+did2/testDocumentScaffold.m: function-based unit tests for
    the scaffolded surface (construction, dot-path get/set, iterate,
    JSON round-trip, error IDs).
  - docs/v2/PLAN.md: progress log entry documenting what landed and
    what comes next; +did2 chosen as the provisional namespace,
    logged in §1.

Note: per AGENTS.md these files have not been executed in MATLAB.
They are intended for human review and test in a licensed MATLAB
environment.
V2 is the long-lived development line for the +did2 / V_gamma rewrite
(docs/v2/PLAN.md). Self-tests should gate the V2 branch the same way
they gate main. Symmetry tests are intentionally left on main only —
the V_gamma document layout is not yet wired up to the symmetry
fixtures.
Spelling check is cheap and prose-heavy V2 docs (docs/v2/PLAN.md and
similar) benefit from it. Pair with the test-code update so any push
or PR against V2 gets the same gates as main, minus the symmetry
tests.
Lets us manually fire the self-test workflow from the Actions UI
against any branch. Note: the "Run workflow" button only appears
once this file is on the default branch (main); until V2 merges to
main, the trigger is defined but not surfaced in the UI.
Same caveat as test-code: the manual "Run workflow" button only
shows in the Actions UI once this file lands on the default branch
(main).
The floating @v1 tag was moved upstream on 2026-05-01/02 to a commit
whose workflow calls codecheckToolbox with more arguments than the
installed matbox MATLAB toolbox accepts, producing:

  Error using codecheckToolbox
  Too many input arguments.

Main's last green test-code run was on 2026-03-31, before that retag.
Pin to 4132d36 (Nov 2025), which is the workflow-file revision main
was using when it last passed. Comment explains the pin so we can
unpin once matbox-actions is fixed upstream.
The reusable workflow ehennestad/matbox-actions/.../test-code-workflow.yml
was retagged on 2026-05-01/02 to call codecheckToolbox with arguments
that the installed matbox MATLAB toolbox rejects ("Too many input
arguments"). Pinning to an older SHA of the reusable workflow did not
help, suggesting the new call shape lives in the older revision too.

NDI-matlab's run-tests.yml uses the individual matbox-actions
(install-matbox + check-code) at @v1 directly and is currently green.
Model test-code.yml on that pattern:

  - actions/checkout@v4
  - matlab-actions/setup-matlab@v2 (R2021b)
  - ehennestad/matbox-actions/install-matbox@v1
  - matbox.installRequirements(...) for mksqlite + vhlab-toolbox-matlab
  - ehennestad/matbox-actions/check-code@v1
  - TestSuite.fromFolder("tests", "IncludingSubfolders", true)

Removed:
  - The reusable-workflow `uses:` (replaced by explicit steps).
  - The CODECOV_TOKEN secret pass-through (no coverage XML produced
    yet; add back with a matbox.testToolbox-style entry point later).

The +did2 tests at tests/+did2/testDocumentScaffold.m are picked up
automatically by fromFolder with IncludingSubfolders.
ehennestad/matbox-actions/check-code@v1 fails on fresh runners with
"codecheckToolbox: Too many input arguments" — the action passes more
arguments than the installed matbox MATLAB toolbox accepts. The same
action passes for NDI-matlab because that repo's setup-matlab cache
holds an older matbox image, kept warm by daily CI runs. Ours is
cold and pulls the current matbox, exposing the mismatch.

Drop check-code from test-code.yml so the self-test suite can run.
Static analysis can be re-added once matbox-actions is fixed
upstream, or via MATLAB's built-in codeAnalyzer.
Matches NDI-matlab's run-tests.yml, which currently passes daily on
release: latest. R2021b is from 2021 and predates many matbox
changes; current matbox may rely on features (e.g. arguments-block
extensions) that R2021b mis-parses, which could itself be the source
of the "Too many input arguments" surfacing through codecheckToolbox.
Even if not the root cause, aligning with NDI's known-green release
removes one variable.
The codecheckToolbox "Too many input arguments" failure was driven by
the R2021b pin; matbox's current check-code uses MATLAB features
beyond R2021b. With release: latest, NDI-matlab runs the same
check-code@v1 successfully every day. Re-add the step so we get the
static-analysis gate alongside the test run.

Test run keeps `if: always()` so a check-code failure doesn't mask
test results.
Mirrors NDI-matlab's run-tests.yml step order. The previous order
ran matbox.installRequirements() before check-code, and check-code
failed there with "codecheckToolbox: Too many input arguments" even
on release: latest. NDI does not call installRequirements at all
and its check-code@v1 step passes daily, so the suspicion is that
installRequirements modifies the MATLAB environment in a way that
mis-aligns check-code's call to codecheckToolbox.

installRequirements is moved after check-code (still required for
the tests, which use mksqlite). Both later steps gain `if: always()`
so a check-code failure does not skip them.
The bare tools/tasks/codecheckToolbox.m wrapper was being picked up by
addpath(genpath('./tools')) inside ehennestad/matbox-actions/check-code@v1,
which shadowed the real matbox.tasks.codecheckToolbox. check-code calls
codecheckToolbox with multiple arguments; our zero-arg wrapper rejected
them with "Too many input arguments", breaking CI.

Move the wrapper into the +didtools package so it's invoked as
didtools.codecheckToolbox and never collides with the matbox symbol.
The old tools/tasks/codecheckToolbox.m is removed in the next commit.
Replaced by tools/+didtools/codecheckToolbox.m in the previous commit.
The bare wrapper at tools/tasks/codecheckToolbox.m shadowed
matbox.tasks.codecheckToolbox once tools/ was on the MATLAB path and
broke ehennestad/matbox-actions/check-code@v1 with "Too many input
arguments". Removing it lets check-code resolve to matbox's version.
@github-advanced-security
Copy link
Copy Markdown

You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool.

What Enabling Code Scanning Means:

  • The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
  • Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
  • You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.

For more information about GitHub Code Scanning, check out the documentation.

Comment thread src/did/+did2/+schema/cache.m Fixed
Comment thread src/did/+did2/+schema/cache.m Fixed
Comment thread src/did/+did2/+schema/cache.m Fixed
Comment thread src/did/+did2/document.m Fixed
Hermetic V_gamma schema fixtures under tests/+did2/fixtures/V_gamma/
so the +did2 self-tests can run without a sibling did-schema checkout
or network access.

  base.json                 root V_gamma class (upstream copy)
  CURIE_lookups_meta.json   trimmed CURIE registry (subset of upstream)
  demoA.json                V_gamma translation of v1's demoA (base + char value)
  demoB.json                multi-level inheritance (demoB -> demoA -> base)
  demoC.json                three _depends_on entries (V_gamma translation of v1 demoC)
  demoFile.json             two _file attachments (V_gamma translation of v1 demoFile)
  README.md                 origins and refresh procedure

Schemas themselves did not change between the flat-fields and
class-scoped versions of V_gamma — only the document-instance wire
shape did — so these fixtures are stable across that change.
Fills in the cache to the level step 1 of docs/v2/PLAN.md needs,
against V_gamma's class-scoped property-block document layout
(V_gamma_SPEC.md "JSON Format: Document Instances").

Implemented:
- classChain(className): root-first chain including the class itself.
- ownFields(className): the _fields list the class declares directly.
- fieldsFor(className): merged inherited fields tagged with the
  declaring class (struct array of {declaringClass, fieldDef}).
- buildBlankDocument(className): V_gamma class-scoped doc with
  x_classname / x_class_version / x_superclasses / x_depends_on at
  the top level, plus one property block per class in the chain
  (empty {} for zero-field classes). base.id auto-minted via
  did.ido.unique_id(); base.datestamp set to current UTC ISO-8601.
- validateDocument(docOrStruct): walks the class chain, validates
  each class's own _fields against its property block. Type-shape
  check runs before _mustBe* flags so a wrong type is reported as
  did2:validation:typeMismatch rather than did2:validation:notScalar.
  New error IDs: missingClassBlock, badClassBlock; reuses
  emptyField, notScalar, typeMismatch, maxLength, minLength,
  minimum, maximum, enum, nanValue, missingClassName.

queryablePaths remains a stub — it belongs to steps 3 and 4 once
the storage layer lands, and will return class-qualified dot-paths
(e.g., 'daqsystem.sample_rate.hertz').

The MATLAB-side storage convention (x_<name> instead of _<name> for
the four leading-underscore JSON keys) is documented in PLAN.md §4.1
and mirrored by did2.document.toJSON's regex rewrite on serialise.
Aligns did2.document with V_gamma's class-scoped property-block
document layout (V_gamma_SPEC.md "JSON Format: Document Instances").

  - className() / classVersion() now read from x_classname /
    x_class_version (MATLAB-legal renames of the leading-underscore
    JSON keys; mirrors what jsondecode produces).
  - toJSON() post-processes the jsonencode output with a regex that
    rewrites `"x_<name>":` keys back to `"_<name>":` so the wire form
    matches the spec. fromJSON relies on jsondecode's default
    behaviour to read it back.
  - Class metadata accessors no longer assume a `_class.name` nested
    path; they read flat top-level fields.
  - Header doc updated to describe the V_gamma layout, the x_<name>
    convention, and the round-trip path.

dot-path get/set, iterate, fromJSON/fromStruct/blank, and the
(className, valueStruct) constructor are unchanged in shape — they
operate on whatever struct the document carries.
Function-based unit tests for did2.schema.cache (plus end-to-end
through did2.document) against the in-repo fixture set
tests/+did2/fixtures/V_gamma/. Covers:

  - schema-path plumbing, getClass, missing-class error, CURIE
    registry presence
  - superclass chains: base (root), demoA (one parent), demoB
    (two-level)
  - classChain root-first ordering
  - ownFields counts (base=4, demoA=1)
  - fieldsFor declaring-class tagging across the chain
  - buildBlankDocument top-level metadata (x_classname,
    x_class_version, x_superclasses ancestor entries, empty
    x_depends_on)
  - buildBlankDocument class-scoped blocks: base, the concrete
    class, all chain blocks present; demoB has both demoA.value
    and demoB.value_b at distinct paths
  - buildBlankDocument base block: minted id (length 33),
    current-UTC datestamp starting with "20" and ending in "Z"
  - validateDocument: empty session_id throws emptyField, passes
    after filling; max-length boundary (300 fails, 256 passes);
    typeMismatch on numeric-where-char; missingClassName on
    structs without x_classname; missingClassBlock when a chain
    block is removed
  - End-to-end through did2.document: blank() / className() /
    classVersion() / get('base.id') / validate() round-trip
  - toJSON rewrite: serialised form contains "_classname", not
    "x_classname"; fromJSON re-parse preserves the class block.

setupOnce points the cache at the fixture via setSchemaPath;
teardownOnce resets the singleton.
Updates the living plan to the V_gamma "JSON Format: Document
Instances" wire shape (upstream did-schema commit 137f583, which
restored class-scoped property blocks) and logs the step-1 close.

Changes:

- Decision #8 in §1 is rewritten to describe class-scoped property
  blocks keyed by `_classname` verbatim (instead of the earlier
  "flat MATLAB struct" placeholder). Notes that MATLAB cannot have
  leading-underscore struct fields, hence the x_<name> internal
  convention and toJSON regex rewrite.
- §4.1 "In-memory document shape" is replaced. Documents the
  V_gamma class-scoped layout, the universal top-level keys
  (`_classname`/`x_classname`, etc.), per-class property blocks
  including empty `{}` for zero-field classes, and the
  `(declaring_class, _name)` field-identity rule. Compares
  V_alpha → V_gamma at the document level for converter (§7)
  reference.
- §12 progress log gains a 2026-05-12 entry covering: the SPEC
  update we drove upstream; the cache methods now implemented
  (classChain, ownFields, fieldsFor, buildBlankDocument,
  validateDocument); the document.m changes (className /
  classVersion read x_<name>, toJSON post-processes the encoded
  text); the hermetic V_gamma fixture set under
  tests/+did2/fixtures/V_gamma/; and the new testSchemaCache.m
  coverage. Step 1 is recorded as complete; queryablePaths and
  detailed named-composite/`_depends_on`-value validation are
  noted as deferred to follow-up.
GitHub Advanced Security / Code Analyzer flagged `numel(parts) == 1`
in did2.document.assignNested as a length comparison that should be
isscalar(parts) for clarity and performance. Cell array isscalar
returns true iff the cell has exactly one element, so the
substitution is equivalent.
V_gamma_SPEC.md restored the V_alpha-style top-level `document_class`
header with sub-keys `class_name`, `class_version`, `superclasses`
(no leading underscore). Schemas and document instances now both
carry that header; only `_depends_on` stays as a top-level
underscore-prefixed key.

Fixtures updated to match the new shape:

  - base.json: document_class header with empty superclasses
  - demoA.json: document_class.superclasses [{class_name: "base"}]
  - demoB.json: document_class.superclasses [{class_name: "demoA"}]
  - demoC.json: document_class header + three top-level _depends_on
                declarations
  - demoFile.json: document_class header + two top-level _file
                   declarations

Inside `_fields`, field definitions are unchanged (still _name,
type, _blank_value, _default_value, _mustBe*, _queryable,
_ontology, _documentation, _constraints).

Inside `document_class.superclasses[i]` in a schema file, the keys
are `class_name` + `_schema` (the schema-file path token). In a
document instance the same array uses `class_name` + `class_version`
instead — that distinction is enforced by the cache and the
buildBlankDocument output.
V_gamma_SPEC.md was amended (upstream did-schema commit 94091d8) to
re-introduce the V_alpha-style `document_class` header in both schema
files and document instances, with sub-keys `class_name`,
`class_version`, `superclasses` (no underscore prefixes).
`_depends_on` stays at the top level.

cache.m changes:

- superclasses(className) now walks `document_class.superclasses`
  (was top-level `_superclasses`).
- buildBlankDocument emits the new shape:
    doc.document_class.class_name / class_version / superclasses
    doc.x_depends_on (empty)
    doc.<class_name> for each class in the chain
  Each superclass entry is `{class_name, class_version}` (the
  document-instance form). The `_schema` path token used in schema
  files is dropped — document instances pin by version, not by path.
- validateDocument reads `doc.document_class.class_name` and throws
  did2:validation:missingClassName if absent.

Header comment updated to describe the new wire shape. The internal
`x_depends_on` / `x_name` convention for the remaining
underscore-prefixed keys is preserved; everything inside
`document_class` is plain (no x_ rename needed since none of those
keys start with an underscore).
V_gamma re-introduced the V_alpha-style `document_class` header in
both schema files and document instances. did2.document is updated
to match:

  - className() now reads `documentProperties.document_class.class_name`
    (was `x_classname`).
  - classVersion() now reads
    `documentProperties.document_class.class_version`
    (was `x_class_version`).
  - Header doc explains the new wire shape and notes that
    `document_class` + its sub-keys (`class_name`, `class_version`,
    `superclasses`) and the class-block keys are plain MATLAB
    identifiers, while only `_depends_on` (top-level) and `_name`
    (inside its entries) need the `x_<name>` rename.

The `toJSON` regex rewrite is unchanged — it correctly rewrites
`"x_depends_on":` and `"x_name":` on encode and leaves
`document_class` / `class_name` / `class_version` / `superclasses`
verbatim because they never carried the `x_` prefix.
V_gamma_SPEC.md was further updated (upstream did-schema commit
77c6363) to drop every leading underscore on NDI-extension keys, in
both schema files and document instances. Schema files now use:

  document_class { class_name, class_version, superclasses }
  maturity_level
  depends_on
  file
  directory
  fields[i]: name, type, blank_value, default_value,
             mustBeNonEmpty, mustBeScalar, mustNotHaveNaN,
             queryable, ontology { node, name }, documentation,
             constraints
  depends_on[i]: name, mustBeNonEmpty, documentation,
                 must_refer_to_document_class, multiple?
  file[i] / directory[i]: name, documentation
  superclasses[i] (schema file): class_name, schema
  superclasses[i] (document):    class_name, class_version

All NDI-reserved key names live in ndi_reserved_keys.json upstream;
schema authors must not reuse them.

Fixtures (base, demoA, demoB, demoC, demoFile) rewritten to the
plain-key shape. Inheritance, fields, dependency declarations, file
records all preserved structurally — only the key names changed.
V_gamma now uses plain keys everywhere (no leading underscores on
NDI-extension keys per upstream did-schema commit 77c6363). cache.m
follows suit:

  - Drops the `extractField` helper that probed for `_<name>` /
    `x_<name>` jsondecode-rename variants. Every key now resolves
    via direct `isfield`/`s.X` access.
  - superclasses() reads `s.document_class.superclasses[i].class_name`
    (no x_ rename anywhere).
  - ownFields() reads `s.fields` (was `_fields` / `x_fields`).
  - buildBlankDocument emits:
      doc.document_class.{class_name, class_version, superclasses}
      doc.depends_on  (empty struct array of {name, value})
      doc.<class_name> for each class in the chain
    No more `x_depends_on` or `x_name` MATLAB-side.
  - buildBlockForClass / buildBlankStructure read field-definition
    keys directly (name, type, blank_value, fields).
  - validateField reads mustBeNonEmpty / mustBeScalar /
    mustNotHaveNaN / constraints / type / name directly.

Same error IDs and validation semantics as before. The in-memory
shape now matches the JSON shape one-to-one — `jsondecode(jsonencode(s))`
is the identity for any well-formed V_gamma document.
V_gamma's "drop underscore prefixes" pass removed every leading
underscore on NDI-extension keys, so the in-memory MATLAB struct now
matches the JSON shape one-to-one. did2.document follows suit:

  - toJSON is now a bare jsonencode call. The
    rewriteXUnderscoreKeys regex helper is removed — there are no
    "x_<name>" keys left to translate.
  - Header doc updated to describe the simpler representation
    (jsonencode/jsondecode round-trip without any rewrite pass).
  - className() / classVersion() continue to read
    documentProperties.document_class.class_name /
    documentProperties.document_class.class_version unchanged.

dot-path get/set, iterate, fromJSON/fromStruct/blank, and the
(className, valueStruct) constructor are unchanged.
V_gamma dropped all leading underscores on NDI-extension keys, so the
in-memory document is the JSON shape verbatim. Tests updated:

  - Assertions on the document_class header read
    doc.document_class.class_name / class_version / superclasses(i).class_name
    (was doc.x_classname / x_class_version / x_superclasses(i).x_classname).
  - depends_on assertion: isfield(doc, 'depends_on') (was 'x_depends_on').
  - Validation tests unchanged — same error IDs and semantics.
  - toJSON test now confirms the wire form uses "document_class" and
    "class_name":"demoA" rather than the previous "_classname":"demoA"
    / "x_classname" pattern. Also re-parses and verifies the class
    blocks survive a JSON round-trip.

22 tests; same coverage as before, just adapted to the new shape.
V_gamma went through two back-to-back SPEC revisions on 2026-05-12:
restore class-scoped property blocks (did-schema 137f583), then drop
the leading-underscore convention on every NDI-extension key
(did-schema 77c6363). Net result: the in-memory MATLAB struct is the
JSON shape verbatim — no x_<name> aliasing or toJSON rewrite pass.

Changes:

- Decision #8 in §1 updated to describe the new layout
  (document_class header + class-scoped blocks + plain keys
  everywhere) and to record the two-step SPEC history.
- §4.1 "In-memory document shape" rewritten. The whole section is
  now a few short paragraphs plus a four-row table — the MATLAB
  representation is plain, no encoding/aliasing remarks needed. The
  V_alpha → V_gamma comparison table is updated to show that the
  converter (§7) is now a thin per-document data migration
  (V_alpha's class-scoped layout maps to V_gamma almost verbatim
  once `property_list_name` and `definition`/`validation` sub-keys
  are dropped).
- §12 progress log gains a 2026-05-12 entry covering both SPEC
  revisions and the resulting +did2 changes (cache, document,
  fixtures, tests).
- Stray `_queryable` / `_ontology` references in §§3.2, 5, 7 were
  corrected to the plain names.
Upstream did-schema (commit b54c940) moved `maturity_level` from a
top-level key into the `document_class` header alongside
`class_name`, `class_version`, `superclasses`. Fixtures updated to
match: all five fixture schemas (base, demoA, demoB, demoC, demoFile)
now carry `maturity_level: "work_in_progress"` as the fourth sub-key
of `document_class`.

No +did2 code reads `maturity_level` today, so cache.m / document.m
are unchanged. Tests are unchanged (no assertions on
`maturity_level`).
…slots

Mirrors the legacy +did test layout exactly:

  tests/+did/+test/+fixture/         → tests/+did2/+test/+fixture/    (placeholder)
  tests/+did/+test/+helper/          → tests/+did2/+test/+helper/     (placeholder)
  tests/+did/+unittest/+abstract/    → tests/+did2/+unittest/+abstract/ (placeholder)
  tests/+did/+unittest/*.m           → tests/+did2/+unittest/*.m       (real tests)
  tests_symmetry/+did/+symmetry/+makeArtifacts/   (placeholder)
  tests_symmetry/+did/+symmetry/+readArtifacts/   (placeholder)

Adds:

  tests/+did2/+unittest/testDocumentScaffold.m
      Function-based tests for did2.document's surface API.
      Same content as the previous tests/+did2/testDocumentScaffold.m;
      docstring updated to advertise the new runtests path:
        runtests('did2.unittest.testDocumentScaffold')

  tests/+did2/+unittest/testSchemaCache.m
      Function-based cache + end-to-end tests (22 tests).
      `setupOnce` recomputes the fixture path: two fileparts up from
      mfilename's directory now (was one), since this file moved down
      one package level.

  Empty placeholders (.gitkeep with a one-line comment explaining the
  legacy slot they reserve) under:
      tests/+did2/+unittest/+abstract/
      tests/+did2/+test/+fixture/
      tests/+did2/+test/+helper/
      tests_symmetry/+did2/+symmetry/+makeArtifacts/
      tests_symmetry/+did2/+symmetry/+readArtifacts/

The fixture data directory tests/+did2/fixtures/V_gamma/ stays put —
it's a non-package sibling holding static JSON, not a MATLAB package.

The old test-file locations (tests/+did2/testDocumentScaffold.m and
tests/+did2/testSchemaCache.m) are removed in follow-up commits. CI
discovery via TestSuite.fromPackage will land in a separate
test-code.yml update so the runner finds the relocated tests under
did2.unittest.
Relocated in the previous commit to tests/+did2/+unittest/testDocumentScaffold.m
to mirror the legacy +did/+unittest/ layout exactly. Invoke via
runtests('did2.unittest.testDocumentScaffold') going forward.
Relocated in the prior batch commit to
tests/+did2/+unittest/testSchemaCache.m, with the fixture-path
helper updated to walk one extra fileparts level so it still
resolves to tests/+did2/fixtures/V_gamma/. Invoke via
runtests('did2.unittest.testSchemaCache').
did2 self-tests moved into the tests/+did2/+unittest/ MATLAB package
to mirror the legacy +did/+unittest/ layout. TestSuite.fromFolder does
not recurse into +package directories, so switch to
TestSuite.fromPackage("did2.unittest", "IncludingSubpackages", true)
— same pattern test-symmetry.yml already uses for did.symmetry.*.

The IncludingSubpackages flag also picks up future +abstract base
classes when they land under tests/+did2/+unittest/+abstract/.
@stevevanhooser stevevanhooser merged commit 0567651 into V2 May 12, 2026
3 checks passed
@stevevanhooser stevevanhooser deleted the claude/start-v2-development-tA41P branch May 12, 2026 14:13
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