# Architect Report — Self-Describing Command Registry for POS **Date:** 2026-08-26 **Status:** DECISION_READY --- ## TL;DR - **Decision:** Add a thin `lib/registry.sh` library that parses `# POS_*:` headers into a queryable API; two new optional headers (`# POS_DEPS:`, `# POS_EXAMPLES:`) extend existing conventions; no framework, no new abstraction layer. - **Key insight:** The existing header system is already 80% of a registry. The missing piece is a shared parsing library so every consumer stops reimplementing `sed` + `grep` header reading. - **Scope:** `lib/registry.sh` (new), `scripts/gen-docs.sh` (extend), `bin/pos-tree` (use registry), `templates/pos-tool.sh` (document new headers), docs. All existing tools keep working unchanged. - **Migration:** Zero-downtime — new headers are optional. Tools add `# POS_DEPS:` and `# POS_EXAMPLES:` incrementally. Registry degrades gracefully when headers are absent. - **Open items:** `pos help` and `pos menu` are future consumers (not in this phase). Dashboard is out of scope. --- ## Decision 1: Header Format — Extend, Don't Replace ### Problem The spec asks for dependencies, curated examples, and a shared query API. Currently, each consumer (`pos tree`, `pos --help`, `gen-docs.sh`, `pos config`) independently implements `sed`/`grep` header parsing with its own logic. ### Decision Add two new optional `# POS_*:` header lines. Keep all existing headers unchanged. ### Exact Syntax ```bash # POS_DEPS: [binary2 ...] # POS_EXAMPLES: | ``` **Rules:** - All new headers are **optional** — tools that don't declare them simply won't expose that metadata. Progressive metadata preserved. - `# POS_DEPS:` is space-separated binary names (what `command -v` checks, not apt package names). - `# POS_EXAMPLES:` can appear on multiple lines — each is ` | ` (pipe-delimited, max one `|`). - Headers must appear between the shebang/strict-mode block (lines 1–6) and the first non-comment line. - Gen-docs and lint only parse headers from the first ~10 lines of each file. **Existing headers (unchanged):** ```bash # POS: # POS_FLAGS: --flag1 --flag2 # POS_SUBCMDS: sub1 sub2 sub3 # POS_CONFIG: | | =: | ... ``` **New headers (optional):** ```bash # POS_DEPS: docker nmap jq # POS_EXAMPLES: pos network scan 192.168.1.0/24 | Scan a /24 CIDR # POS_EXAMPLES: pos network scan 10.0.0.0/28 --full | Full scan with OS detection ``` ### Example: Minimal Tool (No Change Needed) ```bash #!/usr/bin/env bash set -euo pipefail # POS: ssh load-keys — Load all SSH keys into the agent # ... rest of script ``` ### Example: Rich Tool ```bash #!/usr/bin/env bash set -euo pipefail # POS: network download — aria2 RPC daemon + queue control (add/torrent/metalink, watch, limits) # POS_SUBCMDS: start stop status add torrent metalink list info files peers pause resume remove purge move limit set watch restart retry replace menu # POS_FLAGS: --dir --out --split --seed --force --upload --gid --tmux # POS_DEPS: aria2c jq curl # POS_EXAMPLES: pos network download add https://example.com/file.zip | Download a file # POS_EXAMPLES: pos network download status | Show download queue status # POS_EXAMPLES: pos network download --tmux start aria2 daemon with live view ``` ### Rationale | Option | Architecture | Advantages | Costs | Risks | When to choose | |--------|-------------|------------|-------|-------|----------------| | **A: Extend existing headers** | Add `# POS_DEPS:` and `# POS_EXAMPLES:` alongside existing headers | Backward compatible, zero migration cost, progressive metadata, consistent with existing patterns | Two new header formats to parse | Low — optional headers degrade gracefully | **Chosen** — smallest sufficient design | | B: Unified YAML frontmatter | Replace all `# POS_*:` with a YAML block in each file | Richer structure, easier to extend | Breaks all existing consumers, requires migration of 40+ tools, adds YAML dependency | High — YAML parser availability in bash, migration burden | Only if the header system were fundamentally inadequate | | C: Separate registry file | `registry/.yaml` per tool | Clean separation, richer metadata | Duplicates what headers already provide, extra files to maintain, sync risk between header and registry | Medium — source of truth drift | Only if headers couldn't hold the metadata | **Option A wins** because the existing header system already works, is already the source of truth for gen-docs output, and the new metadata (deps, examples) fits naturally into the comment-header format. ### Constraints for Builder - `# POS_DEPS:` line: `sed -n '/^# POS_DEPS: /{s/^# POS_DEPS: //;p;q}' ` — space-separated tokens. - `# POS_EXAMPLES:` lines: `grep '^# POS_EXAMPLES:' Echo sorted tool keys belonging to . Pass "" for category-less tools. # ── Lookup ────────────────────────────────────────────────────── reg_lookup Echo a field's value for a tool. Fields: cat, desc, flags, subcmds, deps, examples Returns empty string if field not set or tool not found. Exit code: 0 if tool found, 1 if not. reg_config_scopes Echo sorted list of unique config scope names. reg_config_keys Echo key|flags|description lines for a scope (newline-delimited). reg_config_envfile Echo the env-file basename for a scope. Exit code: 0 if found, 1 if not. # ── Iteration ─────────────────────────────────────────────────── reg_each Call for each tool, passing: Category is empty for category-less tools. # ── Convenience (for common patterns) ────────────────────────── reg_tool_exists Exit 0 if tool is registered, 1 otherwise. reg_tools_for_category Alias for reg_tools_in. Kept for clarity. ``` ### Source Pattern ```bash # lib/registry.sh — no shebang (library, not executable) # Sourced opt-in by consumers that need tool metadata. # Common.sh helpers (guarded fallback — mirrors lib/config-ui.sh pattern) declare -F log >/dev/null || log() { echo "[+] $*"; } declare -F warn >/dev/null || warn() { echo "[!] $*"; } declare -F err >/dev/null || err() { echo "ERROR: $*" >&2; exit 1; } # ── Tool directory detection ──────────────────────────────────── # Repo: lib/registry.sh → ../bin # Install: /usr/local/bin/registry.sh → /usr/local/bin (same dir) _reg_tools_dir() { local dir dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../bin" 2>/dev/null && pwd)" if [ -d "$dir" ] && ls "$dir"/pos-* &>/dev/null; then echo "$dir" else dirname "${BASH_SOURCE[0]}" fi } # ── Data stores ───────────────────────────────────────────────── declare -a _reg_tools=() declare -A _reg_cat=() declare -A _reg_desc=() declare -A _reg_flags=() declare -A _reg_subcmds=() declare -A _reg_deps=() declare -A _reg_examples=() declare -a _reg_config_scopes=() declare -A _reg_config_keys=() # ── reg_scan ──────────────────────────────────────────────────── reg_scan() { local dir="${1:-$(_reg_tools_dir)}" f local old LC_ALL_PREV="$LC_ALL" export LC_ALL=C _reg_tools=() # Clear all associative arrays for key in "${!_reg_cat[@]:-}"; do unset "_reg_cat[$key]" "_reg_desc[$key]" "_reg_flags[$key]" unset "_reg_subcmds[$key]" "_reg_deps[$key]" "_reg_examples[$key]" done _reg_config_scopes=() for scope in "${!_reg_config_keys[@]:-}"; do unset "_reg_config_keys[$scope]" done local -A scope_seen=() for f in "$dir"/pos-*; do [ -x "$f" ] || continue local name="${f##*/pos-}" local key cat sub if [[ "$name" == *-* ]]; then cat="${name%%-*}" sub="${name#*-}" else cat="" sub="$name" fi key="$sub" _reg_tools+=("$key") _reg_cat["$key"]="$cat" # POS: — description (text after first "— ") local pos_line pos_line="$(sed -n '/^# POS: /{s/^# POS: //;p;q}' "$f" 2>/dev/null)" _reg_desc["$key"]="${pos_line#*— }" # POS_FLAGS: _reg_flags["$key"]="$(sed -n '/^# POS_FLAGS: /{s/^# POS_FLAGS: //;p;q}' "$f" 2>/dev/null)" # POS_SUBCMDS: _reg_subcmds["$key"]="$(sed -n '/^# POS_SUBCMDS: /{s/^# POS_SUBCMDS: //;p;q}' "$f" 2>/dev/null)" # POS_DEPS: _reg_deps["$key"]="$(sed -n '/^# POS_DEPS: /{s/^# POS_DEPS: //;p;q}' "$f" 2>/dev/null)" # POS_EXAMPLES: (may appear multiple times — join with newlines) local examples="" examples="$(sed -n '/^# POS_EXAMPLES: /{s/^# POS_EXAMPLES: //;p}' "$f" 2>/dev/null)" _reg_examples["$key"]="$examples" # POS_CONFIG: (may appear multiple lines per file) local line while IFS= read -r line; do [ -n "$line" ] || continue line="${line#*POS_CONFIG:}" local scope="${line%%|*}" scope="${scope// }" [ -n "$scope" ] || continue _reg_config_keys["$scope"]+="${_reg_config_keys[$scope]:+$'\n'}$line" if [ -z "${scope_seen[$scope]:-}" ]; then scope_seen["$scope"]=1 _reg_config_scopes+=("$scope") fi done < <(grep '^# POS_CONFIG:' "$f" 2>/dev/null || true) done # Sort tools mapfile -t _reg_tools < <(printf '%s\n' "${_reg_tools[@]}" | sort) # Sort config scopes mapfile -t _reg_config_scopes < <(printf '%s\n' "${_reg_config_scopes[@]}" | sort -u) export LC_ALL="$LC_ALL_PREV" } # ── Discovery ─────────────────────────────────────────────────── reg_list() { printf '%s\n' "${_reg_tools[@]}"; } reg_categories() { local -A cats=() local t for t in "${_reg_tools[@]}"; do cats["${_reg_cat[$t]}"]=1 done printf '%s\n' "${!cats[@]}" | sort } reg_tools_in() { local cat="$1" t for t in "${_reg_tools[@]}"; do [ "${_reg_cat[$t]}" = "$cat" ] && echo "$t" done } # ── Lookup ────────────────────────────────────────────────────── reg_lookup() { local tool="$1" field="$2" case "$field" in cat) echo "${_reg_cat[$tool]:-}" ;; desc) echo "${_reg_desc[$tool]:-}" ;; flags) echo "${_reg_flags[$tool]:-}" ;; subcmds) echo "${_reg_subcmds[$tool]:-}" ;; deps) echo "${_reg_deps[$tool]:-}" ;; examples) echo "${_reg_examples[$tool]:-}" ;; *) return 1 ;; esac } reg_config_scopes() { printf '%s\n' "${_reg_config_scopes[@]}"; } reg_config_keys() { local scope="$1" echo "${_reg_config_keys[$scope]:-}" } reg_config_envfile() { local scope="$1" line line="$(echo "${_reg_config_keys[$scope]:-}" | head -1)" [ -n "$line" ] || return 1 line="${line#*|}" # drop scope local env="${line%%|*}" echo "${env// }" } # ── Iteration ─────────────────────────────────────────────────── reg_each() { local cb="$1" t for t in "${_reg_tools[@]}"; do "$cb" "${_reg_cat[$t]}" "$t" "${_reg_desc[$t]}" done } # ── Convenience ───────────────────────────────────────────────── reg_tool_exists() { [ -n "${_reg_desc[$1]+x}" ] } ``` ### Rationale | Option | Architecture | Advantages | Costs | Risks | When to choose | |--------|-------------|------------|-------|-------|----------------| | **A: Regenerate-on-source library** | `reg_scan` parses all files into bash arrays on first call | O(1) lookups after scan, no external deps, works from /usr/local/bin, bash-native | ~180 lines of code, scan cost at startup (~5ms for 40 tools) | Low — scan is fast enough for interactive use | **Chosen** — matches project's bash-only, no-framework philosophy | | B: Cached JSON file | `make gen` produces `registry.json`, consumers parse with `jq` | Fast lookups, rich queries | Requires `jq` at runtime (currently a dep, but adds coupling), extra build step, staleness risk | Medium — JSON dependency for all consumers | Only if performance of header parsing became a bottleneck (it won't for 40 tools) | | C: Per-tool .meta files | Each tool has a sidecar `pos-.meta` | Clean separation, rich format | Duplicates header data, extra files to maintain, sync drift risk | Medium — two sources of truth | Only if headers were fundamentally limited | **Option A wins** because: 1. Headers are already the source of truth — no sync risk. 2. The scan takes ~5ms for 40 tools — no performance concern. 3. Works from `/usr/local/bin/` (all libs live there after install). 4. Matches the project's "no unnecessary framework" philosophy. 5. Follows the pattern established by `lib/config-ui.sh`. ### Source Chain ```bash # In any consumer: source "$(dirname "$0")/../lib/registry.sh" 2>/dev/null || source "$(dirname "$0")/registry.sh" reg_scan # ... use reg_list, reg_lookup, etc. ``` [DECIDED] --- ## Decision 3: Consumer Integration ### Problem Four consumers need to use the registry. Each has different requirements: - `bin/pos-tree` needs tree-building from tool data - `bin/pos` `_pos_category_help()` needs category → tool listing - `scripts/gen-docs.sh` needs all metadata for code generation - `lib/config-ui.sh` needs config scopes and keys ### Decision Migrate consumers to `lib/registry.sh` in this order (highest value first): #### 3.1: `bin/pos-tree` (Priority: HIGH) **Current state** (lines 47–68): Scans `pos-*` files independently, reads `# POS:` and `# POS_SUBCMDS:` via `sed`. **After migration:** ```bash source "$(dirname "$0")/../lib/registry.sh" 2>/dev/null || source "$(dirname "$0")/registry.sh" reg_scan # Build tree from registry instead of scanning files for tool in $(reg_list); do cat="$(reg_lookup "$tool" cat)" desc="$(reg_lookup "$tool" desc)" deps="$(reg_lookup "$tool" deps)" # ... add to tree, annotate with deps if present done ``` **Enhancement:** When `# POS_DEPS:` is present, show it in the tree view: ``` ├── docker ps # Enhanced container overview (health, IPs, ports, uptime) │ [deps: docker] ``` This is the highest-value consumer change — it proves the registry works at runtime and shows the immediate benefit of new metadata. **Implementation note:** Keep the existing `add()` / `render()` tree-building logic. Replace only the data-collection loop (lines 47–68) with registry calls. The tree structure is already correct from filenames + `POS_SUBCMDS`. #### 3.2: `scripts/gen-docs.sh` (Priority: HIGH) **Current state** (lines 30–46): Collects tools into a pipe-delimited array via direct `sed` calls. **After migration:** ```bash # In the tools collection loop, use registry for new fields # Keep the existing collection pattern for backward compatibility # (gen-docs.sh has its own sorting and rendering logic) # Add deps and examples to the tools array format tools+=("$cat|$sub|$desc|$flags|$subcmds|$deps|$examples") ``` **New gen blocks:** - Add `deps` and `examples` columns to `gen_tree()`, `gen_dispatch()`, `gen_filetable()`. - These become visible in `DOC/AGENT_Context_Project.md` once tools add the new headers. **Important:** The existing `gen_*` functions use their own `tools` array (not the registry) because they need specific formatting. The registry provides the raw data; gen-docs formats it. This avoids tight coupling between the generator and the library. **Alternative considered:** Have gen-docs source the registry directly. Rejected because gen-docs needs the data in a specific format (pipe-delimited array) and the registry's data structure is an implementation detail. Keeping the data flow explicit (`registry → gen-docs tools array → gen_* functions`) is cleaner. #### 3.3: `bin/pos` `_pos_category_help()` (Priority: MEDIUM) **Current state** (lines 68–123): Scans `pos--*` files, reads `# POS:` and `# POS_SUBCMDS:` per file. **After migration:** ```bash _pos_category_help() { local cat="$1" source "$(dirname "$0")/../lib/registry.sh" 2>/dev/null || source "$(dirname "$0")/registry.sh" reg_scan echo "pos $cat — $cat tools" echo echo "USAGE" echo " pos $cat [args]" echo echo "COMMANDS" for tool in $(reg_tools_in "$cat"); do local desc deps desc="$(reg_lookup "$tool" desc)" deps="$(reg_lookup "$tool" deps)" printf ' %-28s%s' "$tool" "$desc" [ -n "$deps" ] && printf ' [deps: %s]' "$deps" echo # ... subcommands from reg_lookup "$tool" subcmds done echo echo "Run 'pos $cat --help' for details on a command." exit 0 } ``` **Trade-off:** This adds a `reg_scan` call every time `pos ` runs. For 40 tools, the scan takes ~5ms — negligible for interactive use. If profiling shows this matters, `pos` could cache the scan in a temp file (but this is premature optimization). **Alternative considered:** Keep `_pos_category_help()` using direct `sed` (no registry dependency). Rejected because the whole point is to centralize header parsing. The 5ms scan cost is acceptable. #### 3.4: `lib/config-ui.sh` (Priority: LOW — separate decision) **Current state** (lines 50–203): Has its own header parsing for `# POS_CONFIG:` and `# POS_KEYS:`. The config library is already a mature, working abstraction. **Decision: Do NOT integrate config-ui.sh with the registry in this phase.** **Rationale:** 1. config-ui.sh already works correctly and is well-tested. 2. Its parsing is specialized (multi-line key fields, `*plugins` expansion, `*providers` expansion). 3. Integrating it with the registry would require the registry to handle all config-ui's edge cases, bloating the library. 4. config-ui.sh's `cfg_headers()` / `cfg_scopes()` / `cfg_scope_keys()` API is already the "registry" for config consumers. **Future:** When config-ui.sh needs maintenance, it could source the registry for its initial scan. But this is not needed now. ### Rationale | Option | Architecture | Advantages | Costs | Risks | When to choose | |--------|-------------|------------|-------|-------|----------------| | **A: Incremental migration** | Migrate tree + gen-docs first, then pos, defer config-ui | Lowest risk, proves value early, no breaking changes | Some consumers still use direct sed during transition | Low — transition period is harmless | **Chosen** — smallest risky step | | B: Big-bang migration | Rewrite all consumers at once | Consistent from day one | High risk, many things can break, hard to review | High — one bad refactor breaks everything | Only if the codebase were much smaller | | C: No migration — just add the library | Create registry.sh but don't change any consumers | Library exists for future use | No immediate value, consumers still duplicate logic | Low — but pointless | Only if the task were just "create a library" | [DECIDED] --- ## Decision 4: Migration Strategy — Zero-Downtime Incremental ### Problem 40+ tools exist. All must keep working. The new headers are optional. No tool should require changes to function. ### Decision **Phase 1 (this implementation):** 1. Create `lib/registry.sh` with full API. 2. Update `scripts/gen-docs.sh` to parse new headers (degrades gracefully when absent). 3. Migrate `bin/pos-tree` to use registry (proves runtime value). 4. Update `templates/pos-tool.sh` to document new headers. 5. Update `DOC/DEV.md` and `DOC/AGENT_Context_Project.md` with new header format. **Phase 2 (future, out of scope):** 1. Migrate `bin/pos` `_pos_category_help()` to use registry. 2. Enhance `pos help` to show registry metadata (deps, examples). 3. Add `# POS_DEPS:` and `# POS_EXAMPLES:` to tools incrementally (start with 3–5 representative tools per category). **Phase 3 (future, out of scope):** 1. `pos menu` — interactive menu from registry (new tool). 2. `pos dashboard` — status dashboard from registry (new tool). ### Incremental Adoption for Tool Authors 1. Add `# POS_DEPS: docker jq` to your tool's header block. Done — deps appear in registry. 2. Add `# POS_EXAMPLES: pos | Description` lines. Done — examples appear in registry. 3. No other changes required. The tool keeps working exactly as before. 4. When you run `make gen`, the new metadata appears in generated docs. ### Tool Template Update `templates/pos-tool.sh` gets new header documentation: ```bash # ──────────────────────────────────────────────────────────────── # TEMPLATE — new `pos` CLI tool # # 1. Copy: cp templates/pos-tool.sh bin/pos-- # 2. Header: add a `# POS:` line right after the shebang/strict-mode # lines (single source of truth for generated docs): # # POS: — one-line description # # POS_FLAGS: --flag1 --flag2 (flag-style tools only) # # POS_SUBCMDS: sub1 sub2 (multi-command tools only) # # POS_DEPS: binary1 binary2 (runtime deps, optional) # # POS_EXAMPLES: pos | Description (optional) # 3. ... ``` ### Lint Gate Update `scripts/lint-conventions.sh` gets a new WARN check: - If `# POS_DEPS:` is present, validate that each token looks like a binary name (no spaces, no special chars). This is a soft check — WARN on malformed deps, not FAIL. [DECIDED] --- ## Decision 5: Scope Boundaries ### Approved Scope ```text In scope: lib/registry.sh — NEW FILE, ~180 lines scripts/gen-docs.sh — Extend tools array, add deps/examples to gen_* functions bin/pos-tree — Migrate to use registry (replace file-scanning loop) templates/pos-tool.sh — Document new headers in template comments DOC/DEV.md — Document new header format and registry usage DOC/AGENT_Context_Project.md — Update line count table for lib/registry.sh, gen blocks updated by make gen scripts/lint-conventions.sh — Optional: WARN for malformed POS_DEPS Not in scope: bin/pos _pos_category_help() — Phase 2 (future) pos help enhancements — Phase 2 (future) pos menu (new tool) — Phase 3 (future) pos dashboard (new tool) — Phase 3 (future) Adding POS_DEPS/POS_EXAMPLES to existing tools — Individual tool authors, incremental lib/config-ui.sh integration — Deferred (already works, specialized parsing) completions/pos.bash changes — No new completion data needed (deps/examples aren't completable) install.sh changes — registry.sh is installed with existing lib/* loop ``` ### What Builder Must NOT Do 1. Do NOT add `# POS_DEPS:` or `# POS_EXAMPLES:` to any existing tool in this PR (that's incremental migration, separate commits). 2. Do NOT change the `# POS:` header format or the em-dash convention. 3. Do NOT change `bin/pos` dispatch logic or `INTERACTIVE_CMDS`. 4. Do NOT add any new files beyond `lib/registry.sh`. 5. Do NOT modify the completion script (`completions/pos.bash`). 6. Do NOT refactor `lib/config-ui.sh` to use the registry. 7. Do NOT add a shebang to `lib/registry.sh` (it's a library, not executable). 8. Do NOT break `make gen && make check && make lint`. [DECIDED] --- ## Decision 6: Risk Analysis ### Risk 1: `make gen` output changes break `git diff --exit-code` in CI **Impact:** HIGH — blocks PRs. **Cause:** Adding deps/examples parsing to gen-docs.sh changes the `tools` array format. The gen_* functions that consume this array will produce different output if any tool has the new headers. But since no tools have them yet, the output should be identical. **Mitigation:** - The gen_* functions must produce **identical output** when no tools have `# POS_DEPS:` or `# POS_EXAMPLES:` headers. - Test: run `make gen && git diff --exit-code` before committing. Zero diff = safe. - The tools array format change (adding `$deps|$examples` fields) is internal to gen-docs.sh — the gen_* functions that render output must not use the new fields when they're empty. ### Risk 2: `reg_scan` performance degrades with many tools **Impact:** LOW — current tool count is ~40. **Cause:** Each tool file is read 4–6 times by `sed` during scan. For 40 tools, this is ~200 process spawns. **Mitigation:** - At current scale, scan completes in ~5ms. Even 100 tools would be ~15ms. - If it ever becomes an issue, `reg_scan` could read each file once and parse all headers in a single `awk` pass. This is a future optimization, not needed now. ### Risk 3: Registry library conflicts with existing sourcing patterns **Impact:** MEDIUM — could break tools that source both common.sh and registry.sh. **Cause:** `registry.sh` declares guarded fallbacks for `log`, `warn`, `err` (same pattern as `config-ui.sh`). If both are sourced, the second source is a no-op because the functions already exist. **Mitigation:** - Use the same guarded-declaration pattern as `config-ui.sh`: `declare -F log >/dev/null || log() { ... }`. - `registry.sh` does NOT define `run`, `spawn`, `confirm`, or any other common.sh functions. - `registry.sh` does NOT call `err` during normal operation — only if `reg_scan` is called with an invalid directory (which won't happen in practice). ### Risk 4: `bin/pos-tree` migration breaks tree output **Impact:** HIGH — visible to users. **Cause:** The tree-building logic in `pos-tree` is tightly coupled to the current data collection. Replacing the collection loop might subtly change tree structure. **Mitigation:** - Keep the existing `add()`, `render()` functions unchanged. - Replace ONLY the data-collection loop (lines 47–68) with registry calls. - Test: run `bin/pos-tree` before and after, diff the output. Must be identical (for existing headers). - The only visible change should be when a tool has `# POS_DEPS:` — deps appear in the tree. ### Risk 5: `pos ` performance regression **Impact:** LOW — adds ~5ms per invocation. **Cause:** `_pos_category_help()` would source and call `reg_scan` on every invocation. **Mitigation:** - Phase 2 only (not in this implementation). - If needed, cache scan results in a temp file: `reg_scan` writes to `/tmp/.pos-registry-` and consumers check for freshness. This is premature — implement only if profiling shows a problem. ### Risk 6: New headers malformed, breaking parsing **Impact:** LOW — malformed headers produce empty values, not crashes. **Cause:** A tool author writes `# POS_DEPS` (missing colon) or `# POS_EXAMPLES foo bar` (missing pipe). **Mitigation:** - `reg_scan` uses strict pattern matching: `sed -n '/^# POS_DEPS: /{...}'`. Missing colon = no match = empty value. Graceful degradation. - Add a lint WARN (not FAIL) for `# POS_DEPS:` lines without space-separated tokens. - Add lint WARN for `# POS_EXAMPLES:` lines without `|` delimiter. - Document the expected format clearly in DEV.md. [DECIDED] --- ## Verification Plan ### Gate 1: Syntax ```bash bash -n lib/registry.sh # Must pass (no syntax errors) bash -n scripts/gen-docs.sh # Must pass (after modifications) bash -n bin/pos-tree # Must pass (after modifications) ``` ### Gate 2: Gen Drift ```bash make gen && git diff --exit-code # Zero diff (no tools have new headers yet) ``` ### Gate 3: Self-Consistency ```bash make check # Must pass (syntax + exec bits + doc/code sync + dispatch smoke) ``` ### Gate 4: Convention Lint ```bash make lint # Must pass (0 FAIL, 0 WARN) ``` ### Gate 5: Functional ```bash # Registry works standalone bash -c 'source lib/registry.sh; reg_scan; reg_list; reg_lookup docker ps desc' # pos tree produces identical output bin/pos-tree > /tmp/tree-before.txt # ... apply changes ... bin/pos-tree > /tmp/tree-after.txt diff /tmp/tree-before.txt /tmp/tree-after.txt # Must be empty # pos category help works bin/pos docker --help # Must show docker tools bin/pos network --help # Must show network tools ``` ### Gate 6: Regression ```bash # All existing commands still dispatch bin/pos --help bin/pos docker --help bin/pos network --help bin/pos help network scan ``` --- ## Implementation Guidance for Builder ### Step-by-step 1. **Create `lib/registry.sh`** (~180 lines). Start from the API spec in Decision 2. Use `lib/config-ui.sh` as a structural reference for the guarded fallbacks and source pattern. 2. **Update `scripts/gen-docs.sh`**. In the tools collection loop (lines 30–46): - Add `deps` and `examples` fields to the `tools` array format: `"$cat|$sub|$desc|$flags|$subcmds|$deps|$examples"` - Parse new headers with the same `sed` pattern as existing ones. - In `gen_tree()`, `gen_dispatch()`, `gen_filetable()`: add deps/examples columns ONLY when non-empty. Empty fields = identical output to current. 3. **Migrate `bin/pos-tree`**. Replace lines 47–68 (the file-scanning loop) with registry calls. Keep `add()`, `render()`, and the rest unchanged. Test that output is identical for existing tools. 4. **Update `templates/pos-tool.sh`**. Add `# POS_DEPS:` and `# POS_EXAMPLES:` to the header documentation block. Add them after the existing `# POS_FLAGS:` example. 5. **Update `DOC/DEV.md`**. In "Adding a New CLI Tool → Make it discoverable": - Document the new `# POS_DEPS:` and `# POS_EXAMPLES:` headers. - Explain when to use each (deps: list runtime binaries; examples: show 1–3 representative usages). 6. **Update `lib/registry.sh` line count** in `DOC/AGENT_Context_Project.md` filetable (the hand-maintained rows above the GEN marker). 7. **Run gates:** `make gen && make check && make lint`. Verify 0 FAIL, 0 WARN. ### Critical Constraints - `lib/registry.sh` must NOT have a shebang (library, not executable). - `lib/registry.sh` must be added to the `lib_names` list in `install.sh` Phase 2 (line 143). - `reg_scan` must set `LC_ALL=C` for deterministic sort. - gen-docs.sh changes must produce zero diff when no tools have new headers. - `pos-tree` output must be byte-identical before/after migration (for existing headers). --- ## Open Questions (for Orchestrator) 1. Should `pos --help` (in `bin/pos`) be migrated in this phase or deferred to Phase 2? **Recommendation: defer to Phase 2** — lower risk, and the category help is already working. 2. Should the lint gate enforce that `# POS_DEPS:` tokens are valid binary names? **Recommendation: WARN only, not FAIL** — some deps might be shell builtins or paths, not just binary names. 3. Should `reg_scan` support a `--cached` mode? **Recommendation: no, not yet** — premature optimization for 40 tools. --- *Report written by Architect agent. Next recommended agent: **Builder** (to implement the approved scope).*