Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,32 @@ loop-engineer runtime — and the repo dogfoods both on its own contract in CI.
when absent); the `action-dogfood` CI job installs it and runs the fixture for
real on the PR checkout.

**PR5 — Adopt in your stack.** The four on-ramps shipped above (uvx funnel,
`loop.emit` writer, Stop-hook firewall, CI Action + pre-commit hook) get a single
"Adopt in your stack" README section that funnels a reader from zero-install
`uvx loop-engineer inspect .` through to full adoption, with a refreshed Install
section. Every claim the section makes is gate-backed by a new test, so the docs
can never advertise an on-ramp that does not exist.

### Added
- **README "Adopt in your stack" section + Install refresh** — a runtime-neutral
adoption path leading with `uvx loop-engineer inspect .` (zero install), then the
`loop.emit` writer for foreign runtimes, the Stop-hook firewall, and the CI
Action / pre-commit gate pinned at `SollanSystems/loop-engineer@v0.7.0` (a
forward-looking pin re-verified at the next release).
- **show-hn launch draft leads with the uvx funnel** — the M5-LAUNCH Show HN draft
now opens its command sequence with `uvx loop-engineer inspect .`. The draft
lives under the gitignored `roadmap/` tree (a launch working file, absent in a
fresh checkout), so its gate is env-guarded.
- **Docs-claims gate test** (`scripts/test_docs_adoption.py`) — asserts every
"Adopt in your stack" README claim is backed by a shipped, wired artifact: the
uvx funnel by the `loop-engineer` console script, the `loop.emit` claim by the
real writer functions + the LangGraph guide, the Stop-hook claim by the
registered hook file, and the CI Action claim by `action.yml` + the
`loop-doctor` pre-commit hook. The show-hn assertion skips when the gitignored
draft is absent (fresh CI checkouts), matching the repo's env-guarded-skip
pattern.

## 0.6.1 — 2026-07-04

**PyPI substrate.** `loop-engineer` becomes a self-contained wheel that runs from
Expand Down
51 changes: 36 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,25 +244,16 @@ See `reference/repo-os-contract.md` for the canonical artifact schemas.

### Portable validator / inspector

No Claude Code plugin is required to validate or inspect a loop contract. From
the cloned repo root:
No Claude Code plugin is required to validate or inspect a loop contract:

```bash
python3 -m loop doctor /path/to/workspace
python3 -m loop inspect /path/to/workspace
uvx loop-engineer inspect . # zero-install score of any repo's loop contract
pip install loop-engineer # or install the CLI: `loop doctor`, `loop inspect`, ...
```

To run it against a loop in any other directory, install the core once
(editable):

```bash
pip install -e . # optional faster manifest parsing: pip install -e ".[yaml]"
python3 -m loop doctor /path/to/workspace
```

`python3 -m loop` resolves the bundled `loop/` package from the repo root;
`pip install -e .` puts it on your path so the CLI works from any directory. The
core is pure-stdlib — PyYAML is an optional extra, not a requirement.
From a clone, `python3 -m loop <cmd>` and `pip install -e .` keep working; the
wheel is self-contained (schemas, templates, and the inspect/metrics tooling
ship inside it). The core is pure-stdlib — PyYAML/jsonschema are optional extras.

The portable core lives in `loop/` and validates schema-bearing artifacts in
`schemas/`:
Expand All @@ -289,6 +280,36 @@ never required; every skill runs on the bundled core alone.

---

## Adopt in your stack

Three thin, enforcing on-ramps — each one makes a false "done" fail somewhere
that already exists in your workflow. Start where your loop lives:

**Claude Code** — the plugin ships a Stop-hook firewall: if the session's repo
holds a `.loop/` contract that claims `Succeeded` while `loop doctor` says
otherwise, the stop is blocked with the exact doctor issues. No-op without
`.loop/`, fail-open on error, zero config beyond installing the plugin.

**Any Python runtime** — `loop.emit` is a pure-stdlib writer for foreign
orchestrators (LangGraph, or anything that can call four functions):
`open_contract`, `append_iteration`, `append_receipt`, `terminate`. The writer
refuses an evidence-free `Succeeded` at write time. Recipe:
[docs/integrations/langgraph.md](docs/integrations/langgraph.md).

**CI** — one workflow step validates the contract and publishes a scorecard:

```yaml
- uses: SollanSystems/loop-engineer@v0.7.0
with:
path: "."
```

`doctor` failure fails the job; the inspect verdict is warn-only unless you set
`fail-under-score`. Pre-commit users: hook id `loop-doctor`. This repo runs the
same action against its own contract — the gate gates its maker.

---

## Claude Code reference workflow

The Claude Code plugin is the reference UI over the portable loop contract.
Expand Down
77 changes: 77 additions & 0 deletions scripts/test_docs_adoption.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# scripts/test_docs_adoption.py
"""PR5 gate: every 'Adopt in your stack' README claim is backed by a shipped,
wired artifact — the docs cannot advertise an on-ramp that does not exist."""

from __future__ import annotations

import json
from pathlib import Path

import pytest

REPO_ROOT = Path(__file__).resolve().parent.parent
README = (REPO_ROOT / "README.md").read_text(encoding="utf-8")


def test_uvx_funnel_claim_is_backed_by_console_script():
if "uvx loop-engineer" not in README:
return
pyproject = (REPO_ROOT / "pyproject.toml").read_text(encoding="utf-8")
assert 'loop-engineer = "loop.__main__:main"' in pyproject, (
"README sells `uvx loop-engineer ...` but the package declares no "
"loop-engineer console script (uvx runs the executable named after the package)"
)


def test_emit_claim_names_only_real_functions():
if "loop.emit" not in README:
return
import loop.emit as emit

for name in ("open_contract", "append_iteration", "append_receipt", "terminate"):
assert name not in README or callable(getattr(emit, name, None)), (
f"README names loop.emit.{name} but it does not exist"
)
assert (REPO_ROOT / "docs" / "integrations" / "langgraph.md").is_file()


def test_stop_firewall_claim_is_backed_by_registered_hook():
if "Stop-hook" not in README and "stop firewall" not in README.lower():
return
manifest = json.loads((REPO_ROOT / ".claude-plugin" / "plugin.json").read_text(encoding="utf-8"))
commands = [
h["command"]
for entry in manifest.get("hooks", {}).get("Stop", [])
for h in entry.get("hooks", [])
]
hook_files = [c.split("${CLAUDE_PLUGIN_ROOT}/")[-1].split()[0] for c in commands]
assert any((REPO_ROOT / f).is_file() for f in hook_files), (
"README sells the Stop-hook firewall but plugin.json registers no existing Stop hook file"
)


def test_ci_action_claim_is_backed_by_action_and_precommit():
if "uses: SollanSystems/loop-engineer@" not in README:
return
assert (REPO_ROOT / "action.yml").is_file()
if "loop-doctor" in README:
assert "id: loop-doctor" in (REPO_ROOT / ".pre-commit-hooks.yaml").read_text(encoding="utf-8")


def test_show_hn_leads_with_the_uvx_funnel():
# roadmap/ is gitignored — show-hn.md exists locally but is absent in a fresh
# CI checkout, so guard on existence the way the wheel/recipe tests env-guard.
show_hn_path = (
REPO_ROOT / "roadmap" / "launch" / ".loop" / "artifacts" / "M5-LAUNCH" / "show-hn.md"
)
if not show_hn_path.is_file():
pytest.skip("show-hn.md is a gitignored launch draft — absent in fresh checkouts")
show_hn = show_hn_path.read_text(encoding="utf-8")
first_cmd = next(
line.strip()
for line in show_hn.splitlines()
if line.strip().startswith(("uvx ", "$ uvx", "pip ", "$ pip", "python", "loop ", "git clone"))
)
assert first_cmd.lstrip("$ ").startswith("uvx loop-engineer inspect"), (
f"show-hn's first command is {first_cmd!r}, spec requires `uvx loop-engineer inspect .`"
)
Loading