# Cross-Code Review Report: Installation Integrity for `lib_py/agents` Assets

**Job ID**: 16bdc99c
**Reviewer**: cline (session: herdr:canary-projects-multi-agent-mux-creator-cline)
**Review Target**: `deploy/INSTALL.md` and `deploy/install.sh` (plus `install_mam.sh`, `remove.sh`, `update.sh`, `lib_ownership.sh`, `gitea-ci.yml`) — verifying latest assets including `lib_py/agents/` are reflected when installing to other projects
**Base Commit**: clean working tree (`git diff HEAD` = no changes since base)
**Date**: 2026-08-13

---

## 1. Executive Summary

This review verifies that the MAM installation toolchain correctly distributes, tracks, and removes the newly introduced `lib_py/agents/` package (BaseAgentAdapter, adapter registry, CLI bridge) when installing MAM into target projects. The review covers both installation paths (`install.sh` remote and `install_mam.sh` local-clone), the uninstaller (`remove.sh`), the updater (`update.sh`), the ownership rules (`lib_ownership.sh`), CI lint coverage (`gitea-ci.yml`), and the user-facing installation guide (`INSTALL.md`).

**Verdict: PASS** — All four deploy scripts correctly handle the `lib_py/agents/` directory tree. The `is_framework_owned()` glob (`.agents/skills/*`) classifies all 9 `lib_py/agents/**/*.py` files as framework-owned, ensuring they are copied, hash-tracked, manifest-registered, and cleanly removed. CI's recursive glob (`lib_py/**/*.py`) and `flake8 lib_py/` correctly cover nested files. INSTALL.md prerequisites and command examples are consistent with the documented `install_mam.sh` installer. Two Low-severity findings are non-blocking.

---

## 2. Scope & Methodology

### 2.1 Files Reviewed

| File | Role | Lines |
|---|---|---|
| `deploy/install.sh` | Rev.2 remote installer (curl/git/tar -> staging -> 3-way hash reconciliation) | 621 |
| `deploy/install_mam.sh` | Local-clone installer (rsync-based) | 336 |
| `deploy/remove.sh` | Uninstaller (manifest-driven + fallback) | 320 |
| `deploy/update.sh` | Updater (remove -> fetch latest install.sh) | 247 |
| `deploy/lib_ownership.sh` | Single source of truth for framework-owned / registry file classification | 29 |
| `deploy/INSTALL.md` | User-facing installation & quick-start guide | 134 |
| `deploy/README.md` | Deployment & Gitea integration reference | 83 |
| `deploy/gitea-ci.yml` | CI pipeline (shellcheck, flake8, py_compile, pytest) | ~110 |

### 2.2 Verification Methods

1. **Static syntax checks**: `bash -n` on all 5 shell scripts; `py_compile` on all 9 `lib_py/agents/` Python files + `paths.py` + `state.py`.
2. **Ownership classification test**: Sourced `lib_ownership.sh` and ran `is_framework_owned()` / `is_registry_file()` against every `lib_py/agents/**/*.py` path.
3. **Find-loop simulation**: Python walk replicating install.sh's `find . -type f` + skip patterns to confirm all 9 agent files are captured.
4. **Test execution**: 3 adapter contract tests + 11 deploy registry-merge tests + 14 deploy freshness/layout tests = **28 tests, all PASS**.
5. **Cross-document consistency**: Grepped INSTALL.md vs README.md for installer references; verified CLI flags (`--target`, `--force`) match actual script argument parsers.
6. **CI coverage analysis**: Inspected `gitea-ci.yml` flake8 and py_compile glob patterns for recursive coverage of nested `lib_py/agents/` files.

---

## 3. Installation Path Analysis

### 3.1 `deploy/install.sh` (Rev.2 Remote Installer)

**Asset fetch** (lines 117-153): Three fetch methods -- local `cp -R`, `git clone --depth 1`, or `curl | tar -xz`. All populate a staging directory with the full repo tree.

**Copy loop** (lines 187-354): The core mechanism uses `find . -type f` (recursive) over the staged `.agents/` directory, skipping only `reports/`, `references/`, `*.tmp`, `*.log`, `*.pyc`, `__pycache__/`. It does NOT exclude `lib_py/agents/`. Each file is classified by `is_framework_owned()` (matching `.agents/skills/*`), which classifies all `lib_py/agents/**/*.py` files as framework-owned (verified by direct test). `is_registry_file()` returns NO for agent files (only `.agents/hooks.json` is registry), so they get wholesale copy/update via 3-way hash reconciliation, not key-merge.

**Hash DB** (lines 409-434): `FRAMEWORK_LEDGER` records every framework-owned file pair. SHA256 hashes computed for all, including `lib_py/agents/` files.

**Manifest** (lines 155-157, 258-260): Every copied framework file appended to `.mam/install_manifest.txt`.

**Sanity gate** (lines 98-115, 463): `check_assets_present()` checks 7 representative core files. Does NOT include `lib_py/agents/` -- but this is a fast representative-sample guard. The copy loop's `find` captures everything. See F-2.

### 3.2 `deploy/install_mam.sh` (Local-Clone Installer)

**rsync copy** (line 126): `rsync -a` with excludes for `.git/`, `/reports/`, `/references/`, `*.log`, `*.tmp`, `__pycache__/`, `*.pyc`. Recursive copy preserves directory structure -> copies entire `lib_py/agents/` tree. Excludes do NOT target `lib_py/` or `agents/`.

**Manifest** (line 163): `find .agents -type f -print` records all files including `lib_py/agents/**/*.py`.

**Hash DB** (lines 172-206): Iterates manifest, classifies via `is_framework_owned()`, computes SHA256 for all framework files.

### 3.3 `deploy/remove.sh` (Uninstaller)

**Manifest mode** (lines 75-81): Reads `.mam/install_manifest.txt` and deletes each listed file individually -> covers all `lib_py/agents/**/*.py` entries.

**Fallback mode** (lines 83-108): `fallback_assets` array includes `".agents/skills/lib_py"` as a directory entry. `delete_asset` uses `rm -rf` on directory entries -> removes entire `lib_py/` tree including `agents/` subdirectory.

### 3.4 `deploy/update.sh` (Updater)

**Flow** (lines 182-199): Runs `remove.sh --force` (manifest-driven cleanup) then fetches and pipes latest `install.sh` from remote via `curl | bash`. Target inherits full install.sh coverage from section 3.1.

### 3.5 CI Coverage (`deploy/gitea-ci.yml`)

- **flake8** (lines 72, 74): `flake8 .agents/skills/lib_py/` recursively traverses Python packages, covering `lib_py/agents/` and `lib_py/agents/adapters/`.
- **py_compile** (line 79): `glob.glob('.agents/skills/lib_py/**/*.py', recursive=True)` -- recursive glob correctly captures nested files. (This was the CI glob fix from the A-4 BaseAgentAdapter introduction job.)

---

## 4. INSTALL.md Consistency Check

### 4.1 Prerequisites (Section 1)

| Prerequisite in INSTALL.md | Verified Against | Status |
|---|---|---|
| `herdr` | `install_mam.sh:94` DEPS array | PASS |
| `python3` | `install_mam.sh:94` DEPS array | PASS |
| `uuidgen` | `install_mam.sh:94` DEPS array | PASS |
| `rsync` | `install_mam.sh:94` DEPS + `:126` usage | PASS |
| `pyyaml` | `install_mam.sh:113` `import yaml, sqlite3` | PASS |
| `sqlite3` (built-in) | `install_mam.sh:113` `import yaml, sqlite3` | PASS |

### 4.2 Command Examples (Section 2)

| INSTALL.md Example | Actual Script Flag | Status |
|---|---|---|
| `bash deploy/install_mam.sh --target /path/...` | `install_mam.sh:39` `-t|--target` | PASS |
| `bash deploy/install_mam.sh --target ... --force` | `install_mam.sh:43` `-f|--force` | PASS |

### 4.3 Documented Operations

1. "Dependency diagnosis" -- `install_mam.sh:92-117` checks DEPS + Python modules. PASS
2. "Rules & skills replication" -- `install_mam.sh:120-127` rsync `.agents/` recursively. PASS
3. "Guidelines propagation" -- `install_mam.sh:220-234` copies/injects `AGENTS.md`. PASS
4. "Gitignore exclusion" -- `install_mam.sh:236-291` injects `.gitignore` managed block. PASS

### 4.4 Quick Start Workflow (Section 3)

All 6 workflow examples (Create, Attach, Resume, Stop/Purge, Mux-Loop, Orc-Onboard) reference correct script paths under `.agents/skills/multi-agent-mux-*/scripts/`. Verified against actual file tree.

---

## 5. Findings

### F-1 (Low / Info) -- INSTALL.md documents only `install_mam.sh`, not `install.sh`

**Location**: `deploy/INSTALL.md` (entire document)
**Observation**: INSTALL.md references `install_mam.sh` 4 times but `install.sh` 0 times. The repo has two installers: `install_mam.sh` (local-clone, documented in INSTALL.md) and `install.sh` (Rev.2 remote curl, documented in `deploy/README.md`, used by `update.sh`).
**Impact**: A user reading only INSTALL.md learns about the local-clone path but not the remote one-liner. However, `deploy/README.md` documents the remote installer, and INSTALL.md is placed under `.agents/` in target projects where the local-clone workflow is relevant.
**Assessment**: Documentation structure choice (README = developer reference, INSTALL.md = user manual), not a bug. **Non-blocking.**

### F-2 (Low / Info) -- `check_assets_present()` does not include a `lib_py/agents/` file

**Location**: `deploy/install.sh:98-115`
**Observation**: The sanity-check function verifies 7 representative core files. It does not include any `lib_py/agents/` file.
**Impact**: None in practice. The function is a fast pre/post guard -- the actual copy loop uses `find . -type f` which captures all files recursively. All three fetch methods (git clone, tar extract, cp -R) copy the entire tree, making a partial-drop scenario implausible.
**Recommendation**: Adding `.agents/skills/lib_py/agents/__init__.py` to `core_files` would provide defense-in-depth. Optional, non-blocking.

---

## 6. Test Results

| Test Suite | Tests | Result |
|---|---|---|
| `tests/test_a4_adapter_contract.py` | 3 | All PASS |
| `tests/test_deploy_registry_merge.py` | 11 | All PASS |
| `tests/test_deploy_freshness.py` | 11 | All PASS |
| `tests/test_deploy_layout.py` | 3 | All PASS |
| **Total** | **28** | **All PASS** |

### Static Analysis

| Check | Result |
|---|---|
| `bash -n` all 5 shell scripts | PASS |
| `py_compile` all 9 `lib_py/agents/**/*.py` + `paths.py` + `state.py` | PASS |
| `is_framework_owned()` on 5 `lib_py/agents/` paths | All FRAMEWORK |
| `is_registry_file()` on `lib_py/agents/registry.py` | Correctly NO |
| Find-loop simulation (recursive walk + skip patterns) | 9/9 agent files captured |
| CI flake8 + py_compile glob coverage | Recursive `lib_py/**/*.py` covers nested |

---

## 7. Conclusion

The installation toolchain provides **complete, end-to-end coverage** for the `lib_py/agents/` package across all lifecycle operations:

1. **Install** (both paths): Recursive `find`/`rsync` + `.agents/skills/*` ownership glob -> all 9 agent files copied, hash-tracked, and manifest-registered.
2. **Update**: remove.sh (manifest-driven) + install.sh (fresh fetch) -> full replacement with preserved user configs.
3. **Remove**: Manifest entries + fallback `.agents/skills/lib_py` directory -> clean uninstall with no orphaned agent files.
4. **CI**: Recursive flake8 + py_compile glob -> nested agent files are lint-checked on every push.
5. **INSTALL.md**: Prerequisites, command flags, and documented operations are consistent with the `install_mam.sh` installer.

The two findings (F-1, F-2) are both Low/Info severity and non-blocking. No design-level rework or escalation is needed.

---

[VERDICT: PASS]
