LearnNewsExamplesServices
Frontmatter
id17784
titleCanonical skill distribution: committed bytes, an immutable receipt, and CI that can see drift
stateClosed
labels
enhancementaiarchitecture
assigneesneo-opus-grace
createdAtAug 25, 2026, 11:23 PM
updatedAtAug 26, 2026, 11:06 AM
githubUrlhttps://github.com/neomjs/neo/issues/17784
authorneo-opus-grace
commentsCount1
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtAug 26, 2026, 11:06 AM

Canonical skill distribution: committed bytes, an immutable receipt, and CI that can see drift

Closed Backlog/active-chunk-19 enhancementaiarchitecture
neo-opus-grace
neo-opus-grace commented on Aug 25, 2026, 11:23 PM

Graduated from D#17756 at §6.2 quorum. [GRADUATED_FROM: D#17756 @ body updatedAt 2026-08-25T21:13:40Z]

Context

neomjs/devindex carries a hand-copied AGENTS.md differing from canonical in 8 hunks, two of them semantic — an older §identity_prompt_firewall duty scope and a missing #16528 rule. An agent working there runs an older constitution and nothing compares the two. Where substrate was copied it drifted invisibly; where it was not copied it is simply absent (neo-agent-brain has no .agents/skills at all).

The canonical store now exists — neomjs/neo-agent-skills, created 2026-08-25, public, empty, default branch main. This ticket defines what its first commit must be. That is the cheapest moment this contract will ever have.

Live latest-open sweep: checked the latest 20 open issues at 2026-08-25T21:22:18Z; no equivalent found. A2A in-flight claim sweep over 30 messages (all read-states): no overlapping [lane-claim].

The Problem

Skill substrate takes 181 commits per 90 days across 62 distinct days. No mechanism keeps consuming repos current, and the failure mode is not staleness — it is invisible staleness. devindex is not behind, it is forked, and nothing reports that.

The target population was itself wrong for most of the Discussion: the body priced this over "21 repositories" until a live census returned 52 (44 public / 8 private, 4 forks, 48 owned-non-fork, 0 archived). Every cadence and blast estimate computed over 21 was computed over the wrong set — which is why this ticket forbids hardcoded populations outright.

Two axes were also conflated, and separating them is load-bearing:

axis rule
Repo distribution every enrolled repo carries the same canonical tree — no per-repo subsets (operator SSOT ruling)
Harness exposure each harness receives the manifest-declared projection — per-harness subsets are legitimate

The Architectural Reality

  • .agents/skills/ — subject: 38 skills + 2 manifest files (skills.manifest.json, skills.manifest.schema.json).
  • .agents/skills/skills.manifest.json — budget + projection SSOT. defaults.claudeSymlinkRequired: true, per-skill override observed on debugging-antigravity. Lint-enforced at ai/scripts/lint/lint-skill-manifest.mjs:890.
  • .claude/skills/ — 37 committed per-skill symlinks into ../../.agents/skills/*: the current manifest-derived façade.
  • buildScripts/util/check-chore-sync.mjs (via .husky/pre-commit:1) — the existing sync-guard precedent, gating resources/content/** commits to sync branches.
  • .husky/pre-push:16 runs node ./ai/scripts/lint/check-commit-authorship.mjs as guard 2 of 3, under set -e at line 7.

The enforcing seat cannot be husky. check-chore-sync.mjs and mergeInheritance.mjs honour --no-verify, so hook-based guards are feedback, not authority. Authority is reusable CI plus required branch protection — owned by #17783.

Install-time materialization of the tree is falsified. npm ci --ignore-scripts is already deliberate practice in three of our workflows — openapi-service-parity-lint.yml:72, ticket-archaeology-lint.yml:45, jsdoc-type-lint.yml:46 — two carrying comments that it exists to skip the heavy postinstall. We have prepare, not postinstall, and --ignore-scripts skips both. An install-time tree yields zero skills, silently, with no error.

The Fix

Seed neomjs/neo-agent-skills and stand up B6 + B3:

  1. Canonical tree committed in the canonical repo and bot-synced as committed bytes into each enrolled repo.
  2. AGENT_SUBSTRATE_REVISION receipt — one immutable canonical commit/tree covering the skill tree, the manifest, and the public facts schema/renderer/public-common clauses. It excludes the internal maintainer constitution, which keeps its own revision authority in the D#17644 / Brain substrate.
  3. Promotion epochs — canonical changes coalesce behind a tested promotion revision; one campaign opens at most one PR per enrolled repo. Early promotion reserved for urgent safety changes.
  4. Per-harness façade generated from the manifest. Wave one preserves the current committed per-skill links unchanged.
  5. Two-leg consumer CI (callers of a reusable workflow): synced bytes equal canonical@receipt, and non-sync mutation of synced paths is rejected — while permitting manifest-declared per-harness projection differences.
  6. Registry predicate, never a hardcoded population. Enrollment is rows in the canonical org registry; exclusions are explicit rows with reasons; absence never means exempt. Skill-distribution enrollment is distinct from enforcement enrollment.

Contract Ledger Matrix

Target surface Source of authority Proposed behavior Fallback Docs Evidence
AGENT_SUBSTRATE_REVISION receipt file canonical repo pins immutable canonical tree+manifest+public clauses absent receipt ⇒ CI fails closed learn/agentos/ ADR new
synced .agents/skills/** in enrolled repos canonical@receipt byte-equal to canonical mismatch ⇒ CI red ADR devindex 8-hunk divergence is the red control
per-harness façade (e.g. .claude/skills/) skills.manifest.json manifest-derived projection missing façade ⇒ skills present but not auto-surfaced manifest schema claudeSymlinkRequired observed, lint-enforced
enrollment set canonical org registry predicate over rows absence ⇒ not exempt; explicit row required registry ADR 52-repo live census
org registry ↔ RepoContext registry owns identity/enrollment only non-overlapping field ownership D#17780 OQ8 comment 18153626

Decision Record

Required: ADR — amend ADR 0040 §2.7 + new ADR for the distribution contract (preserved from D#17756's graduation classification).

Decision Record impact

amends ADR 0040 — §2.7's separation principle is confirmed; its boundary description needs the rendered-contributor-file / projected-constitution split. A new ADR records the distribution contract itself. The amendment lands with this work, not after it.

Discussion Criteria Mapping

D#17756 criterion AC
OQ1 — manifest-derived façade is the required property AC-4
OQ2 — wave one preserves current façade; replacement deferred AC-4 + Out of Scope
OQ4 — receipt + two verification legs, constitution excluded AC-2, AC-3, AC-5
Registry → Context join (D#17780 OQ8) AC-6
OQ5 — CI/husky custody Deferred to #17783

Acceptance Criteria

  • AC-1neomjs/neo-agent-skills carries the canonical tree (38 skills + manifest + schema) and the public facts schema/renderer, on a first commit that matches this contract.
  • AC-2 — an AGENT_SUBSTRATE_REVISION receipt exists in each enrolled repo, pinning one immutable canonical tree; the internal maintainer constitution is provably absent from what the receipt covers. Merge-order constraint (2026-08-26): enrolled repos are the registry's rows, so this AC completes only when neomjs/devindex#6 and neomjs/neo-agent-brain#5 land. Both are green. They must merge before the neomjs/neo PR that closes this ticket, or the close fires against an unmet AC.
  • AC-3 — consumer CI fails red when synced bytes differ from canonical@receipt. Red control: devindex's current divergence must go red, demonstrated, not asserted.
  • AC-4 — the per-harness façade is generated from skills.manifest.json; a manifest-declared opt-out is absent from the generated façade, proven by a test, while an included skill is present. The current committed links are preserved unchanged in wave one.
  • AC-5 — non-sync mutation of a synced path is rejected by CI, and a manifest-declared per-harness projection difference is permitted by the same guard. Both arms proven.
  • AC-6 — enrollment is a registry predicate; no population integer is hardcoded anywhere in the mechanism. An excluded repo is an explicit row with a reason.
  • AC-7 — enforcement binds via reusable CI + required branch protection, citing #17783's binding receipt. MOVED TO #17783 (2026-08-26). Honest amendment rather than a residual carried by PR prose: the branch-protection half requires repository admin, and every maintainer seat holds maintainGET /branches/dev/protection returns 404 for us. An AC no assignee can execute does not belong on an implementation ticket. The reusable-CI half is delivered here (always-emitted, least-privileged, workflow_call); the binding is #17783's enforcement-custody scope, where the receipt it cites already lives.
  • AC-8 (Tier-2 revalidationTrigger) — on reactivation of gemini or kimi (participationStatusactive in ai/graph/identityRoots.mjs), this contract is re-presented for retroactive signal before being treated as settled.

Out of Scope

  • The Windows / core.symlinks=false façade replacement — its own ticket, behind an explicit cross-platform AC. Wave one preserves today's proven façade.
  • The one-directory harness-view optimization — its own ticket, and it owes a restart proof of both arms.
  • CI/husky custody and branch-protection mechanics — #17783.
  • The contributor-surface extraction — its own graduated ticket.

Avoided Traps

  • npm + postinstall as the tree transport. Sketched during the Discussion and falsified: --ignore-scripts is our own practice and silently yields zero skills.
  • One directory symlink replacing 37. Rejected by source authority — it would expose a manifest opt-out and forecloses per-harness projection permanently, since you cannot opt a skill out of a directory symlink.
  • Husky as the enforcing seat. --no-verify makes it feedback, not authority.
  • A hardcoded repo count. Produced a wrong-by-half population that survived multiple revisions unexamined.

Related

#17500 (parent epic) · #17783 (enforcement custody) · D#17756 (origin) · D#17644 (seat/session layer) · D#17782 (split runway) · ADR 0040

Origin Session ID: 10411e26-a3db-4f3d-b6bd-69e09045b804

Retrieval Hint: query_raw_memories("AGENT_SUBSTRATE_REVISION receipt promotion epochs ignore-scripts falsified") · query_summaries("D#17756 skills distribution graduation")

tobiu referenced in commit 302375c - "feat: two-leg consumer guard, reusable CI seat, enrollment predicate (neomjs/neo#17784) on Aug 26, 2026, 12:47 AM
tobiu referenced in commit 1b7cecd - "feat: generate the harness facade from the manifest (neomjs/neo#17784) on Aug 26, 2026, 12:49 AM
tobiu cross-referenced by PR #5 on Aug 26, 2026, 12:57 AM
tobiu cross-referenced by PR #4 on Aug 26, 2026, 12:57 AM
tobiu referenced in commit a3282a7 - "feat: bounded facts schema, renderer, and the Tier-2 revalidation gate (neomjs/neo#17784) on Aug 26, 2026, 1:03 AM
tobiu referenced in commit bf88227 - "chore: pin canonical@a3282a71e3 (neomjs/neo#17784) on Aug 26, 2026, 1:03 AM
tobiu referenced in commit 9d5e3ef - "chore: pin canonical@a3282a71e3 (neomjs/neo#17784) on Aug 26, 2026, 1:03 AM
tobiu referenced in commit a85dff3 - "fix: Leg A anchors to canonical history, not the consumer's own receipt (neomjs/neo#17784) on Aug 26, 2026, 2:05 AM
tobiu referenced in commit 63f838e - "chore: pin the repaired canonical guard (neomjs/neo#17784) on Aug 26, 2026, 2:05 AM
tobiu referenced in commit cddcbd9 - "chore: pin the repaired canonical guard (neomjs/neo#17784) on Aug 26, 2026, 2:05 AM
tobiu referenced in commit 8da0605 - "chore: promotion epoch — adopt the pr-review-guide change from #17794 (neomjs/neo#17784) on Aug 26, 2026, 2:11 AM
tobiu referenced in commit 3e53643 - "chore: adopt promotion epoch canonical@8da0605cd0 (neomjs/neo#17784)" on Aug 26, 2026, 2:11 AM
tobiu referenced in commit 9b24b05 - "chore: adopt promotion epoch canonical@8da0605cd0 (neomjs/neo#17784)" on Aug 26, 2026, 2:11 AM
tobiu referenced in commit c04b1ac - "fix: inert roster boundary, exact projection, least-privilege CI (neomjs/neo#17784) on Aug 26, 2026, 2:34 AM
tobiu referenced in commit 469f5f6 - "fix: exclude neo-agent-brain — extraction destination, not a stale consumer (neomjs/neo#17784) on Aug 26, 2026, 10:19 AM
tobiu referenced in commit 02cb8a2 - "fix(ci): canonical-ref defaults to dev, not the deleted main (neomjs/neo#17784) on Aug 26, 2026, 10:48 AM
tobiu referenced in commit ecb4e69 - "feat: npm package contract + consumer materializer (neomjs/neo#17784) on Aug 26, 2026, 11:01 AM