Skip to content

Semantic data schemas for the tier-2 operational object types (#6) - #7

Merged
kreneskyp merged 7 commits into
mainfrom
spec/6-semantic-module-contract
Sep 4, 2026
Merged

Semantic data schemas for the tier-2 operational object types (#6)#7
kreneskyp merged 7 commits into
mainfrom
spec/6-semantic-module-contract

Conversation

@kreneskyp

Copy link
Copy Markdown
Contributor

Closes #6. Closes #5.

What this does

Migrates spec-objects-operational to the semantic-module contract, following
agent-ix/spec-objects-business#4 (merged 567e5c4) and
agent-ix/spec-artifacts-iso#34 (merged 6686f11).

  • TypeSpec source. typespec/main.tsp imports @agent-ix/semantic-core
    0.1.0 and declares one model per operational object type plus nineteen
    support models. make schemas emits 27 JSON Schema 2020-12 documents
    through the official @typespec/json-schema emitter at a pinned toolchain;
    make schemas-check (chained from make lint) fails on any drift, stale
    file, or half-bumped version.
  • Every declared object type gets a real emitted schema — all eight, none
    a type: object placeholder, each sealed and pairwise distinct in at least
    one required, forbidden, or item rule.
  • Manifest 0.3.0 gains the quoin FR-070 semantic block and a
    reference-form data_schema ({schema, digest}) per export. Every 0.2.0
    locator, the configuration-scope lint rule, and the lexicon term set are
    unchanged; every added locator is required: false.
  • Skeletons are executable typed fixtures: the typed ## Properties table
    (Field | Type | Multiplicity | Constraints), three ```sysml
    alternates, and an ## Invariants ```ocl fence on all eight, with
    ten negative counterpart fixtures.
  • Advisory only. No corpus repository is edited, and nothing in the package
    deploys, scales, migrates, pages or rolls back anything.

The modelling decision

Seven types — configuration, migration, sli, slo, alert, runbook, deployment —
are standing definitions. incident is the one observed execution: the
only model requiring an occurrence identity (an identity row and a
Timestamp row), and the only one declaring evidence.

evidence is the module's single canonical mapping to the operational
evidence record family quoin FR-059 defines. EvidenceRef carries that
family's own record_id pattern and record_shape vocabulary and redeclares
none of its fields — it references, never duplicates.

Purity is enforced at two levels: the seal refuses an observation key, and
every standing definition that admits fields refuses an occurrence row, so
a migration cannot declare applied_at: Timestamp and quietly become an
execution. Purity over field names is explicitly not claimed — no JSON
Schema over FieldDecl can refuse a row named consumed_budget — and the
spec says so rather than asserting a gate it does not have.

Issue #5 (lexicon truncation)

Every lexicon definition is now a quoted scalar, so a comma can no longer
truncate one and mint a garbage second key. container, deployment and
build are restored to the wording #5 records as lost. No term is added,
removed or renamed, and NFR-001-AC-3 asserts the restoration as a
strict-prefix relation against the frozen 0.2.0 manifest.

Reviewer note: this is a vocabulary edit. It restores text #5 already
documents as lost rather than introducing new wording, but it is the one part
of this PR that touches a lexicon and it is flagged for sign-off.

Governed cycle

/specify → Test Matrix → composite /spec-review (base + all seven
analyses, in parallel subagents, artifacts under
spec/reviews/6-semantic-module-contract/) → /spec-to-plan
(plan/Plan-001-semantic-data-schemas/, eleven tasks) → implementation →
/code-review + gap analysis (reviews/).

Three review findings were code-affecting and are fixed in the branch rather
than carried: the occurrence-row ban (failure-domain FND-200), the
EvidenceRef identity mismatch against quoin FR-059 (dependency FND-400), and
a @pytest.mark.xfail sitting between a trace marker and its def, which
made the binder skip the marker (evidence FND-503).

Gates

Gate Result
quire validate --scope . "spec/**/*.md" exit 0, structurally clean, zero warnings
quire validate --scope . "plan/**/*.md" exit 0
quire validate --scope . "reviews/**/*.md" exit 0
quire coverage --scope . 125/125 rows backed (100%), 88/88 symbols bound, zero unbacked rows, zero untracked symbols
make lint ruff + black + schema drift gate, clean
make test 162 passed, 7 skipped, 3 strict xfails, 100% coverage

The two numbers count different things. 125/125 is traceability: matrix
and criterion rows minted from this spec (67 from spec/tests.md, 58
acceptance and validation criteria) against the tracking tags on 88 test
symbols. 162 passed / 7 skipped / 3 xfailed is the pass rate. No
semantic row skips — they fail when the engine is absent (FR-005-AC-10,
asserted by TC-071); the 7 skips are the environment-gated non-semantic rows
that remain 🚧.

Neighbour defects filed, not worked around

One defect was found by this change and fixed in place: replacing the
pytest.skip in tests/test_skeletons_and_validate.py with a hard failure
immediately surfaced a broken locator lookup that had been passing as a skip —
the configuration branch read after_heading from a table_row locator,
which names its heading with under_section.

Carried blockers (recorded, not worked around)

Not met

  • IT-002 / TC-036 / TC-091 (Quoin install roundtrip) are written and no
    longer blocked on a Quoin build — the Quoin on this machine is
    0.23.1-2-g3e842ce, exactly IT-002's pin. They now wait only on the
    operator: the install writes to the machine-global quoin module store, so
    they are double-gated behind QUOIN_INSTALL_ROUNDTRIP=1 and restore the
    recorded state in a finally. No agent sets that on an operator's behalf.
    Run with
    QUOIN_INSTALL_ROUNDTRIP=1 poetry run pytest tests/test_quoin_install_roundtrip.py.
  • FR-001-AC-2..AC-4, IT-001, StR-001-VC-1/VC-2 need a running
    filament-core-service. Pre-existing issue FR-040: per-object roles + typed allowed_links #1 debt; gated behind
    FILAMENT_CORE_URL.

Do not merge on my behalf — this is for your review.

🤖 Generated with Claude Code

https://claude.ai/code/session_019a1pGXRt5jL8RqVEiLvhNf

Agent IX and others added 4 commits September 4, 2026 14:10
…t types (#6)

Author US-001, FR-002..FR-005, NFR-001 and IT-002 for the issue #6
semantic-module contract, update spec.md scope and the master Test Matrix,
and add the usecase/ and non-functional/ directories with their indexes.

FR-004 separates the seven standing definitions (configuration, migration,
sli, slo, alert, runbook, deployment) from the one observed-execution type
(incident) and maps operational evidence through a single `evidence` key
referencing the agent-ix/quoin#267 record family rather than copying it.

`quire validate --scope . "spec/**/*.md"` is clean with zero warnings.

Refs #6, #5

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019a1pGXRt5jL8RqVEiLvhNf
Emit one JSON Schema 2020-12 document per operational object type from a
TypeSpec source importing @agent-ix/semantic-core 0.1.0, declare the quoin
FR-070 `semantic` block with reference-form `data_schema`, and rewrite every
skeleton as an executable typed fixture with negative counterparts.

The module's central distinction: configuration, migration, sli, slo, alert,
runbook and deployment are standing definitions; incident is the one observed
execution. Only Incident requires occurrence identity (an identity row AND a
Timestamp row), and only Incident declares `evidence` — a single reference
into the quoin FR-059 operational evidence record family, carrying that
family's own `record_id` pattern and `record_shape` vocabulary rather than
copying any of its fields. Every standing definition that admits `fields`
refuses an occurrence row, so purity holds over declaration rows and not only
over record keys.

Also repairs the three lexicon definitions truncated by unquoted commas in
YAML flow mappings (#5): every definition is now a quoted scalar and the
`container`, `deployment` and `build` wording is restored.

`make lint` and `make test` are green (162 passed, 7 skipped, 3 strict
xfails); `quire validate --scope . "spec/**/*.md"` is clean with zero
warnings; `quire coverage --scope .` reports 125/125 rows backed.

Closes #5
Refs #6

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019a1pGXRt5jL8RqVEiLvhNf
Eleven tasks across tracks A (critical path), B (parallel) and C
(post-critical-path) plus one gate, covering every TC id in spec/tests.md.
The two FR cycles the dependency review found are broken by task ordering:
FR-002 splits into an enablement half before FR-004 and an emitted-set half
after it; FR-005 lands the skeleton sections before FR-003 adds their
locators.

Task-009 (IT-002 Quoin install) is blocked on a Quoin release carrying the
semantic installer; every other task is done.

Refs #6

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019a1pGXRt5jL8RqVEiLvhNf
…lied (#6)

Adds the eight review artifacts under spec/reviews/6-semantic-module-contract/
(base, integrity, ears-conformance, dependency, evidence, failure-domain,
risk-complexity, scope-boundary) and the code review under reviews/.

Three review findings were code-affecting and are fixed here rather than
carried:

- failure-domain FND-200: purity was enforced over record keys only, so a
  migration could declare `applied_at: Timestamp` and be an observed execution
  wearing a standing definition's schema. Every standing definition that
  admits `fields` now bans occurrence rows (FR-004-CON-5), with a tenth
  negative fixture pinning it. The unenforceable half — purity over field
  names — is stated as unenforceable rather than claimed.
- dependency FND-400: `EvidenceRef.record` was a `SemanticId`, but quoin
  FR-059 identifies its records with a bare token, so the mapping could not
  name most of the family it mapped to. `EvidenceRecordId` now carries
  FR-059's own identity pattern and `EvidenceRecordShape` its `record_shape`
  enum.
- evidence FND-503: a `@pytest.mark.xfail` between a trace marker and its
  `def` made the binder skip the marker — 85/88 symbols read while all 88
  carried a tag. Markers moved adjacent to their `def`; now 88/88.

Also fixes two code-review mediums: the generator's `readIfPresent` mapped
every read error to "file absent", and the npm-pack cleanup deleted the
staged paths unconditionally instead of only what the pack created.

Files a third neighbour defect: agent-ix/quoin#340 (`quire coverage` skips
status classification because its declaration reads `Status` while the
TestMatrix archetype asserts `Coverage Status`).

Refs #6

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019a1pGXRt5jL8RqVEiLvhNf
Agent IX and others added 3 commits September 4, 2026 14:55
A `@pytest.mark.trace` separated from its `def` by another decorator is a
binding trap: the xfail cases were already silently unread (85/88), and the
seven `@pytest.mark.integration` cases sat in the same shape. Every marker is
now the last decorator before its `def`, so the class of trap is gone rather
than the three instances of it.

`quire coverage`: 88/88 symbols bound, 125/125 rows backed.

Refs #6

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019a1pGXRt5jL8RqVEiLvhNf
The eight review artifacts are snapshots — several findings were applied while
later analyses were still running, so their verdicts read against trees that no
longer exist. dispositions.md is the single place that says what happened to
each finding: every high applied, the mediums applied or carried with a named
blocker.

Refs #6

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019a1pGXRt5jL8RqVEiLvhNf
)

`git diff --name-only origin/main...HEAD` with `assert changed` is a
merge-degrading guard. A merged change's path set is a fixed historical fact,
but that range is computed against a ref that moves: the moment the branch
merges the range empties, `assert changed` fails, and main goes red for a
branch that no longer exists. agent-ix/spec-objects-business main has been red
on exactly that test since 567e5c4 merged; this branch inherited the shape
from it.

None of the three forbidden path shapes (`corpus/`,
`fixtures/semantic-module`, `/vendor/`) exists anywhere in this repository,
so the tree form over `git ls-files` is equivalent in intent and strictly
stronger — it says these paths are absent from the repository, not merely that
one branch left them alone — and it answers the same question on a branch, on
main, and on main plus unrelated commits.

The liveness assertion is now `assert tracked` ('the repository tracks no
files, so this gate did not run'), which cannot become vacuously true the way
an empty diff range can. Falsified three ways: staging `corpus/probe.md`,
`tests/fixtures/semantic-module/probe.md` and `scripts/vendor/probe.md` each
fails the guard, and removing each restores green.

No `git diff` remains in any guard, so `--no-renames` has nothing to apply
to; the two in scripts/build_tools.py are pre-existing dirty-tree detection for
versioning, not range guards.

Also lands the gap-analysis findings: FR-002-AC-14 and TC-028 give the
destructive `stage-npm.mjs --clean` postpack step its own criterion and test
(FND-004); Task-009's blocker text is corrected from 'no released Quoin' to the
operator-consent gate it actually is (FND-002); plan.md no longer checks
StR-001 and FR-001 that its own matrix marks blocked (FND-005); and the
'zero warnings' and 'no status lies' claims are both narrowed to what is
actually measured (FND-007, FND-003).

Refs #6

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019a1pGXRt5jL8RqVEiLvhNf
@kreneskyp

Copy link
Copy Markdown
Contributor Author

Merge-degrading guard: fixed, falsified, and verified in a post-merge tree

tests/test_skeletons_semantic.py carried the positive-diff variant, exactly as described:

["git", "-C", str(REPO_ROOT), "diff", "--name-only", "origin/main...HEAD"]
assert changed, "the branch changes nothing"

Which form I chose, and why

The tree form. git ls-files finds no corpus/ path, no fixtures/semantic-module path and no /vendor/ path anywhere in spec-objects-operational — none of the three legitimately exists — so the tree assertion is equivalent in intent and strictly stronger: it says those paths are absent from the repository, not merely that one branch left them alone. No history pinning was needed; had one of the paths existed, the tree form would not have been equivalent and I would have pinned both endpoints to a sentinel commit instead.

Liveness is assert tracked, "the repository tracks no files, so this gate did not run" — never assert changed. It cannot go vacuously true the way an empty diff range can.

--no-renames: nothing to apply it to. No git diff remains in any guard. The only two git diff calls left are scripts/build_tools.py:40,47 — pre-existing cookiecutter dirty-tree detection for version stamping, not range guards.

Falsification

Perturbation Result
stage corpus/probe.md FAILEDcorpus/: ['corpus/probe.md']
stage tests/fixtures/semantic-module/probe.md FAILED
stage scripts/vendor/probe.md FAILED
each removed green again

The defect reproduced, then the fix, in the same post-merge tree

Squash-merged this branch onto a scratch clone's main, committed, pointed refs/remotes/origin/main at HEAD, and confirmed diff: [] / status: []:

OLD guard shape → FAILED: AssertionError: the branch changes nothing / assert []
NEW guard       → passed

A guard I could not make fail, and a defect I could not reproduce, would both be worthless. Both were exercised.

The three runs

Run State make lint make test quire validate spec quire coverage
1 — branch head green, 27 schemas match 163 passed, 7 skipped, 3 xfailed, 100% cov exit 0, clean 127/127, 89/89 bound
2 — post-merge squash diff: [], status: [] green, 27 schemas match 163 passed, 7 skipped, 3 xfailed, 100% cov exit 0, clean 127/127, 89/89 bound
3 — post-merge + unrelated sibling commit diff: [] green, 27 schemas match 163 passed, 7 skipped, 3 xfailed, 100% cov exit 0, clean 127/127, 89/89 bound

Runs 2 and 3 were provisioned with all three of poetry install, npm ci and make dev-quire. Counts are up from 162/125/88 because this push also adds TC-028.

One correction to my own method. My first post-merge run reported 111 failures and then, after a re-provision, "31 schemas / 4 xfailed". Both were measured on the wrong repository: the scratchpad path I used was concurrently occupied by another session's spec-objects-architecture clone, which silently replaced mine. I discarded both results, rebuilt the simulation under mktemp -d, and verified git remote get-url origin before trusting any number. The table above is from the verified clone. Reporting either discarded figure would have been a false claim about this branch.

The one genuine provisioning observation: with quire absent, every semantic row fails naming extract_semantic, make dev-quire and agent-ix/quire-rs#392 — the designed behaviour of FR-005-AC-10, not a defect.

Also in this push (gap-analysis findings)

  • FND-004stage-npm.mjs --clean is destructive and owned no criterion. Now FR-002-AC-14 + TC-028: after npm pack, no staged path survives at the root and the inner spec_objects_operational/ sources are byte-identical.
  • FND-002Task-009's blocker text said "no released Quoin carries the semantic installer". Stale: the Quoin here is 0.23.1-2-g3e842ce, IT-002's exact pin. Corrected to the operator-consent gate it actually is.
  • FND-005plan.md checked StR-001 and FR-001 that its own matrix marks 🚧. Unchecked, with a note saying why.
  • FND-003, FND-007 — two of my own claims narrowed to what is measured: "no status lies" is unmeasured while agent-ix/quoin#340 is open, and "zero warnings" covers document diagnostics only — the 18 stderr registry notices come from the module being registered from both the checkout and its worktree and vanish when the worktree is removed.

quire validate --scope . "spec/**/*.md" is structurally clean with zero document warnings on all three runs.

Not merging — yours to merge.

@kreneskyp

Copy link
Copy Markdown
Contributor Author

Mergeable. All gates green on the branch head and in a post-merge simulation; branch protection requires one approving review, which the authoring account cannot give, so this comment is the merge signal.

  • quire validate --scope . "spec/**/*.md" — exit 0, structurally clean, zero document warnings
  • quire validate over plan/**/*.md and reviews/**/*.md — exit 0
  • make lint — ruff + black + schema drift gate (27 schemas match the committed output)
  • make test — 163 passed, 7 skipped, 3 strict xfails, 100% coverage
  • quire coverage --scope . — 127/127 rows backed, 89/89 symbols bound, zero unbacked rows, zero untracked symbols
  • Post-merge and post-merge-plus-sibling-commit runs reproduce all of the above with diff: []

Two things a reviewer should look at deliberately rather than skim:

  1. The lexicon repair (3 lexicon definitions are silently truncated by unquoted commas in YAML flow mappings #5). It restores wording that 3 lexicon definitions are silently truncated by unquoted commas in YAML flow mappings #5 already documents as lost, asserted as a strict-prefix relation against the frozen 0.2.0 manifest so it cannot be a rewording — but it is still a vocabulary edit, and vocabulary edits want sign-off.
  2. The unmet rows. IT-002 / TC-036 / TC-091 are written and unblocked on tooling; they wait on your QUOIN_INSTALL_ROUNDTRIP=1 because the install writes to the machine-global quoin module store. FR-001-AC-2..AC-4 and StR-001-VC-1/VC-2 need a running filament-core-service (pre-existing FR-040: per-object roles + typed allowed_links #1 debt).

Do not read this as a request to merge on my behalf — I have not merged and will not.

@kreneskyp

Copy link
Copy Markdown
Contributor Author

mergeable — verified in an isolated clone (mktemp -d, git remote get-url origin asserted), post-merge.

Gate Result
Squash onto origin/main, update-ref diff:[] status:[]
make test 163 passed, 7 skipped, 3 xfailed, coverage 100%
quire validate --scope . "spec/**/*.md" structurally clean
Guards no git diff in any guard; the only match is the docstring saying why

Matches the branch's own three-run figures exactly.

The method correction in this PR is the most valuable thing in it

This branch's author discarded two of its own post-merge runs — reporting "111 failures", then "31 schemas / 4 xfailed" — after finding the scratchpad path had been silently replaced by a concurrent spec-objects-architecture clone. It rebuilt under mktemp -d and now asserts the origin URL before trusting a figure.

That was not a self-flagellation exercise; it caught a real one of mine. My first post-merge run of spec-objects-architecture#9 reported a failure in test_the_built_wheel_and_sdist_carry_every_exported_schema. My clone was in the same collision. Isolated, that suite is 163/163 clean and the failure did not exist. I have recorded the correction on that PR too.

A fabricated failure is worse than a missed one: it sends someone chasing a defect that is not there. The only reason either was caught is that this agent reported a collision against its own numbers instead of shipping them.

Guard fix

Inherited merge-degrading guard, positive-diff variant: assert changed over origin/main...HEAD, which empties on merge. spec-objects-business main had been red on the identical test since 567e5c4 (fixed in d1840b8).

Replaced with the tree form after checking git ls-files finds no corpus/, fixtures/semantic-module or /vendor/ path anywhere here — so it asserts absence from the repository, not merely that one branch left them alone, and cannot degrade on merge. Liveness is assert tracked, never assert changed. Falsified three ways, each restored, and the old shape was reproduced failing in the same post-merge tree beside the new one passing.

Claims narrowed rather than left standing

  • "no status lies" is unmeasured while quoin#340 is open — not "clean".
  • "zero warnings" covers document diagnostics only; the 18 stderr registry notices came from the module being registered from both checkout and worktree.
  • Purity over field names is explicitly not claimed; the key seal and the occurrence-row ban are what is enforced.

Gap analysis (SR-101, CONDITIONAL, no highs)

stage-npm.mjs --clean was destructive and owned no criterion — now FR-002-AC-14 + TC-028, asserting both that no staged root path survives and that inner sources are byte-identical. A stale blocker on Task-009 was corrected rather than inherited. All three trace-binding traps confirmed absent: 88 markers, 88 bound, 67 matrix ids set-equal to 67 tagged ids in both directions.

@kreneskyp
kreneskyp merged commit 95efb69 into main Sep 4, 2026
@kreneskyp
kreneskyp deleted the spec/6-semantic-module-contract branch September 4, 2026 22:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

Complete semantic data schemas for operational object types 3 lexicon definitions are silently truncated by unquoted commas in YAML flow mappings

1 participant