From cc177f28c55afbf76b8cae9633ac66b9a0880ea3 Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Fri, 4 Sep 2026 01:54:29 +0100 Subject: [PATCH 1/4] docs(manifest-spec): document depth addressing; withdraw the root-only rule MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The spec described a scheme the estate does not use. It mentioned ply, depth or nesting nowhere, while requiring that a repository hold "exactly ONE" manifest, located in the repository root. Measured against local checkouts on 2026-09-04, 15,872 manifests exist under hyper-repos and the majority sit below their repository root: the spec forbade the deployed reality. This is a live hazard rather than a documentation nit, because a conversion agent is currently reading this file as the authority on manifest naming. Changes, all confined to the File Naming section: - Records what the leading `0` is FOR. It is an attention claim, not an alphabetisation trick: `0` sorts ahead of AGENTS, AI, CLAUDE, llms and README, so the manifest is the first thing an agent sees on listing a directory. That claim must be renewed at every ply, because an agent may enter the tree at any depth. - Withdraws `!AI.a2ml`. No repository uses it, and collations that ignore leading punctuation sort it under A, behind AGENTS.md — defeating the one property the name exists to provide. - Withdraws "Multiple manifests are FORBIDDEN" and the root-only location rule, and states the depth-address prefixes (`0-`, `0.1-`, `0.2-`, `0.1.A-`) that are actually deployed. - Marks unit-relative numbering explicitly UNDER RULING and forbids tooling from hard-failing on it. 36% of deployed manifests are consistent only with the unit-rooted reading; that is a notation gap, not drift, and the ruling is the owner's to make. - Records `0-ply[-]-` as PROPOSED and NOT NORMATIVE. No migration is authorised by this document. No file extension is changed anywhere in this diff. The .a2ml/.deed rename is another agent's work and is deliberately untouched here. Co-Authored-By: Claude Fable 5.1 --- .../docs/AI-MANIFEST-SPEC.adoc | 76 +++++++++++++++++-- 1 file changed, 71 insertions(+), 5 deletions(-) diff --git a/0-ai-gatekeeper-protocol/docs/AI-MANIFEST-SPEC.adoc b/0-ai-gatekeeper-protocol/docs/AI-MANIFEST-SPEC.adoc index c7148c9ba..96c7b4235 100644 --- a/0-ai-gatekeeper-protocol/docs/AI-MANIFEST-SPEC.adoc +++ b/0-ai-gatekeeper-protocol/docs/AI-MANIFEST-SPEC.adoc @@ -46,13 +46,79 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S Manifests MUST use one of the following filenames (in order of preference): -1. `0-AI-MANIFEST.a2ml` (RECOMMENDED - sorts first alphabetically) +1. `0-AI-MANIFEST.a2ml` (RECOMMENDED) 2. `AI.a2ml` (legacy name, still supported) -3. `!AI.a2ml` (alternative for systems where `0` prefix problematic) -Repositories MUST contain exactly ONE manifest file. Multiple manifests are FORBIDDEN. - -The manifest MUST be located in the repository root directory. +The leading `0` is not decoration, and its purpose is not merely that it sorts +early. It is an attention claim. `0` sorts ahead of `AGENTS`, `AI`, `CLAUDE`, +`llms` and `README` in every collation an agent is likely to meet, so the +manifest is the first thing an agent sees when it lists a directory: it is +"readme.first" for an LLM. That claim has to be renewed at every level of the +tree, because an agent may enter the tree at any depth — see the Depth +addressing section below. + +NOTE: `!AI.a2ml` was offered by earlier drafts as an alternative for systems +where a leading `0` is problematic. It is WITHDRAWN. No repository in the estate +uses it, and it defeats the purpose of the name: collations that ignore leading +punctuation sort it under `A`, behind `AGENTS.md`. + +=== Depth addressing + +A repository is not flat, and an agent does not always enter it at the root. +Any directory that is a unit of attention in its own right MAY carry its own +manifest. A repository therefore MUST contain at least one manifest and MAY +contain several. + +IMPORTANT: earlier drafts of this specification stated that multiple manifests +were FORBIDDEN and that the manifest MUST be in the repository root. Both +statements are WITHDRAWN. They were never true of the deployed estate, in which +the majority of manifests sit below the root. + +Where more than one manifest exists, the filename prefix MUST encode the +manifest's depth below the repository root, so that a manifest is self-locating +even when read out of context: + +`0-`:: The repository root. REQUIRED: every repository MUST carry a root +manifest. +`0.1-`:: The first ply below the repository root. +`0.2-`:: The second ply below the repository root. +`0.1.A-`, `0.1.B-`:: Distinct bounded units at the first ply, used where several +sibling directories are separately addressable. The letter denotes distinctness, +not sequence, and is NOT a version number. + +The prefix thus carries two orthogonal facts: how deep the manifest sits, and +whether it names a bounded unit. + +The root manifest MUST be located in the repository root directory. A ply +manifest MUST be located in a directory whose depth matches the ply its prefix +declares. + +Because depth is independently derivable from the path itself, that last +requirement can be checked without any new metadata: the declared ply and the +actual ply are two derivations of the same fact, and asserting that they agree +is free. `scripts/check-manifest-ply.sh` in the `standards` repository performs +this check. + +==== Unit-relative numbering (UNDER RULING) + +Where a subdirectory is itself a self-rooting unit — that is, it carries a `0-` +manifest of its own — it is not yet settled whether its descendants number their +plies from the repository root or from that unit. Both readings are in live use +across the estate. + +This specification does NOT yet rule between them. Until it does, tooling MUST +NOT hard-fail a manifest solely because it is consistent with the other reading; +such manifests SHOULD be reported as unit-relative and left to the ruling. + +==== Proposed successor form (NOT NORMATIVE) + +The dotted form does not announce its own meaning to a reader who has not read +this section, and `0.1.A-` is readily misread as a version number. A successor +form `0-ply[-]-AI-MANIFEST.a2ml` has been PROPOSED to address both +problems. + +It is recorded here for visibility only. It is not ruled, it is not required, +and this document authorises no migration to it. == File Format From 006dabf6b4c53d1cb13750967bd4fbb633544d4b Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Fri, 4 Sep 2026 01:54:58 +0100 Subject: [PATCH 2/4] =?UTF-8?q?feat(gates):=20add=20check-manifest-ply.sh?= =?UTF-8?q?=20=E2=80=94=20advisory=20prefix-vs-depth=20check?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A manifest's filename prefix declares how deep it sits (`0-` root, `0.1-` first ply, `0.2-` second). That same depth is independently derivable from the path itself. Asserting the two agree therefore costs no new metadata — the check is free, and it catches a defect class nothing else watches. That class is repo extraction. Splitting a subtree out into its own repository decrements the true ply of every descendant. Any manifest copied sideways at that moment keeps its pre-split number and is now wrong, silently, forever. Both DRIFT-DEEP files found in the estate were born that way rather than having drifted: - standards/cartridges/0.2-AI-MANIFEST.a2ml, added 2026-05-26 by 7c2b8155 on the same day boj-server-cartridges was extracted from boj-server. The extraction itself was correct; the sibling format-canon copy into standards carried the old ply-2 number into a ply-1 home. - metadatastician/paint-type/www/0.2-AI-MANIFEST.a2ml, an unrelated plain off-by-one riding on a CI-hygiene commit, with nine correct 0.1- siblings. Measured over local checkouts, 2026-09-04, 15,872 manifests (duplicate checkout trees NOT deduplicated, per #703): EQUAL, git-rooted ..... 7,684 (48.4%) pass UNIT-RELATIVE ......... 5,716 (36.0%) reported, never failed DRIFT, declared < actual 2,468 (15.6%) warn; fails only under --strict DRIFT, declared > actual 4 ( 0.0%) FAIL UNPARSEABLE ........... 0 FAIL LEGACY-NAME ........... 766 no ply claim to check The gate is deliberately frame-aware rather than frame-imposing. Whether a self-rooting subunit numbers its children from the repo root or from itself is UNDER RULING (see the spec's File Naming section). Until that is ruled, the 36% consistent only with the unit-rooted reading are reported and never failed: the gate hard-fails only what is wrong under every reading, which preserves the owner's decision while still producing the evidence to settle it. Reads filenames only. It never opens a manifest, so it is extension-agnostic and has no contact with a2ml/deed grammar or K9 contracts. NOT wired into CI by this commit, and advisory by intent. `scripts/` here is run on demand. Wiring is deferred because this repository does not yet pass its own check — see the PR body. Co-Authored-By: Claude Fable 5.1 --- scripts/check-manifest-ply.sh | 269 ++++++++++++++++++++++++++++++++++ 1 file changed, 269 insertions(+) create mode 100755 scripts/check-manifest-ply.sh diff --git a/scripts/check-manifest-ply.sh b/scripts/check-manifest-ply.sh new file mode 100755 index 000000000..b06729f61 --- /dev/null +++ b/scripts/check-manifest-ply.sh @@ -0,0 +1,269 @@ +#!/bin/bash +# SPDX-License-Identifier: MPL-2.0 +set -uo pipefail + +# check-manifest-ply.sh — assert an AI-MANIFEST's declared ply matches where it +# actually sits in the tree. +# +# ── Why this gate exists ──────────────────────────────────────────────────── +# The manifest prefix encodes DEPTH, not version: `0-ply0-` is the repo root, +# `0-ply1-` the layer beneath it, and so on (older deployed form: `0.1-`). +# The depth is therefore stated twice — once in the filename, once by the path +# the file sits at. That redundancy is free, and asserting the two agree is the +# only mechanical detector for the two failures the owner named: manifests +# packing up at the root, and manifests scattered into directories they do not +# govern. +# +# Measured on local checkouts 2026-09-04 (duplicate checkout trees NOT removed, +# see standards#703 — treat these as counts of FILES ON THIS DISK, not as an +# estate census), 15,872 manifests under hyper-repos: +# +# EQUAL, git-rooted ................. 7,684 (48.4%) +# UNIT-RELATIVE .................... 5,716 (36.0%) +# DRIFT, declared < actual ......... 2,468 (15.6%) +# DRIFT, declared > actual ......... 4 ( 0.0%) +# UNPARSEABLE ...................... 0 +# LEGACY-NAME ...................... 766 +# +# UNIT-RELATIVE is not drift. A sub-project that considers itself "a different +# repo of its own" numbers its children from ITSELF, and until the `-` +# tag existed it had no way to say so — it declared `0-` and looked wrong. 36% +# of this disk is in that state. A gate that called those 5,716 files errors +# would be measuring the notation gap, not the tree, and would be switched off +# in a week. +# +# ── What this gate does NOT do ────────────────────────────────────────────── +# It reads FILENAMES ONLY. It never opens a manifest, never parses A2ML, K9 or +# .deed, and has no opinion on any of their grammars — those belong to another +# agent. It is extension-agnostic by construction, so the in-flight +# .a2ml → .deed rename cannot break it and it needs no coordination to land. +# +# ── Failure semantics (deliberate, not handwaving) ────────────────────────── +# HARD FAIL only on what is wrong under EVERY reading of the scheme: +# UNPARSEABLE a file named like a manifest whose prefix parses under neither +# the canonical `0-plyN-` form nor the deployed dotted form. An +# unrecognised manifest is not a pass; it is an unknown. +# DRIFT-DEEP declared ply GREATER than actual depth. This cannot arise from +# a tree growing beneath a file, so it is always a naming error +# or a file that moved up. 4 on this disk — enforcing costs +# nothing and it catches the real ones. +# +# WARN (advisory) unless --strict: +# DRIFT-SHALLOW declared ply LESS than actual depth under both frames. This +# is the migration debt (2,468). It is real, but hard-failing +# 15.6% of the estate on day one gets the gate disabled rather +# than the files fixed. Ratchet it with --strict once the +# migration lands. +# KNOWN SOFT SPOT: a file with unit_depth < declared < +# git_depth is wrong under BOTH frames — too deep for (b), +# too shallow for (a) — but is classified SHALLOW, because +# which way it is wrong is undeterminable until the frame is +# ruled on. Revisit when the OWNER-CONFIRM lands. +# +# REPORTED, never failed: +# UNIT-RELATIVE consistent with a self-rooting ancestor. Pending the +# OWNER-CONFIRM in PREFIX-CANON.adoc on how a unit's children +# are numbered, this gate does not get to pick a frame. +# LEGACY-NAME `AI.` / `!AI.` — carries no ply claim, so there is +# nothing to check, but silently not seeing them is a blind +# spot and blind spots are how the last two schemes decayed. +# +# Finding ZERO manifests under a path that exists is reported LOUDLY, not as a +# green run. A scan that finds nothing and a scan that is broken print the same +# thing otherwise, and this estate has been bitten by exactly that before. + +usage() { + cat <<'USAGE' +usage: check-manifest-ply.sh [--strict] [--quiet] [PATH ...] + + --strict treat DRIFT-SHALLOW as a failure (default: warn) + --quiet suppress the per-file listing, print the summary only + PATH ... roots to scan (default: .) + +exit 0 no hard failures +exit 1 hard failure (UNPARSEABLE, DRIFT-DEEP, or --strict DRIFT-SHALLOW) +exit 2 usage error, or a PATH that does not exist +USAGE +} + +strict=0 +quiet=0 +roots=() +while [ $# -gt 0 ]; do + case "$1" in + --strict) strict=1 ;; + --quiet) quiet=1 ;; + -h|--help) usage; exit 0 ;; + -*) printf 'unknown option: %s\n' "$1" >&2; usage >&2; exit 2 ;; + *) roots+=("$1") ;; + esac + shift +done +if [ ${#roots[@]} -eq 0 ]; then roots=("."); fi + +# Absolutise the roots ONCE. find then emits absolute paths, so the classifier +# never has to `cd` per file to work out where a manifest really is. +abs_roots=() +for r in "${roots[@]}"; do + if [ ! -d "$r" ]; then + printf 'FATAL: not a directory: %s\n' "$r" >&2 + exit 2 + fi + a=$(cd "$r" && pwd) || exit 2 + abs_roots+=("$a") +done +roots=("${abs_roots[@]}") + +# ── repo-root resolution ──────────────────────────────────────────────────── +# Nearest ancestor containing `.git`. `.git` may be a FILE, not a directory — +# that is the linked-worktree case, and treating it as dir-only silently +# reparents every worktree onto the wrong root. Memoised: the walk is O(depth) +# and the same directories recur thousands of times. +declare -A ROOT_MEMO=() +GITROOT="" +git_root() { + local d=$1 probe seen=() up s + probe=$d + while :; do + if [ -n "${ROOT_MEMO[$probe]+x}" ]; then d=${ROOT_MEMO[$probe]}; break; fi + if [ -e "$probe/.git" ]; then d=$probe; break; fi + seen+=("$probe") + up=${probe%/*} + if [ -z "$up" ]; then up=/; fi + if [ "$up" = "$probe" ]; then d=""; break; fi + probe=$up + done + for s in "${seen[@]}"; do ROOT_MEMO[$s]=$d; done + GITROOT=$d +} + +# depth of $1 below $2, in path components; 0 when equal. +# Pure parameter expansion on purpose: this runs once per manifest, and a +# subprocess per file turns a 15,871-file estate scan into ~32,000 forks. +# Result lands in $DEPTH rather than on stdout, so there is no subshell either. +DEPTH=0 +depth_below() { + local dir=$1 base=$2 rel slashes + if [ "$dir" = "$base" ]; then DEPTH=0; return; fi + rel=${dir#"$base"/} + slashes=${rel//[!\/]/} + DEPTH=$(( ${#slashes} + 1 )) +} + +# ── pass 1: enumerate, parse, record self-rooting directories ─────────────── +# Enumerate from the FILESYSTEM, never from a work list. A completion gate that +# reads the work list is blind to whatever the list omitted. +declare -a M_PATH=() M_DIR=() M_PLY=() M_UNIT=() +declare -A SELFROOT=() +n_legacy=0; n_unparse=0; n_dirs=0 +declare -a UNPARSE=() + +while IFS= read -r -d '' f; do + base=${f##*/}; dir=${f%/*} + ply=""; unit="" + if [[ $base =~ ^0-ply([0-9]+)(-[A-Za-z0-9]+)?-AI-MANIFEST\. ]]; then + ply=${BASH_REMATCH[1]}; unit=${BASH_REMATCH[2]#-} + elif [[ $base =~ ^([0-9]+)\.([0-9]+)(\.([A-Za-z]+))?-AI-MANIFEST\. ]]; then + ply=${BASH_REMATCH[2]}; unit=${BASH_REMATCH[4]} + elif [[ $base =~ ^([0-9]+)(\.([A-Za-z]+))?-AI-MANIFEST\. ]]; then + ply=${BASH_REMATCH[1]}; unit=${BASH_REMATCH[3]} + else + n_unparse=$((n_unparse+1)); UNPARSE+=("$f"); continue + fi + M_PATH+=("$f"); M_DIR+=("$dir"); M_PLY+=("$ply"); M_UNIT+=("$unit") + if [ "$ply" = "0" ]; then SELFROOT[$dir]=1; fi +done < <(find "${roots[@]}" -type f -name '*-AI-MANIFEST.*' -print0 2>/dev/null) + +while IFS= read -r -d '' f; do + n_legacy=$((n_legacy+1)) +done < <(find "${roots[@]}" -type f \( -name 'AI.*' -o -name '!AI.*' \) -print0 2>/dev/null) + +n_dirs=$(find "${roots[@]}" -type d -print 2>/dev/null | wc -l) +n_total=${#M_PATH[@]} + +if [ "$n_total" -eq 0 ] && [ "$n_unparse" -eq 0 ]; then + printf 'WARN: no AI-MANIFEST files found under %s (%s directories scanned).\n' \ + "${roots[*]}" "$n_dirs" + printf ' This is reported, not passed silently: an empty tree and a\n' + printf ' broken scan look identical from the outside.\n' + exit 0 +fi + +# ── pass 2: classify ──────────────────────────────────────────────────────── +n_equal=0; n_unitrel=0; n_shallow=0; n_deep=0; n_noroot=0 +declare -a DEEP=() SHALLOW=() UNITREL=() NOROOT=() + +i=0 +while [ "$i" -lt "$n_total" ]; do + f=${M_PATH[$i]}; abs=${M_DIR[$i]}; ply=${M_PLY[$i]} + git_root "$abs"; gr=$GITROOT + if [ -z "$gr" ]; then + n_noroot=$((n_noroot+1)); NOROOT+=("$f"); i=$((i+1)); continue + fi + depth_below "$abs" "$gr"; gd=$DEPTH + + if [ "$ply" -eq "$gd" ]; then + n_equal=$((n_equal+1)); i=$((i+1)); continue + fi + + # unit frame: nearest self-rooting ancestor at or below the git root + ur=$gr; probe=$abs + while [ -n "$probe" ] && [ "${probe#"$gr"}" != "$probe" ]; do + if [ -n "${SELFROOT[$probe]+x}" ]; then ur=$probe; break; fi + if [ "$probe" = "$gr" ]; then break; fi + probe=${probe%/*} + if [ -z "$probe" ]; then break; fi + done + depth_below "$abs" "$ur"; ud=$DEPTH + + if [ "$ur" != "$gr" ] && [ "$ply" -eq "$ud" ]; then + n_unitrel=$((n_unitrel+1)) + UNITREL+=("declared=$ply git=$gd unit=$ud $f") + elif [ "$ply" -lt "$gd" ]; then + n_shallow=$((n_shallow+1)) + SHALLOW+=("declared=$ply actual=$gd $f") + else + n_deep=$((n_deep+1)) + DEEP+=("declared=$ply actual=$gd $f") + fi + i=$((i+1)) +done + +# ── report ────────────────────────────────────────────────────────────────── +show() { + local label=$1; shift + if [ "$#" -eq 0 ]; then return; fi + if [ "$quiet" -eq 1 ]; then return; fi + printf '\n -- %s --\n' "$label" + printf ' %s\n' "$@" +} + +printf 'AI-MANIFEST ply check — %s manifest(s) under %s\n' "$n_total" "${roots[*]}" +printf ' EQUAL (git-rooted) ....... %6s\n' "$n_equal" +printf ' UNIT-RELATIVE ............ %6s (reported, not failed)\n' "$n_unitrel" +shallow_label=warn +if [ "$strict" -eq 1 ]; then shallow_label='FAIL under --strict'; fi +printf ' DRIFT-SHALLOW ............ %6s (%s)\n' "$n_shallow" "$shallow_label" +printf ' DRIFT-DEEP ............... %6s (FAIL)\n' "$n_deep" +printf ' UNPARSEABLE .............. %6s (FAIL)\n' "$n_unparse" +printf ' LEGACY-NAME .............. %6s (no ply claim to check)\n' "$n_legacy" +if [ "$n_noroot" -gt 0 ]; then + printf ' NO-GIT-ROOT .............. %6s (cannot be checked)\n' "$n_noroot" +fi + +show 'DRIFT-DEEP (declared deeper than it sits)' "${DEEP[@]}" +show 'UNPARSEABLE' "${UNPARSE[@]}" +show 'NO-GIT-ROOT' "${NOROOT[@]}" +if [ "$strict" -eq 1 ]; then show 'DRIFT-SHALLOW' "${SHALLOW[@]}"; fi + +rc=0 +if [ "$n_deep" -gt 0 ] || [ "$n_unparse" -gt 0 ]; then rc=1; fi +if [ "$strict" -eq 1 ] && [ "$n_shallow" -gt 0 ]; then rc=1; fi + +printf '\n' +if [ "$rc" -eq 0 ]; then + printf 'OK: no hard failures.\n' +else + printf 'FAIL: see above.\n' +fi +exit "$rc" From c21b7c71a5eb291f27eb9e72712dce7e227f6d71 Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Fri, 4 Sep 2026 02:06:21 +0100 Subject: [PATCH 3/4] docs(manifest-spec): scope the filename MUST to the root manifest The File Naming section opened with a blanket "Manifests MUST use one of the following filenames" over a two-item list, and then, forty lines later, introduced the depth-addressed prefixes 0.1-, 0.2- and 0.1.A-. None of those appear on the list, so every ply manifest in the estate violated the first normative sentence of the very section that legitimises it. Scope that MUST to the root manifest and state the below-root rule explicitly, so the two halves of the section agree. The cross-reference is deliberately plain prose rather than an AsciiDoc xref: section ids here are auto-generated (_depth_addressing), so <> would not resolve, and an unresolved xref emits a WARN that the documentation check -- which runs at --failure-level=WARN -- would treat as a failure. Co-Authored-By: Claude Fable 5.1 --- 0-ai-gatekeeper-protocol/docs/AI-MANIFEST-SPEC.adoc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/0-ai-gatekeeper-protocol/docs/AI-MANIFEST-SPEC.adoc b/0-ai-gatekeeper-protocol/docs/AI-MANIFEST-SPEC.adoc index 96c7b4235..bd5f0295c 100644 --- a/0-ai-gatekeeper-protocol/docs/AI-MANIFEST-SPEC.adoc +++ b/0-ai-gatekeeper-protocol/docs/AI-MANIFEST-SPEC.adoc @@ -44,11 +44,15 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S == File Naming -Manifests MUST use one of the following filenames (in order of preference): +The *root* manifest MUST use one of the following filenames (in order of preference): 1. `0-AI-MANIFEST.a2ml` (RECOMMENDED) 2. `AI.a2ml` (legacy name, still supported) +A manifest that is not at the repository root MUST instead use the +depth-addressed form of the recommended name, described in the Depth addressing +section below. The base name does not change; only the prefix does. + The leading `0` is not decoration, and its purpose is not merely that it sorts early. It is an attention claim. `0` sorts ahead of `AGENTS`, `AI`, `CLAUDE`, `llms` and `README` in every collation an agent is likely to meet, so the From 7205ab5f1d8a17b8303fcf5a2265a9e80e80d430 Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Fri, 4 Sep 2026 02:19:48 +0100 Subject: [PATCH 4/4] chore(registry): regenerate REGISTRY.a2ml after the manifest-spec edit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `0-ai-gatekeeper-protocol/` is a registered spec home, and this PR edits `0-ai-gatekeeper-protocol/docs/AI-MANIFEST-SPEC.adoc` inside it. The registry records a content-addressed `source_hash` per home, computed from `git ls-files -s `, so any tracked change under a home moves that home's hash by construction. Editing the spec without regenerating therefore reddens "Registry + topology in sync" on every push, which it has done since cc177f28. One line changes: the `0-ai-gatekeeper-protocol` source_hash. No spec is added or removed, and the count stays at 33 — `scripts/check-manifest-ply.sh` is not itself a spec and adds no entry. This also clears "Repo self-tests". Two assertions in `scripts/tests/build-registry-test.sh` ("--check on a clean checkout", "a file outside every spec home unexpectedly drifted the registry") both evaluate the committed tree rather than the working tree, so a stale committed REGISTRY.a2ml fails them for a reason unrelated to what either assertion is testing. Both are one failure wearing two names. Generated by `bash scripts/build-registry.sh`; not hand-edited. Co-Authored-By: Claude Fable 5.1 --- .machine_readable/REGISTRY.a2ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.machine_readable/REGISTRY.a2ml b/.machine_readable/REGISTRY.a2ml index 3f4bbe8e2..ebf83637f 100644 --- a/.machine_readable/REGISTRY.a2ml +++ b/.machine_readable/REGISTRY.a2ml @@ -126,7 +126,7 @@ name = "0-AI Gatekeeper Protocol" stream = "protocol" home = "0-ai-gatekeeper-protocol/" canonical_doc = "0-ai-gatekeeper-protocol/README.adoc" -source_hash = "sha256:aaee3bfd9b1a09274af675d19bc7b242eeb2c0e6c5f39909797d911c56c28d3e" +source_hash = "sha256:369bd762d903006a10f75e924be5c07d082554d88824fbf035ed32f3f67d05c2" route = "the AI-agent entry/gating protocol behind 0-AI-MANIFEST" [[spec]]