Clotho Task 2: registry.mjs — closed registries, identity, validation (held for TELOS review)#113
Merged
Merged
Conversation
Implements v12 Task 2: the single authoritative source of node/edge/status
membership and canonical identity. Zero-dependency, Node stdlib only.
- NODE_KINDS / EDGE_KINDS / ASSERTION_STATUS / WEAVER_IDS as read-only Set
facades over private Sets (add/delete/clear throw; not Object.freeze(new Set)).
- canonicalJson: JSON-only, sorted keys, preserved array order; rejects
undefined/sparse/non-finite/bigint/symbol/function/cycle/non-plain proto.
- deriveNodeId = sha256(canonicalJson({kind, locator})); content-bound.
- validateLocator for all 11 kinds with exact fields + content bindings
(blob_sha / text_sha256 / entry_hash / summary_sha256; commit = no repo_ref).
- validateSourceRef (git:/file:@/ledger:#), validateAssertionStatus (weaver->
deterministic-extraction, human->human-authorized, model:->model-proposal),
validateEdgeInput with the full endpoint matrix incl. the four depends-on rows
and the discharges matrix; supersedes same-kind + human/model assertor.
- deriveRepositoryRef(git) shallow guard (rev-parse --is-shallow-repository ==
false; single 40-hex root via rev-list --max-parents=0 HEAD).
test-registry.mjs (replaces the Task 1 scaffold): full coverage incl. the
real-git shallow/full-clone fixture (D18) plus injected-git units. npm test green.
Traceability: v12 sha256:bdc93901..., authz-005, Eye impl-authorization #109.
Decisions D2/D13/D16/D18. Per-task cadence: separate PR, re-enters TELOS.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X8pTab2QMpfM2KsZmwkeYg
Applies The Eye's two rulings + the genuine fixes from the authz-005-constituency review of PR #113: Ruling A — edge inputs carry explicit node ids: validateEdgeInput now accepts {edge_kind, from_node, to_node, from_locator, to_locator, source_ref, asserted_by, assertion_status}. Both locators validated as exact {kind,locator}; both stated ids validated as 64-hex and compared against deriveNodeId(locator); a mismatch is rejected (never silently replaced); the endpoint matrix uses the locator kinds. Ruling B — real-git fixture driven through a PRIVATE, fixture-only, no-shell git allowlist in the test (init/config/add/commit/rev-list/rev-parse/clone shapes only; every other shape rejected). Not exported; not production; the Task 4a weaver-facing wrapper stays out of Task 2. Genuine fixes: forEach passes the facade (not the private Set) as the 3rd arg (+ mutation-boundary regression); WEAVER_IDS and ShallowRepositoryError removed from the public exports; exact outer schemas on deriveNodeId and docAddressKey; nonempty model:<seat> suffix required; deriveRepositoryRef accepts only the exact git-output forms (at most one terminal newline; malformed != shallow); per-kind missing repository_ref/content-hash + short/uppercase-hash tests added. npm test green; diff confined to clotho/; zero deps. Expected revision, not a release candidate. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X8pTab2QMpfM2KsZmwkeYg
Addresses the sole remaining required-seat (codex) blockers on PR #113: - requireExactKeys is now truly exact: rejects own symbol keys, own non-enumerable "required" fields (which would validate but be omitted by canonicalJson -> node-id collision, D13), and enumerable fields inherited via Object.prototype pollution. Regressions added, incl. proof such locators cannot mint a node id. - deriveRepositoryRef no longer String()-coerces the injected runner's output: non-string outputs are malformed (never accepted); the stable shallow error is kept only for the exact "true" form. Injected non-string tests added. - Tests completed: the three missing allowed endpoint rows (verified-by repository-file->test; documented-in code-symbol->contract-clause and repository-file->doc-section), a NUL-in-path rejection, and a table-driven assertion over every assertor x every status in the closed set. npm test green; diff confined to clotho/; zero deps. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X8pTab2QMpfM2KsZmwkeYg
dsmcewan
marked this pull request as ready for review
July 16, 2026 18:33
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.
Traceability
sha256:bdc93901…; Authorization: authz-005; Impl authority: Eye decision Clotho Phase 1: Eye implementation-authorization + Slice 1 proposal (for review) #109; Decisions: D2/D13/D16/D18.What this builds (exactly v12 Task 2)
clotho/registry.mjs— the sole authoritative node/edge/status membership + canonical identity + validation module (zero-dependency, Node stdlib only):forEachpasses the facade, not the private set). The five weaver ids are module-private (used internally for status coupling) — not a public registry and not part of the frozen Task 2 interface.canonicalJson(JSON-only, sorted keys, preserved array order; strict rejects).deriveNodeId= sha256 overcanonicalJson({kind, locator}); exact outer schema.validateLocatorfor all 11 kinds with exact fields + content bindings (truly exact: rejects inherited-enumerable/prototype-pollution, non-enumerable, and symbol keys).validateSourceRef,validateAssertionStatus(incl. nonemptymodel:<seat>),validateEdgeInput(explicitfrom_node/to_node64-hex ids checked againstderiveNodeId(from_locator/to_locator); endpoint matrix on locator kinds;supersedessame-kind + human/model),docAddressKey.deriveRepositoryRef— shallow guard; strict git-output parsing (exact forms; non-string/malformed ≠ shallow).clotho/scripts/test-registry.mjs(replaces the Task 1 scaffold) — full coverage incl. the mutation-boundary regression, every repository-scoped content binding, explicit node-id-mismatch rejection, table-driven status coupling, endpoint matrix, strict git parsing, and the real-git shallow/full-clone fixture (D18) driven through a private, test-only no-shell allowlist.Acceptance criteria (v12 Task 2 exit — verified)
registry.mjsis the only node/edge/status membership sourcenpm testgreen; diff confined toclotho/; zero dependencies; no spine source changed; CI green (Node 18 & 20)🤖 Generated with Claude Code