-
-
Notifications
You must be signed in to change notification settings - Fork 0
feat(gates): document manifest depth addressing; add the free prefix-vs-depth check #731
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
cc177f2
006dabf
c21b7c7
7205ab5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -44,15 +44,85 @@ 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 - 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. | ||
| 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 | ||
| 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<N>[-<unit>]-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. | ||
|
Comment on lines
+117
to
+125
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win The specification and the checker disagree on the status of
📍 Affects 2 files
🤖 Prompt for AI Agents |
||
|
|
||
| == File Format | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -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 `-<unit>` | ||||||||||||||||||||||||||||||||||||||
| # 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.<ext>` / `!AI.<ext>` — 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() { | ||||||||||||||||||||||||||||||||||||||
|
Check warning on line 75 in scripts/check-manifest-ply.sh
|
||||||||||||||||||||||||||||||||||||||
| 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 | ||||||||||||||||||||||||||||||||||||||
|
Check failure on line 92 in scripts/check-manifest-ply.sh
|
||||||||||||||||||||||||||||||||||||||
| 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 | ||||||||||||||||||||||||||||||||||||||
|
Check failure on line 102 in scripts/check-manifest-ply.sh
|
||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| # 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 | ||||||||||||||||||||||||||||||||||||||
|
Check failure on line 108 in scripts/check-manifest-ply.sh
|
||||||||||||||||||||||||||||||||||||||
| 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() { | ||||||||||||||||||||||||||||||||||||||
|
Check warning on line 124 in scripts/check-manifest-ply.sh
|
||||||||||||||||||||||||||||||||||||||
| local d=$1 probe seen=() up s | ||||||||||||||||||||||||||||||||||||||
| probe=$d | ||||||||||||||||||||||||||||||||||||||
| while :; do | ||||||||||||||||||||||||||||||||||||||
| if [ -n "${ROOT_MEMO[$probe]+x}" ]; then d=${ROOT_MEMO[$probe]}; break; fi | ||||||||||||||||||||||||||||||||||||||
|
Check failure on line 128 in scripts/check-manifest-ply.sh
|
||||||||||||||||||||||||||||||||||||||
| if [ -e "$probe/.git" ]; then d=$probe; break; fi | ||||||||||||||||||||||||||||||||||||||
|
Check failure on line 129 in scripts/check-manifest-ply.sh
|
||||||||||||||||||||||||||||||||||||||
| seen+=("$probe") | ||||||||||||||||||||||||||||||||||||||
| up=${probe%/*} | ||||||||||||||||||||||||||||||||||||||
| if [ -z "$up" ]; then up=/; fi | ||||||||||||||||||||||||||||||||||||||
|
Check failure on line 132 in scripts/check-manifest-ply.sh
|
||||||||||||||||||||||||||||||||||||||
| if [ "$up" = "$probe" ]; then d=""; break; fi | ||||||||||||||||||||||||||||||||||||||
|
Check failure on line 133 in scripts/check-manifest-ply.sh
|
||||||||||||||||||||||||||||||||||||||
| probe=$up | ||||||||||||||||||||||||||||||||||||||
| done | ||||||||||||||||||||||||||||||||||||||
| for s in "${seen[@]}"; do ROOT_MEMO[$s]=$d; done | ||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: #!/bin/bash
# Description: Find declared Bash version requirements and other empty-array expansions.
set -uo pipefail
# Locate any documented Bash version floor.
rg -n -i --glob '!**/.git/**' 'bash[^\n]{0,40}(4\.[0-9]|version|require)' -g '*.adoc' -g '*.md' -g '*.sh' -g '*.yml' -g '*.yaml' | head -50
# Check how sibling scripts handle the same pattern.
fd -e sh . scripts --exec rg -n 'set -[a-z]*u|\$\{[A-Za-z_]+\[@\]\}' {}Repository: hyperpolymath/standards Length of output: 161 🏁 Script executed: #!/bin/bash
set -u
printf '%s\n' '--- scripts/check-manifest-ply.sh ---'
sed -n '1,18p;110,145p;240,275p' scripts/check-manifest-ply.sh
printf '%s\n' '--- specification references ---'
rg -n -C 4 '0-ply|Bash|bash|version|require' 0-ai-gatekeeper-protocol/docs/AI-MANIFEST-SPEC.adoc scripts/check-manifest-ply.sh 2>/dev/null || true
printf '%s\n' '--- relevant script declarations and uses ---'
rg -n '^(set|declare|local)|\$\{(seen|DEEP|UNPARSE|NOROOT|SHALLOW)\[@\]\}|seen\[|DEEP\[|UNPARSE\[|NOROOT\[|SHALLOW\[' scripts/check-manifest-ply.shRepository: hyperpolymath/standards Length of output: 16311 🌐 Web query:
💡 Result: In Bash versions prior to 4.4, including Bash 4.3, expanding an array that had no assigned elements using the "${arr[@]}" or "${arr[*]}" syntax while the nounset (set -u) option was enabled would trigger an "unbound variable" error [1][2]. This occurred because the shell treated an empty array (one with no elements) as an unset variable in the context of mass expansion [1][3]. Bash 4.4 introduced a change that corrected this behavior [2]. Specifically, when nounset is enabled, expanding an array that contains no assigned elements no longer throws an unbound variable error [2]. For scripts intended to run on older versions of Bash where this behavior is still present, a common workaround is to use the parameter expansion default value syntax, such as "${arr[@]:-}", which ensures the expansion yields an empty string instead of triggering an error if the array is empty or unset [1][2]. Citations:
🏁 Script executed: #!/bin/bash
set -u
printf '%s\n' '--- show implementation and array population ---'
rg -n -C 8 '^(show|git_root|while IFS|DEEP|SHALLOW|UNPARSE|NOROOT|strict|n_total|exit)' scripts/check-manifest-ply.sh
printf '%s\n' '--- numbered specification section ---'
sed -n '72,125p' 0-ai-gatekeeper-protocol/docs/AI-MANIFEST-SPEC.adocRepository: hyperpolymath/standards Length of output: 7411 Guard empty arrays under 📍 Affects 1 file
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
| 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 | ||||||||||||||||||||||||||||||||||||||
|
Check failure on line 147 in scripts/check-manifest-ply.sh
|
||||||||||||||||||||||||||||||||||||||
| 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 | ||||||||||||||||||||||||||||||||||||||
|
Check failure on line 174 in scripts/check-manifest-ply.sh
|
||||||||||||||||||||||||||||||||||||||
| done < <(find "${roots[@]}" -type f -name '*-AI-MANIFEST.*' -print0 2>/dev/null) | ||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Capture the 🐛 Proposed fix-done < <(find "${roots[@]}" -type f -name '*-AI-MANIFEST.*' -print0 2>/dev/null)
+done < <(find "${roots[@]}" -type f -name '*-AI-MANIFEST.*' -print0)Apply the same change at lines 179 and 181, and test the status of the Also applies to: 179-179, 181-181 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| 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 | ||||||||||||||||||||||||||||||||||||||
|
Check failure on line 184 in scripts/check-manifest-ply.sh
|
||||||||||||||||||||||||||||||||||||||
| 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 | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+184
to
+190
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win The empty-scan path exits green and hides the legacy count. The header at lines 71-73 states that finding zero manifests is not a green run. This branch prints a warning and then runs This branch also returns before the summary block at lines 241-249. If a tree contains only legacy Send the warning to stderr, and print the legacy count before the exit. If the intent is a non-green result, use a distinct exit status and document it in 🐛 Proposed fix 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'
+ "${roots[*]}" "$n_dirs" >&2
+ printf ' This is reported, not passed silently: an empty tree and a\n' >&2
+ printf ' broken scan look identical from the outside.\n' >&2
+ printf ' LEGACY-NAME .............. %6s (no ply claim to check)\n' "$n_legacy" >&2
exit 0
fi🧰 Tools🪛 GitHub Check: SonarCloud Code Analysis[failure] 184-184: Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich. [failure] 184-184: Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich. 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| # ── 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 | ||||||||||||||||||||||||||||||||||||||
|
Check failure on line 197 in scripts/check-manifest-ply.sh
|
||||||||||||||||||||||||||||||||||||||
| f=${M_PATH[$i]}; abs=${M_DIR[$i]}; ply=${M_PLY[$i]} | ||||||||||||||||||||||||||||||||||||||
| git_root "$abs"; gr=$GITROOT | ||||||||||||||||||||||||||||||||||||||
| if [ -z "$gr" ]; then | ||||||||||||||||||||||||||||||||||||||
|
Check failure on line 200 in scripts/check-manifest-ply.sh
|
||||||||||||||||||||||||||||||||||||||
| n_noroot=$((n_noroot+1)); NOROOT+=("$f"); i=$((i+1)); continue | ||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||
| depth_below "$abs" "$gr"; gd=$DEPTH | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| if [ "$ply" -eq "$gd" ]; then | ||||||||||||||||||||||||||||||||||||||
|
Check failure on line 205 in scripts/check-manifest-ply.sh
|
||||||||||||||||||||||||||||||||||||||
| 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 | ||||||||||||||||||||||||||||||||||||||
|
Check failure on line 211 in scripts/check-manifest-ply.sh
|
||||||||||||||||||||||||||||||||||||||
| if [ -n "${SELFROOT[$probe]+x}" ]; then ur=$probe; break; fi | ||||||||||||||||||||||||||||||||||||||
|
Check failure on line 212 in scripts/check-manifest-ply.sh
|
||||||||||||||||||||||||||||||||||||||
| if [ "$probe" = "$gr" ]; then break; fi | ||||||||||||||||||||||||||||||||||||||
|
Check failure on line 213 in scripts/check-manifest-ply.sh
|
||||||||||||||||||||||||||||||||||||||
| probe=${probe%/*} | ||||||||||||||||||||||||||||||||||||||
| if [ -z "$probe" ]; then break; fi | ||||||||||||||||||||||||||||||||||||||
|
Check failure on line 215 in scripts/check-manifest-ply.sh
|
||||||||||||||||||||||||||||||||||||||
| done | ||||||||||||||||||||||||||||||||||||||
| depth_below "$abs" "$ur"; ud=$DEPTH | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+210
to
+217
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win The unit-frame walk starts at the manifest's own directory, so every non-root Line 210 sets The result is circular. A manifest declares itself a unit root, and that declaration alone makes it consistent with the unit frame. A stray The header at lines 28-30 and the specification at lines 104-107 both define the unit frame for the descendants of a self-rooting unit. Start the walk at the parent directory. This also affects the measured distribution quoted at lines 21-26. 🐛 Proposed fix # unit frame: nearest self-rooting ancestor at or below the git root
- ur=$gr; probe=$abs
+ ur=$gr; probe=${abs%/*}
+ if [ -z "$probe" ]; then probe=/; fi
while [ -n "$probe" ] && [ "${probe#"$gr"}" != "$probe" ]; do📝 Committable suggestion
Suggested change
🧰 Tools🪛 GitHub Check: SonarCloud Code Analysis[failure] 213-213: Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich. [failure] 211-211: Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich. [failure] 212-212: Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich. [failure] 215-215: Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich. [failure] 211-211: Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich. 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| if [ "$ur" != "$gr" ] && [ "$ply" -eq "$ud" ]; then | ||||||||||||||||||||||||||||||||||||||
|
Check failure on line 219 in scripts/check-manifest-ply.sh
|
||||||||||||||||||||||||||||||||||||||
| n_unitrel=$((n_unitrel+1)) | ||||||||||||||||||||||||||||||||||||||
| UNITREL+=("declared=$ply git=$gd unit=$ud $f") | ||||||||||||||||||||||||||||||||||||||
| elif [ "$ply" -lt "$gd" ]; then | ||||||||||||||||||||||||||||||||||||||
|
Check failure on line 222 in scripts/check-manifest-ply.sh
|
||||||||||||||||||||||||||||||||||||||
| 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 | ||||||||||||||||||||||||||||||||||||||
|
Check failure on line 235 in scripts/check-manifest-ply.sh
|
||||||||||||||||||||||||||||||||||||||
| if [ "$quiet" -eq 1 ]; then return; fi | ||||||||||||||||||||||||||||||||||||||
|
Check failure on line 236 in scripts/check-manifest-ply.sh
|
||||||||||||||||||||||||||||||||||||||
| 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 | ||||||||||||||||||||||||||||||||||||||
|
Check failure on line 245 in scripts/check-manifest-ply.sh
|
||||||||||||||||||||||||||||||||||||||
| 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 | ||||||||||||||||||||||||||||||||||||||
|
Check failure on line 250 in scripts/check-manifest-ply.sh
|
||||||||||||||||||||||||||||||||||||||
| 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 | ||||||||||||||||||||||||||||||||||||||
|
Check failure on line 257 in scripts/check-manifest-ply.sh
|
||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| rc=0 | ||||||||||||||||||||||||||||||||||||||
| if [ "$n_deep" -gt 0 ] || [ "$n_unparse" -gt 0 ]; then rc=1; fi | ||||||||||||||||||||||||||||||||||||||
|
Check failure on line 260 in scripts/check-manifest-ply.sh
|
||||||||||||||||||||||||||||||||||||||
| if [ "$strict" -eq 1 ] && [ "$n_shallow" -gt 0 ]; then rc=1; fi | ||||||||||||||||||||||||||||||||||||||
|
Check failure on line 261 in scripts/check-manifest-ply.sh
|
||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| printf '\n' | ||||||||||||||||||||||||||||||||||||||
| if [ "$rc" -eq 0 ]; then | ||||||||||||||||||||||||||||||||||||||
|
Check failure on line 264 in scripts/check-manifest-ply.sh
|
||||||||||||||||||||||||||||||||||||||
| printf 'OK: no hard failures.\n' | ||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||
| printf 'FAIL: see above.\n' | ||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||
| exit "$rc" | ||||||||||||||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.