diff --git a/.github/workflows/loop-ready.yml b/.github/workflows/loop-ready.yml new file mode 100644 index 0000000..4ab3fd3 --- /dev/null +++ b/.github/workflows/loop-ready.yml @@ -0,0 +1,46 @@ +name: loop-ready + +# The README's terminal demo is rendered from a REAL audit run, never hand-edited. +# This workflow is the live half of that promise: every push re-runs the audit +# (the badge in the README goes red if any kit fails) and re-renders +# assets/terminal-loop-ready.svg from the actual results, committing it only +# when the score actually changed. + +on: + push: + branches: [main] + pull_request: + +permissions: + contents: write + +jobs: + audit-and-render: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Run the Loop Ready audit + run: npm run audit:loops + + - name: Re-render the README terminal demo from the audit output + # Runs even when the audit fails, so the image shows the red run too. + if: always() + run: npm run render:terminal + + - name: Commit the refreshed image (main only, when it changed) + if: always() && github.event_name == 'push' && github.ref == 'refs/heads/main' + run: | + if git diff --quiet assets/terminal-loop-ready.svg; then + echo "Terminal demo unchanged — nothing to commit." + exit 0 + fi + git config user.name "loop-ready-bot" + git config user.email "actions@github.com" + git add assets/terminal-loop-ready.svg + git commit -m "chore(readme): re-render terminal demo from live audit" + git push diff --git a/README.md b/README.md index 40655e8..7ab8470 100644 --- a/README.md +++ b/README.md @@ -1,108 +1,279 @@ +
+ +[![GitHub stars](https://img.shields.io/github/stars/ayeshakhalid192007-dev/LoopEngineering-CrashCourse?style=for-the-badge&logo=github&color=7c3aed&labelColor=1a0f3c)](https://github.com/ayeshakhalid192007-dev/LoopEngineering-CrashCourse/stargazers) +[![Last commit](https://img.shields.io/github/last-commit/ayeshakhalid192007-dev/LoopEngineering-CrashCourse?style=for-the-badge&logo=git&logoColor=white&color=7c3aed&labelColor=1a0f3c)](https://github.com/ayeshakhalid192007-dev/LoopEngineering-CrashCourse/commits) +[![Repo size](https://img.shields.io/github/repo-size/ayeshakhalid192007-dev/LoopEngineering-CrashCourse?style=for-the-badge&color=7c3aed&labelColor=1a0f3c)](https://github.com/ayeshakhalid192007-dev/LoopEngineering-CrashCourse) +[![Contributors](https://img.shields.io/github/contributors/ayeshakhalid192007-dev/LoopEngineering-CrashCourse?style=for-the-badge&color=7c3aed&labelColor=1a0f3c)](https://github.com/ayeshakhalid192007-dev/LoopEngineering-CrashCourse/graphs/contributors) +[![Loop Ready audit](https://img.shields.io/github/actions/workflow/status/ayeshakhalid192007-dev/LoopEngineering-CrashCourse/loop-ready.yml?style=for-the-badge&label=Loop%20Ready%20audit&logo=githubactions&logoColor=white&labelColor=1a0f3c)](https://github.com/ayeshakhalid192007-dev/LoopEngineering-CrashCourse/actions/workflows/loop-ready.yml) + +[![License: MIT](https://img.shields.io/badge/License-MIT-fbbf24?style=for-the-badge&labelColor=1a0f3c)](LICENSE) +[![Course](https://img.shields.io/badge/Course-14_steps_·_4_tracks-7c3aed?style=for-the-badge&labelColor=1a0f3c)](docs/00-start-here/README.md) +[![Loop kits](https://img.shields.io/badge/Loop_kits-20_ready_to_install-a855f7?style=for-the-badge&labelColor=1a0f3c)](starters/getting-started.md) +[![Node](https://img.shields.io/badge/Node-%E2%89%A518-339933?style=for-the-badge&logo=node.js&logoColor=white&labelColor=1a0f3c)](starters/getting-started.md) + +[![Certification](https://img.shields.io/badge/Certification-Loop_Ready-34d399?style=for-the-badge&labelColor=1a0f3c)](docs/assessments/loop-ready-certification.md) +[![100% free](https://img.shields.io/badge/100%25-Free_%26_Open_Source-38bdf8?style=for-the-badge&labelColor=1a0f3c)](LICENSE) +[![Built with loops](https://img.shields.io/badge/Built_with-its_own_loops-8A2BE2?style=for-the-badge&labelColor=1a0f3c)](LOOP.md) +[![PRs welcome](https://img.shields.io/badge/PRs-Welcome-brightgreen?style=for-the-badge&labelColor=1a0f3c)](CONTRIBUTING.md) + +
-![Loop Engineering — design the system that prompts the agent, not the prompt](assets/hero-loop-cycle.svg) +
+ +![Loop Engineering Crash Course — design the system that prompts the agent, not the prompt](assets/hero-banner.svg) # Loop Engineering Crash Course -**Learn to build AI agent loops — autonomous systems with a heartbeat, a spine, a -checker, and a stop you can prove.** +*Stop prompting one message at a time. Design the loop.* + +**Learn to build AI agent loops — autonomous systems with a heartbeat, a body, a +checker, a spine, and a stop you can prove.** -You will not read *about* loops here; you will build them. By the last page you will -have designed, shipped, and safely operated real loops in two different tools — opening -with a single in-session loop and closing with a certified multi-loop fleet. It is free, -open source, and readable end to end on GitHub. +
-[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) -[![Course](https://img.shields.io/badge/course-14%20steps%20%C2%B7%204%20tracks-blue.svg)](docs/00-start-here/README.md) -[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md) -[![Built with loops](https://img.shields.io/badge/built%20with-its%20own%20loops-8A2BE2.svg)](LOOP.md) +The leverage has moved: it no longer lives in the perfect prompt, but in the **control +system that keeps an agent working toward a goal over time**. This free, open-source +course teaches that discipline end to end — 14 steps, 20 ready-to-run loop kits, graded +labs, an operating handbook, and a certification. You don't read *about* loops here; +you build them, in two tools, from a first in-session loop to a certified fleet. + +
+ +![Terminal demo — npm run audit:loops runs ten real checks across all 20 kits while the Loop Ready score climbs to 100](assets/terminal-loop-ready.svg) + +*Real command, real score — this image is +[rendered from an actual run](scripts/render-loop-ready-terminal.mjs) of the +deterministic [Loop Ready audit](scripts/loop-ready-audit.mjs), and +[CI re-renders it](.github/workflows/loop-ready.yml) on every push. If a kit ever +fails, this terminal goes red.* + +
+ +Want a loop running in your own project before the first lesson? One command: + +```bash +# see the 20 ready-made loop kits +npx @loop-engineering/loop-kit list -Loop Engineering is the fast-emerging craft of **designing the system that prompts your -AI agents** instead of prompting them one message at a time. The leverage has moved: it -no longer lives in the perfect prompt, but in the control system that keeps an agent -working toward a goal over time. This repository packages that whole discipline — the -concepts, the mechanics, a library of ready-to-run loops, hands-on labs, and the safety -guidance that keeps you in charge — as a complete course you can read start to finish on -GitHub. The same `docs/` content powers the course website too, so the two surfaces can -never drift apart (one source of truth, always). +# install one into the project you want the loop to watch +npx @loop-engineering/loop-kit ci-sweeper +``` > **We eat our own cooking.** This is a course about loops that is *built by loops*. The > rulebook lives in [`LOOP.md`](LOOP.md), the durable spine in [`STATE.md`](STATE.md), > and every single run is logged, one line at a time, in > [`shared/loop-run-log.md`](shared/loop-run-log.md). -## Start here +## 📌 Quick links -**New? Spend 60 seconds on the router before anything else:** → -[`docs/00-start-here/README.md`](docs/00-start-here/README.md) +| I want to… | | +| --- | --- | +| **Enroll** — find my starting point in 60 seconds | [**View →**](docs/00-start-here/README.md) | +| See the full learning-track map with entry checks | [**View →**](docs/00-start-here/learning-tracks.md) | +| Set up my agent (Claude Code, OpenCode, Codex, Grok) | [**View →**](docs/01-prerequisites/environment-setup.md) | +| Look up a term in the glossary | [**View →**](docs/02-foundations/glossary.md) | +| Install a ready-made loop in my own project | [**View →**](starters/getting-started.md) | +| Browse the 20 loop patterns | [**View →**](#-loop-pattern-library) | +| Operate loops safely (failure modes, recovery) | [**View →**](docs/10-operating/operating-loops.md) | +| Get certified | [**View →**](docs/assessments/loop-ready-certification.md) | -Answer four honest questions and it drops you onto exactly one of four tracks — so a -first-timer never drowns and a veteran never yawns: +![·](assets/section-divider.svg) + +## 🎓 Choose your track + +**New here?** [**Open the 60-second router →**](docs/00-start-here/README.md) + +Like any good LMS, this course meets you where you are. Answer four honest questions and +the router drops you onto exactly one of four tracks — so a first-timer never drowns and +a veteran never yawns: | Track | You are… | You'll learn to… | -| ------- | ---------- | ------------------ | +| --- | --- | --- | | **T1 · Foundations** | using an AI agent by hand (or not yet) | explain the shift and run your first in-session loop | | **T2 · Practitioner** | fluent in the six parts of a loop | choose a heartbeat, write a provable stop, split maker/checker | | **T3 · Engineer** | able to assemble a loop | build the full six-part loop in two tools and operate it safely | | **T4 · Ultra-Pro** | shipping loops already | hill-climbing loops, fleets, governance at team scale | -Full map with entry checks and exit assessments: -[`docs/00-start-here/learning-tracks.md`](docs/00-start-here/learning-tracks.md) +[**View the full track map with entry checks and exit assessments →**](docs/00-start-here/learning-tracks.md) + +![·](assets/section-divider.svg) + +## 📚 Curriculum + +**Prerequisites & foundations (read first):** +[Environment Setup](docs/01-prerequisites/environment-setup.md) · +[Agentic-Coding Primer](docs/01-prerequisites/agentic-coding-primer.md) · +[Spec-Driven Primer](docs/01-prerequisites/spec-driven-primer.md) · +[Mental Models](docs/02-foundations/mental-models.md) · +[Concepts](docs/02-foundations/concepts.md) · +[The Four Layers](docs/02-foundations/the-four-layers.md) · +[Primitives](docs/02-foundations/primitives.md) · +[Primitives Matrix](docs/02-foundations/primitives-matrix.md) · +[Glossary](docs/02-foundations/glossary.md) + +**The 14-step core roadmap** — six parts, each ending in something you built: + +| Part | Steps | What you learn | | +| --- | --- | --- | --- | +| **1 · The Shift** | 01–03 | prompting → looping, the four layers, anatomy of a loop | [**Start →**](docs/03-part-1-the-shift/) | +| **2 · The Heartbeat** | 04–07 | in-session, run-until-done, schedules, event-driven | [**Start →**](docs/04-part-2-heartbeat/) | +| **3 · The Body** | 08–11 | worktrees, skills, connectors/MCP, maker ≠ checker | [**Start →**](docs/05-part-3-the-body/) | +| **4 · The Spine** | 12 | durable state between runs | [**Start →**](docs/06-part-4-the-spine/) | +| **5 · A Complete Loop** | 13 | build the same loop twice (Claude Code & OpenCode) | [**Start →**](docs/07-part-5-complete-loop/) | +| **6 · Human Control** | 14 | staying the engineer: cost, verification, nested loops | [**Start →**](docs/08-part-6-human-control/) | + +**Then keep climbing:** + +| Stage | What it holds | | +| --- | --- | --- | +| **Methods** | design your own loop: [Decision Framework](docs/09-methods/decision-framework.md), [Design Checklist](docs/09-methods/loop-design-checklist.md), [Pattern Picker](docs/09-methods/pattern-picker.md), [Worked Example](docs/09-methods/worked-example-dependency-sweeper.md) | [**View →**](docs/09-methods/make-your-own-loop.md) | +| **Operating handbook** | safety, failure modes, infinite loops, observability, recovery | [**View →**](docs/10-operating/operating-loops.md) | +| **Graded labs** | 11 hands-on projects, from a watch loop to a two-routine gate — with [solutions](docs/projects/solutions/) | [**View →**](docs/projects/) | +| **Advanced** | hill-climbing, multi-loop coordination, evals & traces, governance, enterprise scale | [**View →**](docs/advanced/) | +| **Certification** | the **Loop Ready** capstone: [Final Exam](docs/assessments/final-exam.md) · [Capstone Rubric](docs/assessments/capstone-rubric.md) | [**View →**](docs/assessments/loop-ready-certification.md) | + +Every lesson shows the same build in at least two tools, side by side +(Claude Code ↔ OpenCode), so you learn the discipline — not one vendor's syntax. + +![·](assets/section-divider.svg) -## Quickstart +## 🔁 Anatomy of a loop -1. **Set up your agent** — [`docs/01-prerequisites/environment-setup.md`](docs/01-prerequisites/environment-setup.md) +Six parts, one discipline. A **heartbeat** decides when the agent runs, a **body** does +the work, a **checker** grades it (the maker never grades its own work), a **spine** +remembers across runs, a **provable stop** ends it, and **you** stay the engineer: + +```mermaid +flowchart LR + H(["⏱ Heartbeat
when it runs"]) --> B["🦾 Body
does the work"] + B --> C{"✅ Checker
grades it"} + C -- "not done" --> S[("🧠 Spine
remembers")] + S --> H + C -- "success · run limit · no progress" --> X(["🛑 Provable stop"]) + X --> U["🧑‍💻 You — the engineer"] +``` + +Start with [Mental Models](docs/02-foundations/mental-models.md) and +[The Four Layers](docs/02-foundations/the-four-layers.md); keep the +[Glossary](docs/02-foundations/glossary.md) open in a tab. + +![·](assets/section-divider.svg) + +## 🧰 Loop pattern library + +Twenty production-shaped loop kits, each with a definition, a spine, a budget, a +constitution, and a read-only checker. Install any of them into your own project with +one command — no clone required: + +```bash +npx @loop-engineering/loop-kit +``` + +| Pattern | What it does | +| --- | --- | +| [`ci-sweeper`](patterns/ci-sweeper.md) | fires the instant CI goes red on `main` and sweeps it back to green | +| [`pr-babysitter`](patterns/pr-babysitter.md) | keeps open PRs from rotting — conflicts, red CI, silent reviewers | +| [`daily-triage`](patterns/daily-triage.md) | clears the overnight noise before you open your laptop | +| [`issue-triage`](patterns/issue-triage.md) | `daily-triage`'s narrower cousin, stripped down to issues alone | +| [`dependency-sweeper`](patterns/dependency-sweeper.md) | keeps dependencies current without updating them blind | +| [`dependency-cve-burndown`](patterns/dependency-cve-burndown.md) | reads the 3 a.m. CVE advisory so it never waits until Monday | +| [`docs-sweep`](patterns/docs-sweep.md) | catches documentation the moment the code moves on without it | +| [`changelog-drafter`](patterns/changelog-drafter.md) | drafts the changelog your busy sprint was going to forget | +| [`test-coverage-loop`](patterns/test-coverage-loop.md) | burns down the "we should really test that" backlog | +| [`test-stabilizer-loop`](patterns/test-stabilizer-loop.md) | hunts flaky tests until a red run means something again | +| [`prod-error-sweep`](patterns/prod-error-sweep.md) | sweeps the production errors that actually cost users something | +| [`page-load-loop`](patterns/page-load-loop.md) | keeps pushing page-load performance downhill, beat after beat | +| [`repo-cleanup-loop`](patterns/repo-cleanup-loop.md) | clears repository clutter — branches nobody deleted, and worse | +| [`post-merge-cleanup`](patterns/post-merge-cleanup.md) | finishes the story every merge leaves behind | +| [`ticket-to-pr-ready`](patterns/ticket-to-pr-ready.md) | one ticket, one run: reproduce, fix, arrive PR-ready | +| [`spec-dev-review`](patterns/spec-dev-review.md) | one story → a scoped spec packet: findings, progress, review | +| [`clodex-adversarial-review`](patterns/clodex-adversarial-review.md) | a real maker–checker pair: Claude implements, Codex reviews | +| [`codex-completion-contract`](patterns/codex-completion-contract.md) | stops "mostly done" from quietly passing as "done" | +| [`loop-harness-verification`](patterns/loop-harness-verification.md) | a general-purpose harness for any scheduled repo task | +| [`stale-safe-batch-release`](patterns/stale-safe-batch-release.md) | the bookend: batches what every other kit left safely releasable | + +Prefer to design your own from a blank? `npx @loop-engineering/loop-kit new ` +scaffolds one from [the blank template](starters/_template/README.md). +[**View the full install guide →**](starters/getting-started.md) + +![·](assets/section-divider.svg) + +## 🚀 Getting started (5 minutes) + +1. **Enroll** — run the [60-second router](docs/00-start-here/README.md). It hands you + a track. +2. **Set up your agent** — follow the + [Environment Setup guide](docs/01-prerequisites/environment-setup.md) (Claude Code, OpenCode, Codex, or Grok; every lesson shows at least Claude Code ↔ OpenCode). -2. **Speed-run the primers** — [`agentic-coding-primer.md`](docs/01-prerequisites/agentic-coding-primer.md) - and [`spec-driven-primer.md`](docs/01-prerequisites/spec-driven-primer.md). -3. **Ground yourself in the foundations** — start with - [`mental-models.md`](docs/02-foundations/mental-models.md), keep the - [`glossary.md`](docs/02-foundations/glossary.md) open in a tab. -4. **Begin the 14 steps** (Part 1 opens Day 2 — see the roadmap below). - -## The course at a glance - -**Foundations (read first):** -[glossary](docs/02-foundations/glossary.md) · -[mental models](docs/02-foundations/mental-models.md) · -[concepts](docs/02-foundations/concepts.md) · -[the four layers](docs/02-foundations/the-four-layers.md) · -[primitives](docs/02-foundations/primitives.md) · -[primitives matrix](docs/02-foundations/primitives-matrix.md) - -**The 14-step roadmap** *(content lands over the next days — links activate as parts ship)*: - -| Part | Steps | What you learn | -| ------ | ------- | ---------------- | -| 1 · The Shift | 01–03 | prompting → looping, the four layers, anatomy of a loop | -| 2 · The Heartbeat | 04–07 | in-session, run-until-done, schedules, event-driven | -| 3 · The Body | 08–11 | worktrees, skills, connectors/MCP, maker ≠ checker | -| 4 · The Spine | 12 | durable state between runs | -| 5 · A Complete Loop | 13 | build the same loop twice (Claude Code & OpenCode) | -| 6 · Human Control | 14 | staying the engineer: cost, verification, nested loops | - -Then: methods for designing your own loop, the operating handbook (safety, failure -modes, recovery), the prebuilt loop library, graded labs, and the **Loop Ready** -certification capstone. - -## What's in the repo +3. **Speed-run the primers** — the + [Agentic-Coding Primer](docs/01-prerequisites/agentic-coding-primer.md) and the + [Spec-Driven Primer](docs/01-prerequisites/spec-driven-primer.md). +4. **Ground yourself in the foundations** — start with + [Mental Models](docs/02-foundations/mental-models.md). +5. **Begin the 14 steps** — [Part 1 · The Shift](docs/03-part-1-the-shift/), then follow + the [curriculum](#-curriculum) through to the + [Loop Ready certification](docs/assessments/loop-ready-certification.md). + +![·](assets/section-divider.svg) + +## 🛡️ Operating & safety + +A loop without a provable stop is an incident with a delay. The operating handbook is a +first-class part of the course, not an appendix: + +- [Operating loops](docs/10-operating/operating-loops.md) — the day-to-day handbook +- [Safety](docs/10-operating/safety.md) — L1 report-only first, human gates, budgets +- [Failure modes](docs/10-operating/failure-modes.md) · + [Infinite loops](docs/10-operating/infinite-loops.md) · + [Anti-patterns](docs/10-operating/anti-patterns.md) +- [Observability](docs/10-operating/observability.md) · + [Recovery playbook](docs/10-operating/recovery-playbook.md) · + [Multi-loop operation](docs/10-operating/multi-loop.md) + +The house rules this repo itself runs under — one owner per file, a sacred spine, stop +conditions as specs, green ≠ done — live in [`LOOP.md`](LOOP.md). + +## 🏅 Certification: Loop Ready + +The course ends with a graded capstone, not a participation badge. You design, build, +and operate a loop of your own, then defend it against the +[Capstone Rubric](docs/assessments/capstone-rubric.md) and pass the +[Final Exam](docs/assessments/final-exam.md). +[**View the full certification path →**](docs/assessments/loop-ready-certification.md) + +![·](assets/section-divider.svg) + +## 📂 Repository map | Folder | What it holds | -| -------- | --------------- | +| --- | --- | | `docs/` | the course — single source of truth for GitHub *and* the website | | `loops/` | [the loops that build this course](loops/README.md) — real prompts, real spines | -| `patterns/` | the seven core loop patterns *(Day 2+)* | +| `patterns/` | the 20 loop patterns ([table above](#-loop-pattern-library)) | | `starters/` | [install-and-fill loop kits](starters/README.md) — [one `npx` command](starters/getting-started.md) into any project, or start from [`_template/`](starters/_template/README.md) | -| `skills/`, `templates/`, `examples/`, `stories/` | reusable parts and case studies *(Day 2+)* | +| `packages/loop-kit/` | the published CLI behind `npx @loop-engineering/loop-kit` | +| `skills/`, `templates/`, `examples/`, `stories/` | reusable parts and case studies | | `resources/` | [source attribution](resources/sources.md) for all nine primary sources | -| `web/` | the Next.js course website *(Day 4)* | +| `web/` | the Next.js course website *(in progress)* | + +## 🤝 Contributing & credits + +Pull requests are genuinely welcome — start with the +[Contributing Guide](CONTRIBUTING.md). This course stands on the shoulders of nine +primary sources: it synthesizes and adapts MIT-licensed material and public writing, +and it credits every one of them, page by page, on the +[Sources & Attribution page](resources/sources.md). Academic or written citation? +Use [`CITATION.cff`](CITATION.cff). + +Licensed [MIT](LICENSE) — free to learn from, fork, and teach with. + +
-## Contributing & credits +**[⬆ Back to top](#loop-engineering-crash-course)** · Built with its own loops, one +provable stop at a time. -Pull requests are genuinely welcome — start with [`CONTRIBUTING.md`](CONTRIBUTING.md). -This course stands on the shoulders of nine primary sources: it synthesizes and adapts -MIT-licensed material and public writing, and it credits every one of them, page by page, -in [`resources/sources.md`](resources/sources.md). Licensed [MIT](LICENSE). +
diff --git a/assets/hero-banner.svg b/assets/hero-banner.svg new file mode 100644 index 0000000..dc3dd01 --- /dev/null +++ b/assets/hero-banner.svg @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CRASH COURSE + + + Loop Engineering + + + Design the system that prompts the agent — + not the prompt. + + + A free, open-source course: build autonomous agent loops with a heartbeat, + a body, a checker, a spine — and a stop you can prove. + + + + + 14 Steps + · + 4 Tracks + · + 20 Loop Kits + · + 2 Tools, Side by Side + · + Loop Ready Certified + + + + + + + + + + + + + + + THE + LOOP + + + + + + Heartbeat + + + Body + + + Checker + + + Spine + + + Provable stop + + + diff --git a/assets/terminal-loop-ready.svg b/assets/terminal-loop-ready.svg new file mode 100644 index 0000000..06ae2c0 --- /dev/null +++ b/assets/terminal-loop-ready.svg @@ -0,0 +1,220 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + loop-ready-audit — ~/my-project + + zsh + + + + $npm run audit:loops + + + + + + Auditing 20 kits in starters/ — ten checks per kit, 10 points each … + + + + + + + definition + LOOP.md + 20/20 + + + + docs + README.md + 20/20 + + + + spine + <kit>-state.md + 20/20 + + + + budget + loop-budget.md + 20/20 + + + + constitution + loop-constraints.md + 20/20 + + + + run log + loop-run-log.md + 20/20 + + + + checker + .claude/agents/loop-verifier.md + 20/20 + + + + skill + .claude/skills/loop-task/SKILL.md + 20/20 + + + + dual tool + opencode.json + loop-task.md + 20/20 + + + + filled in + no unfilled template placeholders + 20/20 + + + + + + ✔ 20 / 20 kits Loop Ready·exit 0 + + + + + + + + 0 + 10 + 20 + 30 + 40 + 50 + 60 + 70 + 80 + 90 + 100 + / 100 + LOOP READY + + + ALL KITS PASS + + + diff --git a/package.json b/package.json index 06ac018..c0badc9 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "scripts": { "validate:registry": "node scripts/validate-registry.mjs", "audit:loops": "node scripts/loop-ready-audit.mjs", + "render:terminal": "node scripts/render-loop-ready-terminal.mjs", "bundle:kit": "node packages/loop-kit/scripts/bundle-kits.mjs" } } diff --git a/scripts/loop-ready-audit.mjs b/scripts/loop-ready-audit.mjs index 635ce7f..ff9c778 100644 --- a/scripts/loop-ready-audit.mjs +++ b/scripts/loop-ready-audit.mjs @@ -82,20 +82,40 @@ function auditKit(name) { } const kits = listKits(); +// --json emits machine-readable results (used by render-loop-ready-terminal.mjs +// to draw the README demo from real data); human output is unchanged without it. +const jsonMode = process.argv.includes("--json"); if (kits.length === 0) { - console.log("No kits found in starters/ yet (only _template/ exists). Nothing to audit."); + if (jsonMode) { + console.log(JSON.stringify({ total: 0, passed: 0, kits: [] })); + } else { + console.log("No kits found in starters/ yet (only _template/ exists). Nothing to audit."); + } process.exit(0); } let anyFail = false; +const results = []; for (const name of kits) { const result = auditKit(name); + results.push(result); if (!result.pass) anyFail = true; + if (jsonMode) continue; console.log(`\n${result.pass ? "PASS" : "FAIL"} — ${name}`); for (const m of result.missing) console.log(` missing: ${m}`); for (const p of result.placeholders) console.log(` unfilled placeholder in ${p}`); } -console.log(`\n${kits.length} kit(s) checked. ${anyFail ? "At least one FAIL." : "All PASS."}`); +if (jsonMode) { + console.log( + JSON.stringify( + { total: kits.length, passed: results.filter((r) => r.pass).length, kits: results }, + null, + 2 + ) + ); +} else { + console.log(`\n${kits.length} kit(s) checked. ${anyFail ? "At least one FAIL." : "All PASS."}`); +} process.exit(anyFail ? 1 : 0); diff --git a/scripts/render-loop-ready-terminal.mjs b/scripts/render-loop-ready-terminal.mjs new file mode 100644 index 0000000..fad3b34 --- /dev/null +++ b/scripts/render-loop-ready-terminal.mjs @@ -0,0 +1,261 @@ +#!/usr/bin/env node +// Renders assets/terminal-loop-ready.svg from a REAL run of the Loop Ready audit. +// The README terminal demo is never hand-edited: this script runs +// scripts/loop-ready-audit.mjs --json, groups the actual results into the ten +// checks every kit must pass, and draws exactly what the audit said — ten +// streaming check rows, a score ring that climbs as each check lands, and the +// real exit code. Ten checks × 10 points = the Loop Ready score out of 100. +// Usage: node scripts/render-loop-ready-terminal.mjs (or: npm run render:terminal) + +import { spawnSync } from "node:child_process"; +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const repoRoot = path.resolve(__dirname, ".."); +const outFile = path.join(repoRoot, "assets", "terminal-loop-ready.svg"); + +// ── 1. Run the real audit ──────────────────────────────────────────────────── +const audit = spawnSync("node", [path.join(__dirname, "loop-ready-audit.mjs"), "--json"], { + cwd: repoRoot, + encoding: "utf8", +}); +const exitCode = audit.status ?? 1; + +let data; +try { + data = JSON.parse(audit.stdout); +} catch { + console.error("render-terminal: could not parse audit --json output."); + process.exit(1); +} +const { total, passed, kits } = data; +if (total === 0) { + console.error("render-terminal: no kits to audit — nothing to render."); + process.exit(1); +} +const allPass = passed === total; + +// ── 2. Group the real results into the ten checks every kit must pass ─────── +// Each check is worth 10 points; a check's points scale with how many kits pass +// it, so the score is 100 exactly when every kit passes every check. +const missing = (kit, file) => kit.missing.includes(file); +const CHECKS = [ + { label: "definition", path: "LOOP.md", fails: (k) => missing(k, "LOOP.md") }, + { label: "docs", path: "README.md", fails: (k) => missing(k, "README.md") }, + { label: "spine", path: "-state.md", fails: (k) => missing(k, `${k.name}-state.md`) }, + { label: "budget", path: "loop-budget.md", fails: (k) => missing(k, "loop-budget.md") }, + { label: "constitution", path: "loop-constraints.md", fails: (k) => missing(k, "loop-constraints.md") }, + { label: "run log", path: "loop-run-log.md", fails: (k) => missing(k, "loop-run-log.md") }, + { label: "checker", path: ".claude/agents/loop-verifier.md", fails: (k) => missing(k, ".claude/agents/loop-verifier.md") }, + { label: "skill", path: ".claude/skills/loop-task/SKILL.md", fails: (k) => missing(k, ".claude/skills/loop-task/SKILL.md") }, + { label: "dual tool", path: "opencode.json + loop-task.md", fails: (k) => missing(k, "opencode.json.example") || missing(k, "skills/loop-task.md") }, + { label: "filled in", path: "no unfilled template placeholders", fails: (k) => k.placeholders.length > 0 }, +]; + +const rows = CHECKS.map((c) => { + const ok = kits.filter((k) => !c.fails(k)).length; + return { ...c, ok, points: (ok / total) * 10 }; +}); + +// Cumulative score after each row lands — these drive the climbing counter. +// The final score floors instead of rounding so a single failing kit can never +// round back up to a perfect 100 (epsilon guards float sums like 99.9999…). +const steps = [0]; +for (const r of rows) steps.push(steps[steps.length - 1] + r.points); +const score = Math.floor(steps[steps.length - 1] + 1e-9); +const scoreValues = steps.map((s, i) => (i === steps.length - 1 ? score : Math.floor(s + 1e-9))); + +const scoreColor = score === 100 ? "#34d399" : score >= 70 ? "#fbbf24" : "#f87171"; + +// ── 3. Timeline ────────────────────────────────────────────────────────────── +const T = 7.0; // total keyframe timeline in seconds +const ROW_START = 1.9; +const ROW_STEP = 0.32; +const rowDelay = (i) => ROW_START + i * ROW_STEP; +const lastRow = rowDelay(rows.length - 1); +const summaryDelay = lastRow + 0.7; +const badgeDelay = summaryDelay + 0.3; +const pct = (t) => ((t / T) * 100).toFixed(2); + +// Climbing counter: value i is visible from rowDelay(i-1)+0.15 until the next +// value takes over; the final value stays. +const numCss = scoreValues + .map((v, i) => { + const from = i === 0 ? 0.4 : rowDelay(i - 1) + 0.15; + const isLast = i === scoreValues.length - 1; + const to = isLast ? T : rowDelay(i) + 0.15; + const kf = isLast + ? `@keyframes n${i} { 0%, ${pct(from)}% { opacity: 0; } ${pct(from + 0.02)}%, 100% { opacity: 1; } }` + : `@keyframes n${i} { 0%, ${pct(from)}% { opacity: 0; } ${pct(from + 0.02)}%, ${pct(to)}% { opacity: 1; } ${pct(to + 0.02)}%, 100% { opacity: 0; } }`; + return ` .n${i} { opacity: 0; animation: n${i} ${T}s linear forwards; }\n ${kf}`; + }) + .join("\n"); + +const rowCss = rows + .map((_, i) => ` .r${i} { animation-delay: ${rowDelay(i).toFixed(2)}s; }`) + .join("\n"); + +// ── 4. Geometry ────────────────────────────────────────────────────────────── +const ROW_H = 30; +const rowsTop = 150; +const summaryY = rowsTop + rows.length * ROW_H + 26; +const height = summaryY + 50; +const RING = { cx: 726, cy: 278, r: 84 }; +const circ = 2 * Math.PI * RING.r; +const ringTarget = (circ * (1 - score / 100)).toFixed(1); + +const esc = (s) => s.replace(/&/g, "&").replace(//g, ">"); + +const rowSvg = rows + .map((r, i) => { + const y = rowsTop + i * ROW_H; + const good = r.ok === total; + const mark = good ? "✔" : "✖"; + const markCls = good ? "green" : "red"; + return ` + ${mark} + ${esc(r.label)} + ${esc(r.path)} + ${r.ok}/${total} + `; + }) + .join("\n"); + +const summary = allPass + ? `✔ ${passed} / ${total} kits Loop Ready·exit ${exitCode}` + : `✖ ${passed} / ${total} kits Loop Ready·exit ${exitCode}`; + +const numSvg = scoreValues + .map((v, i) => ` ${v}`) + .join("\n"); + +// ── 5. Draw ────────────────────────────────────────────────────────────────── +const svg = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + loop-ready-audit — ~/my-project + + zsh + + + + $npm run audit:loops + + + + + + Auditing ${total} kits in starters/ — ten checks per kit, 10 points each … + + + + +${rowSvg} + + + + + ${summary} + + + + + + + +${numSvg} + / 100 + LOOP READY + + + ${allPass ? "ALL KITS PASS" : `${passed}/${total} PASS`} + + + +`; + +fs.writeFileSync(outFile, svg); +console.log( + `render-terminal: score ${score}/100 — ${passed}/${total} kits pass — wrote ${path.relative(repoRoot, outFile)}` +);