Skip to content

Strict description reader, phases 1-3 (Stage 1, PR 2 of 3) - #184

Merged
guijacquemet merged 3 commits into
mainfrom
claude/description-reader-phases-1-3
Nov 7, 2026
Merged

guijacquemet merged 3 commits into
mainfrom
claude/description-reader-phases-1-3

Conversation

@guijacquemet

Copy link
Copy Markdown
Contributor

Summary

PR 2 of the Stage 1 design (section 5): the reference reader's phases 1 to 3 (bytes and encoding, duplicate keys, header and version, structure, forbidden fields) of Spec experiment-v1, at Spec commit 8922a359ec261148a23a4b6e740a1bdb69e9e318. Additive: pure new modules plus tests; no run, notebook, CLI command or output uses them yet. Waits for R7 / Gate G0 like all of Stage 1.

Stacked on PR 1 (#183, vendored fixtures). This branch contains that commit, so until #183 merges the diff shows both; the base is main. PR 3 stacks on this one.

What changed

  • description_rules.py: the one table of the rules: the Code enum (all 43 E_/W_ codes of section 6), the closed field tables of section 4 (order and required), the forbidden names of 4.7, the enumerations and SUPPORTED_VERSIONS = {1} (a constant, no network). tests/test_spec_rules_table.py compares every table with the vendored text, so a rule changed in the Spec without the reader fails.
  • description_report.py: DescriptionIssue (code, table path, offending value), DescriptionReport.
  • description_read.py: phase 1 on raw bytes (BOM and invalid UTF-8 reach E_FILE_ENCODING; a key declared twice in any spelling is E_DUPLICATE_KEY, other decode errors E_TOML_SYNTAX), phase 2 (schema, schema_version, exact type(x) is int, no default version), and read_structure(bytes), which stops after the first failing phase and reports every error of it.
  • description_structure.py: phase 3: closed field sets, E_FORBIDDEN_FIELD for field names and ID keys, required fields, exact TOML types (no conversion), E_EMPTY/E_TEXT_EMPTY, E_ENUM, E_DATE_FORMAT, E_VALUE_RANGE, E_REPEAT_UNCONFIRMED.
  • Layers declared in pyproject.toml (all pure); AGENTS.md repository map and CHANGELOG.md updated.

Tests (new)

  • tests/test_spec_reader_phases_1_3.py: every invalid/ fixture of phases 1-3 (65 files: 8 + 7 + 50) gives exactly its Expected code set from its phase, each error with a path and value; every other fixture passes these phases; bytes-not-text; no-network version check; i14 exact types; 13 duplicate-key spellings and 9 syntax faults; hostile bytes (UTF-16, overlong, surrogate, 100,000 brackets) give a code, not a traceback.
  • tests/test_spec_refval_crosscheck.py: the vendored reference validator is the oracle. Same phase, code set and table paths on every fixture, and on 16,999 one-step mutations (delete, retype, rename, duplicate) of nine valid fixtures, 0 differences.
  • tests/test_spec_rules_table.py, and tests/test_spec_no_fallback.py is now non-vacuous (it asserts it sees the reader modules).

Regression check: on PR 1's tip no description* module exists (git ls-tree count 0), so all of these fail at import there; on this branch they pass.

Points where the text or the oracle is ambiguous or differs (for review)

  1. Duplicate key inside an inline table (i = {a = 1, a = 2}): tomllib says "Duplicate inline table key", which the oracle's mapping (it looks for "twice") reports as E_TOML_SYNTAX. Section 2.2 names inline tables explicitly, so the reader reports E_DUPLICATE_KEY. No fixture covers it; the oracle differs here only, and the cross-check compares fixtures and mutations (which never reach this text path). Worth a fixture in the Spec.
  2. RecursionError from extremely deep nesting is reported as E_TOML_SYNTAX (the oracle raises).
  3. E_EMPTY carries the empty table or array as its value (the oracle gives none); a value is absent only for a missing field.
  4. Top-level paths have no leading dot (notes, not .notes as in the oracle); the cross-check normalizes this.
  5. R_FILE_SYMLINK (section 2.1) and a read_description(path) entry point are not here: they belong with publication (PR 5 of the plan). The reader takes bytes.

Tests run

python tools/check.py: ruff check clean, ruff format clean, code rules clean, 3192 passed, 4 skipped (PR 1: 2713 passed, 4 skipped; main: 2316 passed, 4 skipped). The same 4 pre-existing skips; none in a Spec test. Not run: the full CI matrix (Windows, macOS, Python 3.11 with the pytest suite). Python 3.11 was checked on the decode mapping and the phase 1 fixtures only (disk limits stopped a full 3.11 environment); the duplicate-key mapping passes there.


Generated by Claude Code

Adds tests/spec_fixtures/: the MorphoBricks-Spec fixtures/ folder, docs/experiment-v1.md and
the reference validator (a test oracle only) at Spec commit 8922a359ec26, with PIN.toml.

- tests/test_spec_pin.py: the vendored fixtures/ folder must hash (git tree algorithm) to
  `git rev-parse <sha>:fixtures`; the two single files to their blob IDs. One changed, added,
  removed, renamed or CRLF-converted file fails.
- tests/spec_gate.py: a skipped or xfailed test in any tests/test_spec_*.py is a failure.
- tests/test_spec_no_fallback.py: no second table of codes or names, no lenient, legacy or
  default-version path in the reader modules (description*.py); the scan is itself tested.
- tests/test_spec_vendored_missing.py: with the vendored tree removed the suite fails.
- tests/test_spec_catalogue.py: every fixture has its header lines and a catalogue row.

Tests only: no change to the package, the CLI, the notebook or any output.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Scma6W3LC11s3qjXHZuGDm
New pure modules for the experiment-description reader (Spec experiment-v1 sections 2-4, 6):

- description_rules: the one table of the rules (codes, field tables, forbidden names, the
  enumerations, the supported version set), compared with the vendored text by a test.
- description_report: DescriptionIssue (code, table path, value) and DescriptionReport.
- description_read: phase 1 (raw bytes: UTF-8 without BOM, TOML 1.0, a key declared twice in any
  spelling as E_DUPLICATE_KEY), phase 2 (schema and version, exact types) and the driver
  read_structure(bytes), which stops after the first failing phase.
- description_structure: phase 3 (closed field sets, forbidden fields and ID keys, required
  fields, exact types, non-empty tables and arrays, dates, ranges, enumerations, N35).

Every invalid/ fixture of phases 1-3 (65) gives exactly its Expected codes from its phase;
all other fixtures pass these phases. The vendored reference validator is the oracle: the
reader agrees with it on every fixture and on ~17,000 one-step mutations of nine valid ones.
Not used by any run, notebook or CLI command yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Scma6W3LC11s3qjXHZuGDm
The pin test compared refval's __file__ with a POSIX suffix, which fails on Windows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Scma6W3LC11s3qjXHZuGDm
@guijacquemet
guijacquemet merged commit 0d45da8 into main Nov 7, 2026
7 checks passed
@guijacquemet
guijacquemet deleted the claude/description-reader-phases-1-3 branch November 7, 2026 12:42
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