Files
Claude 61caad16cb feat: Phases 3-5 — generation v2, fleet ops, api-spec IR, MCP serve fleet-wide
Phase 3 (Generation v2):
- scaffold-cli.py migrated to Jinja2 (${} delimiters preserve existing
  templates; StrictUndefined; TEMPLATE_VERSION 2.0.0; .manifest.json
  provenance emission)
- auth.py.tpl unified (google-sso via {% if %} block; auth_google_sso.py.tpl
  deleted); anchor-based method injection replaced by template conditionals
- New scaffolds: command_group.py.tpl (--resource flag), test_e2e.py.tpl
  (canonical subprocess fixtures), README.md.tpl + SKILL.md.tpl (replace
  dead Handlebars .template files, now actually rendered)
- 21-profile golden test matrix (protocol x http_client x auth) — compile-
  clean, no placeholder leaks, profile-correct deps/files
- Skills consolidation: skills/shared/CONVENTIONS.md (single definition of
  every implementation rule) + RECOVERY.md (per-gate failure decision
  trees); boilerplate skill 829 -> 106 lines; HARNESS.md now navigational;
  reviewer agents de-overlapped; gap-analyzer mandatory first step of
  /refine; quality checklist tiered (38 Tier-1 critical checks) with
  --tier1-only/--strict in validate-checklist.py

Phase 4 (Fleet ops):
- cli-web-devkit canary: registry-declared read-only live-site smoke
  commands (12 no-auth CLIs) + daily canary.yml workflow that auto-files
  site-breakage issues
- contract.yml workflow: fleet contract suite + vendored-file drift on PR
- release-please switched to manifest/monorepo mode (root + cli-web-core +
  devkit packages, per-package changelogs); publish.yml with PyPI trusted
  publishing (setup guide in docs/PUBLISHING.md)
- cli-web-devkit docs: README fleet table + install block generated from
  registry.json between markers, --check freshness gate in CI (caught the
  install list missing linkedin)

Phase 5 (Product leaps):
- MCP SERVE FLEET-WIDE: cli_web_core/mcp_server.py adapter (vendored to all
  19 CLIs, synced by resync) exposes every Click command as a typed MCP
  tool over stdio; mcp-serve wired into all 19 entry points;
  contract-tested handshake x19
- api-spec.json IR: evidence-required endpoint schema + cli-web-devkit
  spec validate; wired into methodology Phase 2 (structural enforcement of
  never-invent-endpoints)
- cli-web-devkit gaps: deterministic captured-vs-implemented-vs-exposed
  diff (AST-based), wired into gap-analyzer skill Step 0
- Capture quality scoring (0-100) in validate-capture report

Fleet contract fixes found by the new gates:
- gh-trending emitted bare JSON arrays instead of the success envelope
  (fixed + e2e updated; stale version assertion fixed)
- notebooklm/stitch/codewiki/chatgpt lacked --version (fixed in Phase 2
  commit); booking registry commands corrected

Verified: 514 plugin/devkit/core tests, 95 fleet contract tests (incl. 19
MCP handshakes), 19/19 CLI unit suites, gh-trending e2e live, ruff+format
clean (512 files), mypy strict clean, verify-plugin 28/28, drift 39/39 ok,
registry + README freshness gates green.

https://claude.ai/code/session_017wCNXuLy6UpGJ1jcPdsD5i
2026-06-09 22:54:20 +00:00

108 lines
4.2 KiB
Python

import json
from cli_web_devkit.paths import repo_root
from cli_web_devkit.registry import Registry
from cli_web_devkit.sync import MANIFEST_NAME, SHARED_FILES, drift, load_manifest, resync
ROOT = repo_root()
def _make_fleet(tmp_path, vendored_content: bytes | None, with_override: bool = False):
"""Build a minimal repo: canon files + one registered CLI."""
pkg = tmp_path / "demo/agent-harness/cli_web/demo"
(pkg / "utils").mkdir(parents=True)
for canon_rel, vendored_rel in SHARED_FILES.items():
canon = tmp_path / canon_rel
canon.parent.mkdir(parents=True, exist_ok=True)
canon.write_bytes(b"CANONICAL CONTENT v2\n")
# All vendored copies except repl_skin start in sync; repl_skin
# content is controlled by the test.
if vendored_rel != "utils/repl_skin.py":
target = pkg / vendored_rel
target.parent.mkdir(parents=True, exist_ok=True)
target.write_bytes(b"CANONICAL CONTENT v2\n")
if vendored_content is not None:
(pkg / "utils/repl_skin.py").write_bytes(vendored_content)
(tmp_path / "registry.json").write_text(
json.dumps(
{
"version": "1.0.0",
"clis": [
{
"name": "cli-web-demo",
"website": "demo.example",
"protocol": "REST",
"auth": "none",
"directory": "demo/agent-harness",
"namespace": "cli_web.demo",
"commands": ["x"],
"install": "pip install -e demo/agent-harness",
}
],
}
)
)
if with_override:
(tmp_path / "demo/agent-harness" / MANIFEST_NAME).write_text(
json.dumps(
{
"manifest_version": 1,
"cli": "cli-web-demo",
"shared_files": {},
"overrides": [{"file": "utils/repl_skin.py", "reason": "custom prompt"}],
}
)
)
return tmp_path
def test_drift_detects_divergence(tmp_path):
_make_fleet(tmp_path, b"OLD STALE COPY\n")
statuses = {(i.cli, i.file): i.status for i in drift(tmp_path) if i.cli == "cli-web-demo"}
assert statuses[("cli-web-demo", "utils/repl_skin.py")] == "drifted"
def test_drift_detects_missing(tmp_path):
_make_fleet(tmp_path, None)
statuses = [i.status for i in drift(tmp_path) if i.cli == "cli-web-demo"]
assert "missing" in statuses
def test_drift_respects_overrides(tmp_path):
_make_fleet(tmp_path, b"INTENTIONALLY DIFFERENT\n", with_override=True)
items = {i.file: i for i in drift(tmp_path) if i.cli == "cli-web-demo"}
assert items["utils/repl_skin.py"].status == "override"
assert "custom prompt" in items["utils/repl_skin.py"].detail
def test_resync_fixes_drift_and_writes_manifest(tmp_path):
root = _make_fleet(tmp_path, b"OLD STALE COPY\n")
changed = resync(root)
assert "demo/agent-harness/cli_web/demo/utils/repl_skin.py" in changed
vendored = root / "demo/agent-harness/cli_web/demo/utils/repl_skin.py"
assert vendored.read_bytes() == b"CANONICAL CONTENT v2\n"
reg = Registry.load(root / "registry.json")
manifest = load_manifest(root, reg.entry("cli-web-demo"))
rec = manifest["shared_files"]["utils/repl_skin.py"]
assert rec["source"] == next(iter(SHARED_FILES))
assert len(rec["sha256"]) == 64
# After resync, drift is clean for the CLI
assert all(i.status == "ok" for i in drift(root) if i.cli == "cli-web-demo")
def test_resync_skips_overridden_files(tmp_path):
root = _make_fleet(tmp_path, b"INTENTIONALLY DIFFERENT\n", with_override=True)
resync(root)
vendored = root / "demo/agent-harness/cli_web/demo/utils/repl_skin.py"
assert vendored.read_bytes() == b"INTENTIONALLY DIFFERENT\n"
def test_real_fleet_has_no_unexplained_drift():
"""The actual repo must be in sync (run `cli-web-devkit resync` if this fails)."""
bad = [i for i in drift(ROOT) if i.status in ("drifted", "missing")]
assert not bad, f"fleet drift detected: {[(i.cli, i.file, i.status) for i in bad]}"