A typed, append-only record for the project - #38
Draft
nrdxp wants to merge 10 commits into
Draft
Conversation
Node N1 (campaign 9) needs a failing acceptance surface before the entry.ncl/evidence.ncl contracts exist, driven from the architect's 34 format elements and node-boundaries.md [B6]'s four acceptance criteria. 45 fixtures (28 entry-, 17 evidence-) pin the concrete field shape the implementer builds to; 48 fiveam assertions verify each fails for exactly one diagnosed reason (the contract file is missing), never a Lisp typo, distinguishing a genuine contract refusal from an accidental pass caused by the contract not existing yet. Not registered in librecode-test.asd -- outside this dispatch's declared surface -- so `just test` does not yet run this suite; see the node's return for that gap.
Nickel contracts for the campaign-9 record schema (node N1): entry.ncl and evidence.ncl, derived field-by-field from the architect's 34 format elements and the admission-policy/observation rulings. Turns the red-first fixture suite in t/fixtures/record/entry/ green.
Wire record-schema-tests.lisp into librecode-test.asd (amended surface). Registration exposed a latent path-plumbing bug: resolving fixture/contract paths from *compile-file-truename*/*load-truename* lands in ASDF's fasl cache under a fasl-based load, not the source tree, once the file loads through asdf:test-op rather than a direct `sbcl --load`. Switched to asdf:system-source-directory, which is stable under both invocation styles.
The lead-maintainer's merge gate withheld consent over three defects
in node N1's entry/evidence contracts, found after the schema was
otherwise green (45/45 fixtures, 2550 checks).
Defect one: entry.ncl was a closed record with no `statement` field,
so it refused one outright rather than merely under-requiring it --
the same omission covered a question's `discharge` signpost and a
directive's `provenance`. The prior test-worker read [F51] as
excusing these fields ("required by no determined query") without
its next clause: "mandatory, on the human seam's authority and not
on a query's." All three are now admitted; discharge and provenance
each carry their own presence/absence coherence rule, and every
pre-existing fixture now carries `statement`.
Defect two: evidence.ncl's species rules were one-directional -- a
vouch validated regardless of what else it carried, and likewise for
a corroboration. Both are now bidirectional, the model being
entry.ncl's own CloserCoherent: each species requires its fields and
refuses the other's.
Defect three: the derivation edge's `role` qualifier had no fixture
at all, leaving node-boundary acceptance criterion (d) -- every
format element exercised by at least one fixture -- unmet for
element 17.
Nine new fixtures pin the negative half of each defect; the fixture
inventory count and this suite's header now read 54 (35 entry, 19
evidence).
Node N2 needs party, lineage, delegation, correlation-class-policy, reserved-seat-registry, and typed-event contracts before the admission gate (N3) can refuse out-of-pen writes and reserved-seat overreach. This pins their field shapes as 28 fixtures plus the acceptance suite, derived from format-elements [F19] [F21]-[F23] [F45] and the admission-policy log, following N1's bidirectional- coherence and closed-record idioms so the implementer builds to a fixed interface rather than inventing one. Registered in librecode-test.asd per the standing directive that registration is the test-worker's own surface; verified red through `just test` rather than a direct load, since a direct-load verification was N1's own blocking defect.
Node N2 of campaign 9: implements the schema for the party declaration, lineage declaration, delegation entry, and reserved-seat registry (party.ncl), the correlation-class policy (policy.ncl, kept a distinct entry class from lineage since their admission paths differ), and the typed protocol event closed to the refusal and dispatch kinds (event.ncl). Every kind/class multiplexing is bidirectional (requires its own fields, refuses every other branch's) rather than repeating N1's one-directional species- coherence defect. party.ncl deliberately avoids std.contract.any_of for its four-shape dispatch: it loses the specific rule name in its failure message, so the four branches are instead selected by a flat optional-field shape plus per-branch coherence predicates, mirroring entry.ncl's own kind/register multiplexing. Brings the red suite in t/record-parties-tests.lisp to green with no fixture changes.
The lead-maintainer's gate reproduced a lineage declaration naming the same party twice validating at exit 0: it is one party counted as two, not two correlated parties, and no downstream count can tell the two apart once admitted. LineagePartyCountExactlyTwo only checked length, not distinctness. A seat registry naming one seat twice had the same gap one class over. Both are closed by one shared AllDistinctBy fold rather than duplicating it per shape.
policy.ncl's own ClassesNonEmpty already refuses a policy naming zero classes since it partitions nothing, but a named class carrying zero members passed the same reasoning one level down: it partitions nothing either, and nothing in ClassesNonEmpty ranges over a single class's own member list.
event.ncl credited [AG2] alone with all five determinate refusal classes, but [AG2] names only four (unsigned_or_unattributed, signer_equals_author, out_of_pen, reserved_seat_overreach) -- the fifth (cycle) belongs to node-boundaries [B8]'s own scoping of N3.
check-fail asserted only a non-zero exit, never which rule fired -- the same conflation this project's own record already carries two prior instances of (a probe read by status that was actually malformed; a contract whose refusal reason was the only thing telling a regression from a wrong-shaped input apart). Every fail-side call site now names the expected diagnostic substring, and the three new REWORK fixtures (duplicate lineage party, duplicate seat, zero-member class) get their own tests pinning the rule each one exercises. t/record-schema-tests.lisp (node N1) carries the identical status-only conflation in its own check-fail but sits outside this node's declared surface -- left untouched, reported as a coupling.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this is
A typed, append-only record for this project, built as Nickel contracts with a YAML fixture corpus and a Lisp suite driving them. The premise is that everything else the tool does is a projection downstream of one artefact — a record whose entries state what is claimed, who claims it, and what backs it — so that artefact is specified and machine-checked first, before anything is built on top of it.
An entry carries a grade that says how it is backed. A claim is backed by a check that was actually run, or by a named witness's testimony, or it is derived from other entries and must name them. A question carries the condition that would close it and who can close it. The contracts refuse documents that do not fit, and the refusal names the rule that fired.
What has landed so far
The core entry and evidence contracts.
contracts/record/entry.ncltypes an entry — its identity, signer, anchor, grade, statement, edges and registers — andcontracts/record/evidence.ncltypes evidence as an independently signed entry naming its target, rather than as a field on the thing it supports. 54 fixtures undert/fixtures/record/entry/exercise it, driven byt/record-schema-tests.lisp.The party, policy and event contracts.
contracts/record/party.nclbinds a name to a key and to a declared pen — the entry classes that party may write — and covers lineage declarations, delegation, and the reserved-seat registry.contracts/record/policy.ncltypes correlation-class policy, whose admission path differs.contracts/record/event.ncltypes protocol events with a closed mapping from kind to payload, carrying the refusal and dispatch kinds. 28 fixtures undert/fixtures/record/party/andt/fixtures/record/event/, driven byt/record-parties-tests.lisp.How to review it
The suite is the entry point:
just testruns 2617 checks and exits 0 at the current tip. Every fixture whose name ends-failmust be refused, and every fail-side assertion in the newer suite names the rule it expects to fire, so a fixture refused for the wrong reason shows up as a failure rather than a pass.To see a contract refuse something directly:
The contracts are commented with the reasoning behind each closed set and each coherence predicate, which is where to look when a rule seems arbitrary.
Deliberately out of scope
The contracts describe and refuse; they do not yet run anything. Reproducing a check at its anchor, deciding admission at write time, and gating dispatch entries are separate pieces of work landing on this branch later.
Known gaps found during this work and not being fixed here are indexed in #37.
Checklist