Skip to content

Clotho Task 4a: closed inventory, substrate, git + code weavers#117

Merged
dsmcewan merged 14 commits into
mainfrom
clotho-task4a
Jul 17, 2026
Merged

Clotho Task 4a: closed inventory, substrate, git + code weavers#117
dsmcewan merged 14 commits into
mainfrom
clotho-task4a

Conversation

@dsmcewan

Copy link
Copy Markdown
Owner

Implements frozen v12 Task 4a (clotho/inventory.mjs, clotho/weavers/util.mjs,
clotho/weavers/git.mjs, clotho/weavers/code.mjs, clotho/scripts/test-closure.mjs

  • unit tests). Held for the deterministic gate + required-seat review.
  • util.mjs — shared substrate: the ONE D33 module-load classifier + relative
    resolver + closure derivation; comment/string-aware lexer; Phase-1 export
    scanner + import parser; real-file walker; D26/D29 counted-iterator; D21
    physical-containment; no-shell weaver-facing git wrapper; source seeding.
  • inventory.mjs — closed inventory: package/doc roots + exclusions,
    contract-files (D31), run sources, five weavers + integer versions, the frozen
    REQUIRED_INVENTORY_IDS table, the deep-frozen D32 loader-safe-exports map,
    git/code implementation closures (D33). Per AM-17, only git+code weaver
    inventories are committed at this PR; ledger/test/doc land in 4b, orchestrator
    in Task 5.
  • git.mjs / code.mjs — the git weaver (introduced-by) and code weaver
    (depends-on, all four endpoint shapes).
  • test-closure.mjs — proves each committed weaver inventory equals its derived
    accepted-relative-module-load closure (never trusted), with fixtures for every
    accepted form and all fatal/no-edge failure modes.

Authorization: The Eye's Clotho Phase 1 implementation decision (#109), v12
sha256:bdc93901…, authz-005, per-task cadence. npm test green (7 tests);
confined to clotho/; zero dependencies.

🤖 Generated with Claude Code

dsmcewan and others added 14 commits July 16, 2026 18:26
…t draft)

Implements frozen v12 Task 4a:
- clotho/weavers/util.mjs — shared substrate: comment/string-aware lexer; the ONE
  D33 module-load classifier + relative-specifier resolver + closure derivation;
  Phase-1 export scanner + static-import parser; real-file walker; D26/D29
  counted-iterator; D21 physical-containment; no-shell weaver-facing git wrapper;
  source-descriptor seeding.
- clotho/inventory.mjs — closed inventory: package/doc roots + exclusions,
  contract-files (D31), run sources, five weavers + integer versions, the frozen
  REQUIRED_INVENTORY_IDS table, the deep-frozen D32 loader-safe-exports map, and
  the git/code implementation-file closures (D33). Per AM-17, only the git and
  code weavers' inventories are committed at this PR.
- clotho/weavers/git.mjs — git weaver: introduced-by edges via exact path-scoped
  git log; earliest result; malformed output fatal.
- clotho/weavers/code.mjs — code weaver: depends-on edges, all four endpoint
  shapes; symbol- vs file-level threading; unrepresentable-consumer only for
  unresolvable specifiers.
- scripts/test-{util,inventory,git,code,closure}.mjs — full unit matrix incl.
  closure-equality (inventories proven equal to derived closures, never trusted),
  frozen-table equality, D32 deep-equal/deep-freeze, counted-iterator and
  containment semantics; all registered in test-all.mjs.

npm test green (7 tests); confined to clotho/; zero deps; node: stdlib only.

Authorization: The Eye's Clotho Phase 1 implementation decision (#109), v12
sha256:bdc93901…, authz-005, per-task cadence.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X8pTab2QMpfM2KsZmwkeYg
…+ codex)

Applies all consolidated round-1 findings as one revision:
- ONE resolver (D33): code.mjs uses util's shared resolveRelativeSpecifier;
  private resolveModulePath deleted. Explicit .mjs only (no append, no other ext).
- Classifier exactness: mode-stack lexer blanks regex-literal interiors and
  preserves template ${...} code; each from-clause bound to its own governing
  keyword (forward parse); obj.import()/a.module.require() member calls excluded;
  export * as ns from recognized.
- D21 containment: lstat the allowed root in place (reject symlinked root) before
  any realpath; deepest existing ancestor resolved beneath the repo real path.
- walkFiles fails closed: throws on any symlinked entry (no silent skip); closure
  entry points get the same symlink/regular/containment checks.
- Git wrapper: validateGitArgs accepts exactly the frozen arg arrays (order
  enforced); fixed cwd/shell:false/encoding applied last so caller options cannot
  override them.
- Duplicate export descriptors are fatal (scanExports no longer de-dupes names);
  unsupported-export warnings propagate.
- Structured warnings {weaver,message} on both weavers (D10/AM-39 weaver side;
  driver check stays Task 5).
- Real closure-equality harness with per-form omission fixtures + all fatal
  failure modes; AM-34 test 19 strengthened against renaming.
- Fixed a pre-existing NUL-byte harness artifact in seedSourceDescriptors.

npm test green (7 tests); confined to clotho/; zero deps; no NUL bytes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X8pTab2QMpfM2KsZmwkeYg
…+ codex)

- Classifier: skips ALL JS whitespace (not just U+0020) so imports after tab/
  newline are detected; decodes string escapes (\x2e/. relative literals);
  module.require/bare require no longer double-classify.
- Unified fail-closed component walker: lstat every component of the repo-root
  ancestor chain and candidate parents before any realpath; reject symlink
  components; ENOENT tail distinguished from other lstat errors; used by
  resolveRelativeSpecifier, walkFiles, deriveAcceptedClosure, physicalContainment.
- identifierUsedOutside: a `/` after return/throw/case is a regex literal, so a
  binding only in `return /binding/` is not a false use.
- scanExports: per-keyword dispatch warns for every unsupported form incl.
  computed exports and dynamic symbol flow.
- code.mjs: unrepresentable-consumer reserved for explicit-.mjs specifiers that
  don't resolve to a real file below closed roots; non-.mjs -> no edge; symlink/
  escape/non-regular -> fail closed.
- git.mjs: only "" is no-result; "\n"/internal blank line is fatal.
- Tests: AM-34 test-19 asserts deriveAcceptedClosure calls both shared functions;
  counted-source final-break boundary pinned (D29 run-to-completion); direct
  locator-field assertions in test-code; fixtures for every fix.

Review artifact now includes registry.mjs (leaf; both committed closures correct).
npm test green (7 tests); confined to clotho/; zero deps; no NUL bytes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X8pTab2QMpfM2KsZmwkeYg
…_ROOTS curation

Round-3 required-seat findings (claude + codex):
- Lexer regex/division soundness: paren-kind stack distinguishes regex after a
  control-condition ) from division after an expression )/]/}/identifier; `of`
  removed from regex-introducing set.
- One canonical repo-relative POSIX validator (isCanonicalRepoRelPosix) for
  walker outputs, resolved closure paths, and git path args (reject absolute/
  ./../backslash/NUL).
- git CRLF: parseShaLines accepts LF or CRLF; bare CR / blank / non-40-hex fatal.
- LEDGER_SOURCES reframed as a committed CLOSED source inventory whose exact
  reviewed value at this SHA is empty (Clotho weaves no committed ledger
  artifact); removed the AM-17-deferral misframing; added exact shape assertion.
- Chain-walk helpers consolidated; counted-iterator N-1 contract ratified in
  comment + unit; test pins (export const = call, git imports only registry,
  non-.mjs missing-file silence, both-iterables-exhausted).

PACKAGE_ROOTS scope (The Eye's ruling): TELOS spine only (5 packages), with an
explicit PACKAGE_ROOTS_EXCLUDE = [ai-forge, forge, saas-forge] and a completeness
test discovering every package.json dir and requiring discovered == ROOTS ∪
EXCLUDE, ROOTS ∩ EXCLUDE = ∅. Per The Eye, this scope ruling is being frozen into
the plan via a narrow Daedalus delta (v12 -> v13) BEFORE acceptance — this commit
preserves the held implementation; it is not accepted until the plan is amended
and re-authorized.

npm test green (7 tests); confined to clotho/; zero deps; no NUL bytes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X8pTab2QMpfM2KsZmwkeYg
claude confirmed the implementation strong/faithful (AM-40 correctly implemented);
these are bounded test-only additions:
- Direct real-repo closure equality (test-closure §12): deriveAcceptedClosure of
  git/code/registry deep-equals the exact expected lists (registry proven leaf =>
  git/code inventories are the genuine recursive fixed point); derived array shown
  on failure.
- Accounting unreachability (test-util §12): walks own+symbol keys of the iterable
  and its iterator, asserting the D26 accounting accessor/counts are never exposed
  to the weaver.
- require-of-non-.mjs fatal (test-closure §13): literal require()/module.require()
  of a .cjs target is fatal ambiguous-extension in closure derivation, never a
  .cjs edge.

No weaver/util/inventory behavior change. npm test green (7 suites); confined to
clotho/; committed closures unchanged. Reviewed against v13 (authz-006).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X8pTab2QMpfM2KsZmwkeYg
…+ codex) vs v13

- Counted-iterator: removed the unauthorized "RATIFIED contract" framing; the
  iterator already counts N on natural for...of completion. Reframed comment+test
  to derive semantics from the frozen "without fatal error" definition + D29
  exhaustion; early-break tail is partial (uncounted).
- Lexer: postfix ++/-- => following / is division, so x++ / import("./m.mjs") is
  detected (was masked); import/export as a property key/member is never a load
  site (no keyword->from bleed).
- Declaration-aware scanners: scanExports ignores { export: 1 } property keys;
  scanImports span extends through assert{}/with{} so identifiers there are not
  uses.
- Canonical validator applied to resolveRelativeSpecifier + deriveAcceptedClosure
  entry outputs (fatal on noncanonical).
- Fail-closed fs: only ENOENT is absence; every other lstat/readdir error throws /
  returns false (walkFiles, physicalContainment, closure entry).
- PACKAGE_ROOTS completeness over TRACKED package.json (git ls-files), exact set
  equality both directions + disjointness + sorted/unique AM-40 arrays.
- code-weaver fixture: used named import absent from a seeded target's exports ->
  file-level fallback (not unrepresentable-consumer).
- Header provenance -> plan v13 (v12 + AM-40); per-form omission binding comment.

npm test green (7 suites); confined to clotho/; committed closures unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X8pTab2QMpfM2KsZmwkeYg
…) + refinements

Crux (codex hard stops): replaced the "split on every import/export token" lexer
heuristic with a shared parseModuleDecl + moduleDecls generator that delimits real
statement-position declarations by brace-matching the specifier list. Both
classifyModuleLoads and scanImports consume it, so:
- export { h as import } from "./helper.mjs" — a keyword-named alias no longer
  truncates the export region; the file is reached (closure no longer omits it).
- const o = { import: 1 }; ... import { alpha } from "./dep.mjs" — the property key
  no longer starts a spurious declaration nor swallows the real import; a
  pre-declaration use of alpha is detected (edge emitted, not suppressed).
- property/member/meta lookalikes ({import:1}, o.export, import.meta) => no site.
Regressions in test-util/test-closure (keyword-alias re-export, whitespace
module.require closure fixture).

claude refinements: git validate-before-spawn unit; blob_sha same-file pin;
GLOBAL_EXCLUDE/DOC_WEAVER_EXCLUDE deferred-wiring comment.

npm test green (7 suites); committed closures unchanged (git {registry,git},
code {registry,code,util}); confined to clotho/.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X8pTab2QMpfM2KsZmwkeYg
…ons (v15)

Implements The Eye's AM-41 (v15, authz-008) enforced closed source-profile
contract for the shared D33 scanner:
- Enforced profile: ProfileError + PROFILE_DIAGNOSTIC (unsupported-module-lexical-
  profile). lex() blanks one optional leading shebang (#! byte0/line1/LF|CRLF) and
  flags the first out-of-profile construct; classify/scan/closure fail closed
  (no edge, no closure result, no coverage) for an out-of-profile entry or member.
- Exact out-of-profile set b1-b6: bare CR/U+2028/U+2029; non-leading #! (code
  position; #! in a string/comment is in-profile per The Eye); <!-- / line-leading
  -->; string line-continuation / legacy-octal escape; string-literal specifier/
  alias name; unterminated string/comment/leading-shebang.
- Token context: member/private/whitespace/comment loader lookalikes not loads;
  dynamic import options/trailing comma accepted; contextual `from` as a name;
  closure resolver traverses .mjs+.cjs, code.mjs stays .mjs-only.
- Original-uncollapsed-component containment in the shared resolver (lstat before
  ..-collapse) + junction fixture.
- Two proved properties: supported-profile forms correct under variation
  (incl. LF/CRLF leading shebang, #!-in-string/comment); each b1-b6 fails closed.

Committed closures unchanged (git {registry,git}, code {registry,code,util}).
npm test green (7 suites); confined to clotho/; no NUL.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X8pTab2QMpfM2KsZmwkeYg
… (claude + codex)

- Regex/division by previous-significant-token (comment/space-robust prevSigWord),
  incl. control-head and for(..of) contexts (return /*c*/ /re/, if /*c*/ (a) /re/).
- Complete fail-closed profile: unterminated template, out-of-set string escapes,
  and truncated static/call forms (import{x}from, export * from, import("./x",,
  require("./x") all throw unsupported-module-lexical-profile (b6).
- Original-order containment: originalChainSymlinkFree no longer bails at first
  ENOENT; physicalContainment walks original uncollapsed components before resolve;
  link/.. and missing/../link/.. fixtures require kind===symlink.
- Closure extension generalization: shared closure resolver traverses every
  explicit-extension existing target (.cjs/.js/.json/...); code.mjs stays .mjs-only;
  extensionless stays fatal.
- git.mjs self-validates symbol (identifier) + path (repo-rel POSIX) before the git
  call. git wrapper drops the caller options bag (no env/argv0/cwd passthrough).
- test git ls-files via pinned no-shell allowlist; multi-declarator export const
  warns; inventory.mjs proven a profile-clean closure leaf.

Committed closures unchanged (git {registry,git}, code {registry,code,util}).
npm test green (7 suites); confined to clotho/; no NUL.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X8pTab2QMpfM2KsZmwkeYg
…ex convergence)

Applies codex's complete exhaustive enforced-profile list as one revision:
- Regex/division: await/yield in REGEX_KEYWORDS; brace-kind stack (block } => regex,
  object } => division). prevSigChar skips ALL whitespace + comments (obj.\nimport()
  is a member call). Call-position-only (function/method/property loader words not
  loads).
- Fail-closed completeness: b5 string-literal specifier/alias names (export {x as
  "public"}, import {"a" as b}); b6 unterminated regex (EOF/newline/char-class),
  truncated import{x}/export*, unbalanced dynamic options; malformed escapes
  \xG0/\uZZZZ/\u{}/out-of-range (range-safe decodeEscape, no RangeError leak);
  whitespace-prefixed line-leading -->.
- scanExports: member/method/private export lookalikes emit neither symbol nor
  warning.
- Closure external membership: admit only approved merkle-dag/ primitives regardless
  of allowExternal contents; inventory constrains PERMITTED_EXTERNAL_CLOSURE_FILES to
  merkle-dag/ paths.
- git runner controlled env: strips GIT_DIR/GIT_WORK_TREE/GIT_COMMON_DIR/... so
  inherited env cannot redirect the repo (two-repo unit).
- Backslash specifier rejected before resolution (Windows containment).
- code-weaver: default binding named `from` no longer dropped.

Committed closures unchanged (git {registry,git}, code {registry,code,util}).
npm test green (7 suites); confined to clotho/; no NUL.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X8pTab2QMpfM2KsZmwkeYg
…/b6 (codex)

- Significant-token completeness: string/template/regex literals reset the
  prev-significant token to a VALUE (so `/` after them is division), clearing stale
  keyword state; a class/function body { } is classified as a BLOCK (blockHeadPending)
  so a regex after `class C {}` is a regex and division-adjacent dynamic imports stay
  visible (return "x" / import("./dep.mjs") / 1 => import edge present).
- b5: a string-literal name reached directly in an import/export clause (incl.
  `export * as "ns" from`) fails closed; identifier namespace alias still classifies.
- b6: a literal-specifier dynamic import with unbalanced/truncated options or an
  unclosed call throws unsupported-module-lexical-profile instead of degrading to
  no-edge; valid options/trailing comma still classify.

Committed closures unchanged (git {registry,git}, code {registry,code,util}).
npm test green (7 suites); confined to clotho/; no NUL.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X8pTab2QMpfM2KsZmwkeYg
… (class-fix)

Root/class fixes replacing case-by-case patches:
- Regex-vs-division as a DENY rule: division only after a VALUE token (identifier,
  numeric/string/template/regex literal, this/super/true/false/null, )/]/value-}/
  postfix); regex after every reserved keyword automatically (default/extends/case/
  ... no longer a hand-maintained allow-list). Division-adjacent dynamic imports
  stay visible; regex lookalikes after keywords create no edge.
- One complete horizontal-whitespace predicate (tab/VT/FF/space/NBSP/BOM) used
  everywhere, incl. the b3 line-leading --> scan (\f--> fails closed).
- Bare/truncated `import` (terminated by ; or EOF) fails closed (b6) without
  affecting import()/import.meta/import "x"/clauses/{import:1}.

Committed closures unchanged (git {registry,git}, code {registry,code,util}).
npm test green (7 suites); confined to clotho/; no NUL.

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 17, 2026 05:50
@dsmcewan
dsmcewan merged commit f12e5d2 into main Jul 17, 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