Skip to content

Clotho Task 2: registry.mjs — closed registries, identity, validation (held for TELOS review)#113

Merged
dsmcewan merged 3 commits into
mainfrom
clotho-task2-registry
Jul 16, 2026
Merged

Clotho Task 2: registry.mjs — closed registries, identity, validation (held for TELOS review)#113
dsmcewan merged 3 commits into
mainfrom
clotho-task2-registry

Conversation

@dsmcewan

@dsmcewan dsmcewan commented Jul 16, 2026

Copy link
Copy Markdown
Owner

Task 2 — clotho/registry.mjs. Passed the deterministic gate + required-seat review (claude/agy/codex approve/high, signed) after a 3-round convergence. Held for The Eye's acceptance per the per-task cadence (#109).

Traceability

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):

  • Closed NODE_KINDS (11) / EDGE_KINDS (8) / ASSERTION_STATUS (5) as read-only Set facades (mutators throw; forEach passes 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 over canonicalJson({kind, locator}); exact outer schema.
  • validateLocator for all 11 kinds with exact fields + content bindings (truly exact: rejects inherited-enumerable/prototype-pollution, non-enumerable, and symbol keys).
  • validateSourceRef, validateAssertionStatus (incl. nonempty model:<seat>), validateEdgeInput (explicit from_node/to_node 64-hex ids checked against deriveNodeId(from_locator/to_locator); endpoint matrix on locator kinds; supersedes same-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.mjs is the only node/edge/status membership source
  • shallow/full-clone contract proven vs real git + injected units
  • npm test green; diff confined to clotho/; zero dependencies; no spine source changed; CI green (Node 18 & 20)

🤖 Generated with Claude Code

dsmcewan and others added 3 commits July 16, 2026 13:07
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
dsmcewan marked this pull request as ready for review July 16, 2026 18:33
@dsmcewan
dsmcewan merged commit ed0e05c into main Jul 16, 2026
17 checks passed
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.

1 participant