simplicio-loop 3.14.1__tar.gz → 3.16.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {simplicio_loop-3.14.1/simplicio_loop.egg-info → simplicio_loop-3.16.0}/PKG-INFO +1 -1
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/README.md +15 -3
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/pyproject.toml +1 -1
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop/__init__.py +2 -2
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop/_bundle/hooks/loop_stop.py +129 -21
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop/_bundle/skills/simplicio-loop/SKILL.md +26 -5
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop/_bundle/skills/simplicio-review/SKILL.md +10 -0
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop/_bundle/skills/simplicio-tasks/SKILL.md +33 -0
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop/_bundle/skills/simplicio-tasks/references/extension-points.md +2 -2
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop/_bundle/skills/simplicio-tasks/references/orchestration.md +15 -0
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop/_bundle/skills/simplicio-tasks/references/quality-safety-delivery.md +63 -0
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0/simplicio_loop.egg-info}/PKG-INFO +1 -1
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop.egg-info/SOURCES.txt +2 -0
- simplicio_loop-3.16.0/tests/test_flow_audit.py +72 -0
- simplicio_loop-3.16.0/tests/test_impact_audit.py +125 -0
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/tests/test_worker_selftests.py +17 -3
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/LICENSE +0 -0
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/MANIFEST.in +0 -0
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/PYPI.md +0 -0
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/setup.cfg +0 -0
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop/_bundle/hooks/README.md +0 -0
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop/_bundle/hooks/action_gate.py +0 -0
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop/_bundle/hooks/hooks.claude.json +0 -0
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop/_bundle/hooks/hooks.json +0 -0
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop/_bundle/hooks/learn_stop.py +0 -0
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop/_bundle/hooks/loop_capture.py +0 -0
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop/_bundle/hooks/orient_clamp.py +0 -0
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop/_bundle/hooks/orient_rewrite.py +0 -0
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop/_bundle/hooks/simplicio_dashboard.py +0 -0
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop/_bundle/hooks/simplicio_watch.py +0 -0
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop/_bundle/skills/simplicio-compress/SKILL.md +0 -0
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop/_bundle/skills/simplicio-learn/SKILL.md +0 -0
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop/_bundle/skills/simplicio-orient/SKILL.md +0 -0
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop/_bundle/skills/simplicio-tasks/references/agentsview-adapter.md +0 -0
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop/_bundle/skills/simplicio-tasks/references/azure-devops-adapter.md +0 -0
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop/_bundle/skills/simplicio-tasks/references/lmcache-adapter.md +0 -0
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop/_bundle/skills/simplicio-tasks/references/standing-loop-247.md +0 -0
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop/_bundle/skills/simplicio-tasks/references/token-capture.md +0 -0
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop/_bundle/skills/simplicio-tasks/references/token-economy.md +0 -0
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop/_bundle/skills/simplicio-tasks/references/understand-anything-adapter.md +0 -0
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop/_bundle/skills/simplicio-tasks/references/video-evidence.md +0 -0
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop/_bundle/skills/simplicio-tasks/references/web-evidence.md +0 -0
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop/cli.py +0 -0
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop.egg-info/dependency_links.txt +0 -0
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop.egg-info/entry_points.txt +0 -0
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop.egg-info/requires.txt +0 -0
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop.egg-info/top_level.txt +0 -0
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/tests/test_action_gate.py +0 -0
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/tests/test_loop_e2e.py +0 -0
- {simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/tests/test_worker_smoke.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: simplicio-loop
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.16.0
|
|
4
4
|
Summary: The Universal Looping AI Orchestrator — a runtime-agnostic super-plugin (6 skills) that drains any queue of work end-to-end on any LLM/runtime.
|
|
5
5
|
Author-email: Wesley Simplicio <wesleybob4@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -84,7 +84,7 @@ protocol on 11 runtimes**, and it does all of this with **aggressive, honest tok
|
|
|
84
84
|
## 📘 Official capability record
|
|
85
85
|
|
|
86
86
|
The complete, official roster of what `simplicio-tasks` ships — every capability below is **real,
|
|
87
|
-
runnable, and tested** (`python3 scripts/check.py`: claims-audit
|
|
87
|
+
runnable, and tested** (`python3 scripts/check.py`: claims-audit 5/5 + local test suite). Each links to its
|
|
88
88
|
deep section and its worker.
|
|
89
89
|
|
|
90
90
|
| Capability | What it does | Proof / worker | Details |
|
|
@@ -92,6 +92,8 @@ deep section and its worker.
|
|
|
92
92
|
| 🎬 **Video evidence** (`video_evidence`) | Records the **real browser session** as moving proof a UI change works (Playwright, default); renders a **deterministic captioned MP4** with [hyperframes](https://github.com/heygen-com/hyperframes) for an explicit explainer request (`/simplicio-tasks make a video of screen X`) | `scripts/video_evidence.py` · BLOCKED (never fake-pass) without the toolchain | [§ Video evidence](#-video-evidence--playwright-by-default-hyperframes-on-request) |
|
|
93
93
|
| 🧠 **Attempt memory + stall detector** | A durable run-journal (`.orchestrator/loop/journal.jsonl`) + a stall detector so the loop **changes strategy instead of oscillating**; incremental triage (`since`) reads only the delta each turn | `scripts/loop_journal.py` · `selftest` 9/9 | [§ Anti-oscillation](#-attempt-memory--stall-detector-anti-oscillation) |
|
|
94
94
|
| 🧭 **Repo conventions** (`repo_conventions`) | **Learns the repo's own playbook** — mines git history + merged PRs + static config into `.orchestrator/conventions.json` so every new branch/commit/PR mirrors the team's established style; worktree-per-item isolation is the default | `scripts/repo_conventions.py` · `selftest` 19/19 | [§ The full flow](#️-the-full-flow--from-demand-to-delivery) |
|
|
95
|
+
| 🧩 **Scope reflection** (`dependency_graph`) | Maps local dependencies, reverse dependents, and related tests from the planned touched files; blocks task plans that ignore callers, sibling files, or proof points before the edit starts | `scripts/impact_audit.py` · `selftest` | [§ Tests & local checks](#-tests--local-checks-no-paid-ci) |
|
|
96
|
+
| 🕸️ **Flow coverage** (`endpoint_compare`) | Maps mixed front/back/service workspaces: UI actions → frontend HTTP calls → backend endpoints → service calls; blocks frontend calls with no backend endpoint and stubbed endpoints, and surfaces unclassified loose ends | `scripts/flow_audit.py` · `selftest` | [§ Tests & local checks](#-tests--local-checks-no-paid-ci) |
|
|
95
97
|
| 🔒 **Fail-closed safety gate** (`action_gate`) | A `PreToolUse`/git-pre-push hook that **mechanically blocks** force-push, history rewrite, mass-delete, destructive DDL, infra teardown, and secret-laden commits/pushes — Step 5 made executable, not prose | `hooks/action_gate.py` · `selftest` 15/15 | [§ Safety](#-safety-non-negotiable) |
|
|
96
98
|
| 🔬 **Local verification** | A test suite (worker selftests + an **e2e of the loop driver** proving evidence-gated exit) + a **claims-audit** (referenced scripts exist · counts consistent · `_bundle ≡ source`) — all local, **no paid CI** | `scripts/check.py` · `scripts/claims_audit.py` · `tests/` | [§ Tests & local checks](#-tests--local-checks-no-paid-ci) |
|
|
97
99
|
| ✅ **Honest savings** | The savings line is now **evidence-gated, not mandatory** — a number is shown only with a measured receipt (clamp/signatures/cache/`deterministic_edit`/ledger); never fabricated | token-economy contract | [§ Token economy](#-token-economy) |
|
|
@@ -227,8 +229,8 @@ flowchart TD
|
|
|
227
229
|
POOL --> QG
|
|
228
230
|
subgraph QG["7 · Quality gates"]
|
|
229
231
|
direction LR
|
|
230
|
-
Q1["AC gate = real DoD"]
|
|
231
|
-
Q2["WORKS not just compiles · web_verify
|
|
232
|
+
Q1["AC gate + impact_audit = real DoD"]
|
|
233
|
+
Q2["WORKS not just compiles · web_verify · video_evidence · flow_audit"]
|
|
232
234
|
Q3["adversarial review · thermos rubrics"]
|
|
233
235
|
end
|
|
234
236
|
QG --> SG
|
|
@@ -535,6 +537,16 @@ python3 scripts/check.py # the whole gate (audit + tests)
|
|
|
535
537
|
- **Claims audit** (`scripts/claims_audit.py`, fail-closed) — every `scripts/*.py` the docs
|
|
536
538
|
reference exists · the extension-point count agrees across all files · each cited worker command
|
|
537
539
|
actually runs · the shipped `simplicio_loop/_bundle/` skills are **byte-identical** to source.
|
|
540
|
+
- **Impact audit** (`scripts/impact_audit.py`) — for any code task, proves the declared task
|
|
541
|
+
surface covers the local blast radius: dependencies, reverse dependents, and related tests.
|
|
542
|
+
```bash
|
|
543
|
+
python3 scripts/impact_audit.py audit . --file path/to/seed.py --cover path/to/seed.py --fail-on high
|
|
544
|
+
```
|
|
545
|
+
- **Flow audit** (`scripts/flow_audit.py`) — for mixed front/back/service repos, produces the
|
|
546
|
+
`endpoint_compare` evidence map and fails on objective integration gaps:
|
|
547
|
+
```bash
|
|
548
|
+
python3 scripts/flow_audit.py audit . --fail-on high
|
|
549
|
+
```
|
|
538
550
|
- **Wire it as a git pre-push hook** to keep `main` honest for free:
|
|
539
551
|
```bash
|
|
540
552
|
printf '#!/bin/sh\npython3 scripts/check.py\n' > .git/hooks/pre-push && chmod +x .git/hooks/pre-push
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "simplicio-loop"
|
|
7
|
-
version = "3.
|
|
7
|
+
version = "3.16.0"
|
|
8
8
|
description = "The Universal Looping AI Orchestrator — a runtime-agnostic super-plugin (6 skills) that drains any queue of work end-to-end on any LLM/runtime."
|
|
9
9
|
readme = "PYPI.md"
|
|
10
10
|
requires-python = ">=3.8"
|
|
@@ -11,17 +11,26 @@ hook must never trap the agent in an endless loop. The real guards are the
|
|
|
11
11
|
|
|
12
12
|
State (single source of truth): .orchestrator/loop/scratchpad.md (+ sibling `done` flag)
|
|
13
13
|
Reads stdin JSON from the host (Claude: {transcript_path,...}; Cursor: {text,...}).
|
|
14
|
+
|
|
15
|
+
Cross-agent handoff: an INCOMPLETE stop (budget halted, iteration cap, manual STOP
|
|
16
|
+
signal) writes `.orchestrator/loop/HANDOFF.md` before clearing the scratchpad, so a
|
|
17
|
+
different agent/runtime picking up this repo cold — because the first one ran out of
|
|
18
|
+
budget — can resume without re-deriving the goal, the verified acceptance criteria, or
|
|
19
|
+
the dead-end attempts. A successful (promise-fulfilled) stop needs no handoff.
|
|
14
20
|
"""
|
|
15
21
|
import json
|
|
16
22
|
import os
|
|
17
23
|
import re
|
|
18
24
|
import sys
|
|
25
|
+
import time
|
|
19
26
|
|
|
20
27
|
LOOP_DIR = os.path.join(".orchestrator", "loop")
|
|
21
28
|
SCRATCHPAD = os.path.join(LOOP_DIR, "scratchpad.md")
|
|
22
29
|
DONE_FLAG = os.path.join(LOOP_DIR, "done")
|
|
23
30
|
LAST_RESP = os.path.join(LOOP_DIR, "last_response.txt")
|
|
24
31
|
ANCHOR = os.path.join(LOOP_DIR, "anchor.json")
|
|
32
|
+
JOURNAL = os.path.join(LOOP_DIR, "journal.jsonl")
|
|
33
|
+
HANDOFF = os.path.join(LOOP_DIR, "HANDOFF.md")
|
|
25
34
|
STOP_SIGNAL = os.path.join(".orchestrator", "STOP")
|
|
26
35
|
BUDGET = os.path.join(".orchestrator", "loop-budget.json")
|
|
27
36
|
GATE_LOCK = os.path.join(LOOP_DIR, "gate.lock")
|
|
@@ -122,31 +131,118 @@ def gate_running():
|
|
|
122
131
|
try:
|
|
123
132
|
if not os.path.exists(GATE_LOCK):
|
|
124
133
|
return False
|
|
125
|
-
import time
|
|
126
134
|
return (time.time() - os.path.getmtime(GATE_LOCK)) < GATE_TTL_SEC
|
|
127
135
|
except Exception:
|
|
128
136
|
return False
|
|
129
137
|
|
|
130
138
|
|
|
139
|
+
def read_anchor():
|
|
140
|
+
"""Return the parsed task anchor dict, or None if absent/corrupt. Fail-open."""
|
|
141
|
+
try:
|
|
142
|
+
with open(ANCHOR, encoding="utf-8") as f:
|
|
143
|
+
return json.load(f)
|
|
144
|
+
except Exception:
|
|
145
|
+
return None
|
|
146
|
+
|
|
147
|
+
|
|
131
148
|
def anchor_pending():
|
|
132
149
|
"""Return the unverified acceptance-criteria ids from the task anchor, or [].
|
|
133
150
|
|
|
134
151
|
The mechanical anti-drift gate: a `<promise>` must not end the loop while the frozen task anchor
|
|
135
|
-
still has criteria that are not `done`.
|
|
136
|
-
|
|
137
|
-
self-contained). FAIL-OPEN: a missing / unreadable / empty anchor, or one with no criteria,
|
|
138
|
-
returns [] so the gate never blocks — a buggy anchor must never trap the loop, and the rejection
|
|
139
|
-
it does cause is still bounded by `max_iterations` + the budget. Only a cleanly-parsed anchor
|
|
140
|
-
with ≥1 criterion that is not `done` reports pending.
|
|
152
|
+
still has criteria that are not `done`. FAIL-OPEN: a missing / unreadable / empty anchor, or one
|
|
153
|
+
with no criteria, returns [] so the gate never blocks — a buggy anchor must never trap the loop.
|
|
141
154
|
"""
|
|
155
|
+
data = read_anchor()
|
|
156
|
+
if not data:
|
|
157
|
+
return []
|
|
158
|
+
crit = data.get("criteria") or []
|
|
159
|
+
return [c.get("id") for c in crit
|
|
160
|
+
if isinstance(c, dict) and c.get("status") != "done"]
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def tail_journal(n=8):
|
|
164
|
+
"""Last N attempt records from the journal, oldest first. [] on any read error."""
|
|
142
165
|
try:
|
|
143
|
-
with open(
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
166
|
+
with open(JOURNAL, encoding="utf-8") as f:
|
|
167
|
+
lines = [ln for ln in f if ln.strip()]
|
|
168
|
+
out = []
|
|
169
|
+
for ln in lines[-n:]:
|
|
170
|
+
try:
|
|
171
|
+
out.append(json.loads(ln))
|
|
172
|
+
except Exception:
|
|
173
|
+
continue
|
|
174
|
+
return out
|
|
148
175
|
except Exception:
|
|
149
|
-
return []
|
|
176
|
+
return []
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def write_handoff(reason, meta=None, body=None):
|
|
180
|
+
"""Write the cross-agent continuation artifact before an INCOMPLETE stop.
|
|
181
|
+
|
|
182
|
+
Aggregates the frozen task anchor (goal + acceptance criteria + evidence), the last journal
|
|
183
|
+
attempts (what was already tried, to avoid re-running a dead end), and the live scratchpad
|
|
184
|
+
iteration/promise — everything a fresh agent needs to resume cold, without this conversation.
|
|
185
|
+
Fail-open: any error here must never block the stop itself.
|
|
186
|
+
"""
|
|
187
|
+
try:
|
|
188
|
+
anchor = read_anchor() or {}
|
|
189
|
+
criteria = anchor.get("criteria") or []
|
|
190
|
+
attempts = tail_journal()
|
|
191
|
+
lines = [
|
|
192
|
+
"# simplicio-loop handoff",
|
|
193
|
+
"",
|
|
194
|
+
"Stop reason: %s" % reason,
|
|
195
|
+
"Stopped at: %s" % time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
|
|
196
|
+
]
|
|
197
|
+
if meta:
|
|
198
|
+
lines += [
|
|
199
|
+
"Iteration: %s / %s" % (meta.get("iteration", "?"), meta.get("max_iterations", "?")),
|
|
200
|
+
"Completion promise: %s" % (meta.get("completion_promise") or "(none set)"),
|
|
201
|
+
]
|
|
202
|
+
if anchor.get("goal"):
|
|
203
|
+
lines += ["", "## Frozen goal", "", anchor["goal"]]
|
|
204
|
+
elif body:
|
|
205
|
+
lines += ["", "## Goal (from scratchpad, no anchor set)", "", body]
|
|
206
|
+
if criteria:
|
|
207
|
+
lines += ["", "## Acceptance criteria"]
|
|
208
|
+
for c in criteria:
|
|
209
|
+
if not isinstance(c, dict):
|
|
210
|
+
continue
|
|
211
|
+
mark = "x" if c.get("status") == "done" else " "
|
|
212
|
+
ev = (" — %s" % c["evidence"]) if c.get("evidence") else ""
|
|
213
|
+
lines.append(
|
|
214
|
+
"- [%s] %s (%s)%s"
|
|
215
|
+
% (mark, c.get("text", c.get("id", "?")), c.get("status", "pending"), ev)
|
|
216
|
+
)
|
|
217
|
+
if attempts:
|
|
218
|
+
lines += ["", "## Last attempts (`scripts/loop_journal.py resume` for the full read)"]
|
|
219
|
+
for a in attempts:
|
|
220
|
+
lines.append(
|
|
221
|
+
"- iter %s: %s -> %s (fp %s)%s"
|
|
222
|
+
% (
|
|
223
|
+
a.get("iteration", "?"),
|
|
224
|
+
a.get("action", "?"),
|
|
225
|
+
a.get("gate", "?"),
|
|
226
|
+
(a.get("fingerprint") or "")[:12],
|
|
227
|
+
(" — %s" % a["note"]) if a.get("note") else "",
|
|
228
|
+
)
|
|
229
|
+
)
|
|
230
|
+
lines += [
|
|
231
|
+
"",
|
|
232
|
+
"## Resume",
|
|
233
|
+
"",
|
|
234
|
+
"1. `python3 scripts/task_anchor.py status` (or `gate --exit-code`) — verified vs open.",
|
|
235
|
+
"2. `python3 scripts/loop_journal.py resume` — dead-end actions to avoid.",
|
|
236
|
+
"3. `git log --oneline -10` / `git diff` — what already landed.",
|
|
237
|
+
"4. Re-arm the loop once the stop cause (budget/cap/manual) is resolved.",
|
|
238
|
+
"",
|
|
239
|
+
]
|
|
240
|
+
tmp = HANDOFF + ".tmp"
|
|
241
|
+
with open(tmp, "w", encoding="utf-8") as f:
|
|
242
|
+
f.write("\n".join(lines))
|
|
243
|
+
os.replace(tmp, HANDOFF)
|
|
244
|
+
except Exception:
|
|
245
|
+
pass # fail-open: a broken handoff write must never block the stop
|
|
150
246
|
|
|
151
247
|
|
|
152
248
|
def budget_halted():
|
|
@@ -177,8 +273,18 @@ def emit_refeed(followup):
|
|
|
177
273
|
|
|
178
274
|
def main():
|
|
179
275
|
try:
|
|
180
|
-
|
|
276
|
+
meta, body = None, None
|
|
277
|
+
if os.path.exists(SCRATCHPAD):
|
|
278
|
+
try:
|
|
279
|
+
with open(SCRATCHPAD, encoding="utf-8") as f:
|
|
280
|
+
meta, body = parse_frontmatter(f.read())
|
|
281
|
+
except OSError:
|
|
282
|
+
meta, body = None, None
|
|
283
|
+
|
|
284
|
+
# Explicit STOP signal beats everything — but still hand off if there was live state.
|
|
181
285
|
if os.path.exists(STOP_SIGNAL):
|
|
286
|
+
if meta is not None:
|
|
287
|
+
write_handoff("manual STOP signal", meta, body)
|
|
182
288
|
cleanup_and_stop()
|
|
183
289
|
# Waiting on a background gate (workflow / CI / long task)? Let the turn end WITHOUT
|
|
184
290
|
# consuming an iteration or re-feeding — we are blocked on that gate, not idle. The gate's
|
|
@@ -188,9 +294,6 @@ def main():
|
|
|
188
294
|
# (1) No active loop.
|
|
189
295
|
if not os.path.exists(SCRATCHPAD):
|
|
190
296
|
allow_stop()
|
|
191
|
-
with open(SCRATCHPAD, encoding="utf-8") as f:
|
|
192
|
-
content = f.read()
|
|
193
|
-
meta, body = parse_frontmatter(content)
|
|
194
297
|
# (2) Corrupt state.
|
|
195
298
|
if meta is None:
|
|
196
299
|
cleanup_and_stop()
|
|
@@ -215,21 +318,26 @@ def main():
|
|
|
215
318
|
# in the task anchor — the mechanical anti-drift gate. Pending ACs ⇒ ignore the
|
|
216
319
|
# promise and keep looping (still bounded by max_iter), never a false "done".
|
|
217
320
|
if ((not evidence_required) or has_evidence) and not anchor_pending():
|
|
218
|
-
cleanup_and_stop() # (3) promise fulfilled → stop
|
|
321
|
+
cleanup_and_stop() # (3) promise fulfilled → stop, no handoff needed
|
|
219
322
|
# promise without evidence, or anchor still has open ACs → ignore, keep looping
|
|
220
323
|
# (3') Cursor capture may have raised the flag.
|
|
221
324
|
if os.path.exists(DONE_FLAG):
|
|
222
325
|
cleanup_and_stop()
|
|
223
|
-
# (4) Iteration cap.
|
|
326
|
+
# (4) Iteration cap — incomplete stop, hand off.
|
|
224
327
|
if max_iter > 0 and iteration >= max_iter:
|
|
328
|
+
write_handoff("max_iterations cap reached", meta, body)
|
|
225
329
|
cleanup_and_stop()
|
|
226
|
-
# (5) Budget halted.
|
|
330
|
+
# (5) Budget halted — incomplete stop, hand off. This is the exact "ran out of tokens/$"
|
|
331
|
+
# case: a different agent must be able to pick this up cold.
|
|
227
332
|
if budget_halted():
|
|
333
|
+
write_handoff("budget halted", meta, body)
|
|
228
334
|
cleanup_and_stop()
|
|
229
335
|
# (6) Continue: bump iteration in place, re-feed the goal body.
|
|
230
336
|
nxt = iteration + 1
|
|
337
|
+
with open(SCRATCHPAD, encoding="utf-8") as f:
|
|
338
|
+
raw = f.read()
|
|
231
339
|
new_content = re.sub(
|
|
232
|
-
r"^iteration:\s*\d+", "iteration: %d" % nxt,
|
|
340
|
+
r"^iteration:\s*\d+", "iteration: %d" % nxt, raw, count=1, flags=re.M
|
|
233
341
|
)
|
|
234
342
|
try:
|
|
235
343
|
tmp = SCRATCHPAD + ".tmp"
|
|
@@ -173,14 +173,29 @@ detector below. It is the difference between a loop that converges and one that
|
|
|
173
173
|
last recorded turn's commit. **And re-read the task anchor** — `python3 scripts/task_anchor.py
|
|
174
174
|
check --goal "<the goal worked this turn>" --exit-code` — so the turn stays on the SAME frozen
|
|
175
175
|
acceptance criteria and cannot drift: a `DRIFT` verdict (exit 11) means the goal moved; STOP and
|
|
176
|
-
re-anchor explicitly (`--force`), never wander silently.
|
|
177
|
-
|
|
178
|
-
|
|
176
|
+
re-anchor explicitly (`--force`), never wander silently. Before deciding the next code change,
|
|
177
|
+
refresh the local impact map for the planned seed files with
|
|
178
|
+
`python3 scripts/impact_audit.py audit <root> --file <seed> --cover <known-reviewed-file> --json
|
|
179
|
+
> .orchestrator/impact-audit.json` so the turn sees callers, neighboring dependencies, and
|
|
180
|
+
related tests before it edits. For shared/public contracts or signature changes, tighten that gate
|
|
181
|
+
to `--fail-on medium`. For mixed front/back/service workspaces or any cross-surface user flow,
|
|
182
|
+
also refresh the flow map with
|
|
183
|
+
`python3 scripts/flow_audit.py audit <root> --fail-on high --json > .orchestrator/flow-audit.json`
|
|
184
|
+
so triage sees UI actions, frontend calls, backend endpoints, and service calls before deciding
|
|
185
|
+
the next move. The journal is the loop's memory for ATTEMPTS; the anchor is its memory for SCOPE;
|
|
186
|
+
the impact audit is its memory for BLAST RADIUS; the flow audit is its memory for INTEGRATION.
|
|
187
|
+
Act only on what is still genuinely open; never redo done work or act on a stale picture
|
|
188
|
+
(idempotency).
|
|
179
189
|
3. **Work the goal** each turn as if fresh, against that triaged state. The model DECIDES the
|
|
180
190
|
AC-scoped change; the **`simplicio-dev-cli` operator APPLIES and verifies it**
|
|
181
191
|
(`simplicio-dev-cli task "<change>" --target <file>`) — do not hand-edit inside the loop. End EVERY
|
|
182
192
|
iteration with a short, concrete verification — the operator's passing test run, or one gate /
|
|
183
|
-
command / `file:line` receipt.
|
|
193
|
+
command / `file:line` receipt. If the actual edit surface expands, rerun `impact_audit.py` with
|
|
194
|
+
the new seeds/cover and treat uncovered reverse dependents as failed verification; use
|
|
195
|
+
`--fail-on medium` for shared/public contracts or signature changes. If the change crosses
|
|
196
|
+
UI/API/service boundaries, rerun
|
|
197
|
+
`flow_audit.py` after the edit and treat high gaps as failed verification; use `--fail-on medium`
|
|
198
|
+
when the AC promises backend integration for that UI flow. **Then RECORD the attempt** in the journal:
|
|
184
199
|
`loop_journal.py record --iteration N --action "<what you changed>" --hypothesis "<why>"
|
|
185
200
|
--gate pass|fail --gate-output <test.log>` — on a failure the gate output is fingerprinted so the
|
|
186
201
|
SAME failure is recognised next turn. Keep iterations small and verifiable: a turn that only
|
|
@@ -262,6 +277,12 @@ only if, in the SAME turn, there is concrete evidence the work is truly done:
|
|
|
262
277
|
|
|
263
278
|
- the run-verification gate passed ("works, not just compiles" — `simplicio-tasks` Step 4b) —
|
|
264
279
|
the `simplicio-dev-cli` operator's passing test+verify pass (its contract step 5/6) satisfies this, or
|
|
280
|
+
- the flow coverage gate passed for a mixed front/back/service change —
|
|
281
|
+
`python3 scripts/flow_audit.py audit <root> --fail-on high` (or `--fail-on medium` for ACs that
|
|
282
|
+
promise backend integration) found no unhandled UI/API/service gaps, or
|
|
283
|
+
- the scope/impact gate passed for the changed shared files —
|
|
284
|
+
`python3 scripts/impact_audit.py audit <root> --file <seed> ...` found no uncovered reverse
|
|
285
|
+
dependents (and, for shared/public contracts, no uncovered local deps/tests under `--fail-on medium`), or
|
|
265
286
|
- the named acceptance criteria are each checked with a `file:line` or command-output receipt —
|
|
266
287
|
mechanically enforced by the task anchor: `python3 scripts/task_anchor.py gate --exit-code` must
|
|
267
288
|
return READY (every anchored AC `done` with a receipt; exit 12 = still pending) before the promise
|
|
@@ -291,7 +312,7 @@ Where the host runtime supports lifecycle hooks, bind the two cross-platform hoo
|
|
|
291
312
|
| Hook | Fires | Job |
|
|
292
313
|
|---|---|---|
|
|
293
314
|
| `afterAgentResponse` → `loop_capture.py` | after every turn | extract `<promise>…</promise>`; if it exactly equals `completion_promise` AND in-turn evidence exists → `touch .orchestrator/loop/done`. Fire-and-forget, `exit 0`. Never stops the loop itself. |
|
|
294
|
-
| `stop` → `loop_stop.py` | when the turn ends | guard clauses, each ends the loop cleanly (remove state, `exit 0`): (1) no scratchpad → stop; (2) corrupt frontmatter → stop; (3) `done` flag present → stop (promise fulfilled); (4) `iteration >= max_iterations > 0` → stop (cap); (5) budget halted → stop; else increment `iteration` in place and emit `{"followup_message": "<header>\n\n<goal body>"}` to re-feed. |
|
|
315
|
+
| `stop` → `loop_stop.py` | when the turn ends | guard clauses, each ends the loop cleanly (remove state, `exit 0`): (1) no scratchpad → stop; (2) corrupt frontmatter → stop; (3) `done` flag present → stop (promise fulfilled); (4) `iteration >= max_iterations > 0` → write `HANDOFF.md`, then stop (cap); (5) budget halted → write `HANDOFF.md` (frozen goal + AC status + last attempts) for a different agent to resume, then stop; else increment `iteration` in place and emit `{"followup_message": "<header>\n\n<goal body>"}` to re-feed. |
|
|
295
316
|
|
|
296
317
|
Detection (`capture`) and termination (`stop`) are split on purpose — neither parses the
|
|
297
318
|
other's inline state. Iteration carries forward through git history + the working tree, not
|
|
@@ -60,6 +60,16 @@ down, no proportional token blow-up). Each gets the SAME context bundle and a DI
|
|
|
60
60
|
ledger entry with a screenshot + trace path AND 0 console errors (see the orchestrator's
|
|
61
61
|
`references/web-evidence.md`, Playwright). Missing or failing → `fix-required`. Evidence is the
|
|
62
62
|
artifact PATH, never pasted DOM/pixels.
|
|
63
|
+
- **Cross-surface change → require flow evidence.** If the diff touches frontend + backend/service
|
|
64
|
+
files, or an AC promises a UI→API/service flow, REQUIRE `scripts/flow_audit.py audit <root>
|
|
65
|
+
--fail-on high` evidence (`--fail-on medium` when backend integration is part of the AC). Missing
|
|
66
|
+
audit, frontend call without backend endpoint, stubbed endpoint, or unclassified UI/API/service
|
|
67
|
+
loose end → `fix-required`.
|
|
68
|
+
- **Shared-contract change → require impact evidence.** If the diff changes exported/shared
|
|
69
|
+
functions, controllers, DTOs/schemas, shared types, or a widely imported module, REQUIRE
|
|
70
|
+
`scripts/impact_audit.py audit <root> --file <seed> --cover <reviewed-file> ...` evidence.
|
|
71
|
+
Any uncovered reverse dependency is `fix-required`; use `--fail-on medium` when the contract
|
|
72
|
+
itself changed so uncovered neighboring deps/tests also block.
|
|
63
73
|
|
|
64
74
|
Each reviewer's task: **"Refute this change. Find any AC not met, any fake return, any break.
|
|
65
75
|
Default to 'not done' if uncertain. Cite every finding as `file:line` with a one-line why."**
|
|
@@ -173,6 +173,27 @@ wander off the task (the "desvio de tarefas" fix, Step 4 drift guard). Detail:
|
|
|
173
173
|
|
|
174
174
|
> **Understand Anything (optional).** If `.understand-anything/knowledge-graph.json` exists, use Understand Anything as the primary orientation — the graph already holds the complete code structure, relationships, and guided tours. Query it via semantic search instead of signatures-only reads.
|
|
175
175
|
|
|
176
|
+
> **Scope reflection / impact coverage (mandatory for any coded change).** Before editing, declare
|
|
177
|
+
> the seed files you expect to touch and the files you already know must be reviewed/adjusted, then
|
|
178
|
+
> run `python3 scripts/impact_audit.py audit <root> --file <seed> --cover <planned-file> --json >
|
|
179
|
+
> .orchestrator/impact-audit.json`. This maps local dependencies, reverse dependents, and related
|
|
180
|
+
> tests so the task cannot silently ignore callers, adjacent modules, or tests that prove the same
|
|
181
|
+
> flow. Any `high` gap (`uncovered_reverse_dependency`) blocks planning until that caller/dependent
|
|
182
|
+
> file is added to the review surface or explicitly ruled out by evidence. For shared/public
|
|
183
|
+
> contracts, signature changes, DTO/schema changes, or refactors in widely imported modules, use
|
|
184
|
+
> `--fail-on medium` so uncovered local dependencies and related tests also block the task plan.
|
|
185
|
+
|
|
186
|
+
> **Full-stack flow coverage (mandatory for mixed front/back/service workspaces).** When a repo
|
|
187
|
+
> root contains frontend + backend + services together — or the item touches a user flow that crosses
|
|
188
|
+
> those surfaces — run the flow audit BEFORE planning: `python3 scripts/flow_audit.py audit <root>
|
|
189
|
+
> --fail-on high --json > .orchestrator/flow-audit.json`. This maps UI actions, frontend HTTP
|
|
190
|
+
> calls, backend endpoints, and backend service calls. Any `high` gap (frontend call with no backend
|
|
191
|
+
> endpoint, or stubbed/incomplete backend endpoint) is a BLOCKER and must become an AC/fix before
|
|
192
|
+
> `done`. `medium` gaps (button/action with no observed backend call, endpoint with no observed
|
|
193
|
+
> caller, backend local-looking service call with no local endpoint) must be classified explicitly:
|
|
194
|
+
> local-only/internal/external by design, or promoted to an AC. For flows whose AC promises backend
|
|
195
|
+
> integration, rerun with `--fail-on medium`; do not leave loose ends unclassified.
|
|
196
|
+
|
|
176
197
|
> **Video-creation work-items (`video_evidence`).** A work-item — or the skill argument itself
|
|
177
198
|
> (e.g. `/simplicio-tasks make an explainer video of the login screen`) — may ASK for a demo video.
|
|
178
199
|
> Classify it cheaply in the terminal: `python3 scripts/video_evidence.py detect --goal "<text>"`.
|
|
@@ -220,12 +241,24 @@ Never mark done without green gates + evidence; a failure is NOT a blocker — i
|
|
|
220
241
|
is mechanical: `python3 scripts/task_anchor.py gate --exit-code` (exit 12 = criteria still
|
|
221
242
|
pending) MUST pass before you declare done or open the PR — "done" requires every anchored AC
|
|
222
243
|
verified with a receipt.
|
|
244
|
+
- **4a' Scope/impact gate:** if the actual change surface expanded, rerun
|
|
245
|
+
`python3 scripts/impact_audit.py audit <root> --file <seed> ... --cover <reviewed-file> ...`
|
|
246
|
+
and refresh `.orchestrator/impact-audit.json`. The task is not done while a changed shared file
|
|
247
|
+
still has uncovered reverse dependents, or while a contract/signature change leaves callers,
|
|
248
|
+
neighboring dependencies, or related tests outside the declared review surface. Use
|
|
249
|
+
`--fail-on medium` for shared/public contracts.
|
|
223
250
|
- **4b WORKS, not just compiles:** RUN it (`--help` + happy path / affected tests). Front-end
|
|
224
251
|
change → `web_verify` (screenshot + trace, `references/web-evidence.md`). For moving proof of a UI
|
|
225
252
|
change, `video_evidence verify --url <url>` records the **real session with Playwright** (default
|
|
226
253
|
engine) → a video attached to the PR. Only when the item ITSELF asks for a personalized explainer
|
|
227
254
|
("make a video of screen X") use `--engine hyperframes` (deterministic captioned slideshow).
|
|
228
255
|
Contract: `references/video-evidence.md`. Compiles-but-never-run = PARTIAL.
|
|
256
|
+
- **4b' Flow coverage gate (mixed front/back/service workspaces):** rerun
|
|
257
|
+
`python3 scripts/flow_audit.py audit <root> --fail-on high` after the change (or
|
|
258
|
+
`--fail-on medium` when the AC promises backend integration). The task is not done while a
|
|
259
|
+
frontend call lacks a backend endpoint, a backend endpoint is stubbed/incomplete, or any UI/API/
|
|
260
|
+
service gap remains unclassified. Attach `.orchestrator/flow-audit.json` or the human summary as
|
|
261
|
+
evidence alongside tests/screenshots.
|
|
229
262
|
- **4c Adversarial verify (MEDIUM+):** 2–3 independent verifiers prompted to REFUTE + check each
|
|
230
263
|
AC; majority-refute → back to fix. Delegate to `simplicio-review` when loaded. Full: `references/quality-safety-delivery.md`.
|
|
231
264
|
|
|
@@ -32,7 +32,7 @@ fallback with standard tools. The skill depends on the ABSTRACTION, never on a s
|
|
|
32
32
|
| `status` | Live observability dashboard | `gh` queries |
|
|
33
33
|
| `security` | Supply-chain / secret scan | `rg` for secrets |
|
|
34
34
|
| `intake` | Ingest work from sprint/board link | `gh issue list` |
|
|
35
|
-
| `dependency_graph` | Inter-item ordering as a resumable DAG (B after A; independents fan out); re-run skips done nodes | LLM topo-sorts by depends-on/blocked-by, runs ready first, journals done node-ids to resume |
|
|
35
|
+
| `dependency_graph` | Inter-item ordering as a resumable DAG (B after A; independents fan out); re-run skips done nodes. At code level, it also means "what else breaks if I touch this?" — runnable form: `scripts/impact_audit.py audit <root> --file <seed> --cover <planned-file> --json > .orchestrator/impact-audit.json` maps local dependencies, reverse dependents, and related tests for the planned task surface. `high` gaps block missing callers/dependents; `--fail-on medium` blocks uncovered local deps/tests for shared contracts. | LLM topo-sorts by depends-on/blocked-by, runs ready first, journals done node-ids to resume; for code impact, it uses `rg`/imports/git grep to enumerate local dependencies, reverse callers, and related tests, then widens the plan before editing |
|
|
36
36
|
| `durable_workflow` | Per-item pipeline (intake→plan→edit→validate→deliver) as a resumable phase state-machine; retry skips done phases | LLM drives phases, journals which phase each item reached, resumes from last completed |
|
|
37
37
|
| `work_queue` | Durable priority queue that runs+auto-retries+requeues-stuck, with a write-serialization lock for shared checkouts | LLM keeps queue in JSONL/SQLite, pops by priority, re-enqueues on fail, lockfile+TTL guards shared-tree writes |
|
|
38
38
|
| `resource_governor` | Dynamic mid-loop throttle: decide when to back off + machine-tier ceilings before scaling a wave | LLM re-probes CPU/RAM/load each tick, reduces fleet / sleeps longer under load, degrades tiers |
|
|
@@ -49,7 +49,7 @@ fallback with standard tools. The skill depends on the ABSTRACTION, never on a s
|
|
|
49
49
|
| `toolchain_detect` | Detect which build/lint/typecheck/test toolchains the repo actually has so validate/diagnostics route right | LLM inspects manifests/lockfiles/config + probes PATH to pick the correct toolchain per stack |
|
|
50
50
|
| `checkpoint_restore` | Snapshot run/repo state before a risky batch; restore to known-good if validation/delivery fails | LLM tags a commit / stashes / copies the journal before destructive ops, restores on failure |
|
|
51
51
|
| `notify` | Push progress/blocker/digest to a human channel + receive inbound approvals (async approval I/O) | LLM writes digest/approval-request to a file or session; no-reply = block the destructive op (headless rule) |
|
|
52
|
-
| `endpoint_compare` | Compare web/API/
|
|
52
|
+
| `endpoint_compare` | Compare web/API/service surfaces to detect drift; gaps become follow-up items or blockers. Runnable form: `scripts/flow_audit.py audit <root> --fail-on high --json > .orchestrator/flow-audit.json` maps UI actions, frontend HTTP calls, backend endpoints, and backend service calls. It blocks on frontend calls without backend endpoints and stubbed backend endpoints; `--fail-on medium` blocks unclassified UI/API/service loose ends for flows whose AC promises integration. | LLM lists UI actions, frontend calls, backend routes/controllers, OpenAPI entries, and service calls with `rg`, then diffs by hand to flag missing endpoints, stub handlers, orphan callers, and unclassified loose ends |
|
|
53
53
|
| `web_verify` | Drive a real browser (navigate/click/console) to prove a UI/web change works end-to-end; capture screenshot+trace as evidence | Playwright via `playwright-mcp` or headless `npx playwright` / `pytest-playwright`; evidence = artifact path, not pixels (see web-evidence.md) |
|
|
54
54
|
| `video_evidence` | Produce a demo video of a screen/feature as evidence — two engines | **DEFAULT = Playwright** native session recording (`video_evidence verify --url …` records the real browser drive to `.webm`→`.mp4`) for the normal moving-proof flow; **hyperframes** (`npx hyperframes render` — heygen-com/hyperframes) only for an EXPLICIT custom explainer request ("make a video of screen X"), assembling the `web_verify` screenshots into a deterministic captioned MP4. Evidence = video path, not bytes (see video-evidence.md); BLOCK if the toolchain is absent |
|
|
55
55
|
| `web_research` | Fetch current external knowledge (docs/CVE/version/SDK error), gated behind local-memory-miss, with provenance | LLM uses built-in web search/fetch only after local miss; records source URL as provenance |
|
|
@@ -45,6 +45,21 @@ API surface — a 600-line file → ~40 lines; full-body read only when editing
|
|
|
45
45
|
|
|
46
46
|
**2b-3 Build the plan BEFORE coding:** files to change, files to read first, AC checklist, risks/
|
|
47
47
|
unknowns, complexity (trivial|small|medium|large|critical). Coding starts only after the plan.
|
|
48
|
+
Make the plan mechanically accountable with the impact audit:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
python3 scripts/impact_audit.py audit <root> \
|
|
52
|
+
--file <seed-you-expect-to-touch> \
|
|
53
|
+
--cover <files-already-in-plan> \
|
|
54
|
+
--fail-on high \
|
|
55
|
+
--json > .orchestrator/impact-audit.json
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Treat a `high` issue as a planning failure: a caller/dependent file sits outside the declared task
|
|
59
|
+
surface, so the plan is incomplete. For shared/public contracts, signature changes, DTO/schema
|
|
60
|
+
changes, or refactors inside widely imported modules, tighten the gate to `--fail-on medium` so
|
|
61
|
+
uncovered local dependencies and related tests also block the plan. The point is to know the blast
|
|
62
|
+
radius before editing, not after the regression.
|
|
48
63
|
|
|
49
64
|
## Step 3 — Route: fast-path vs heavy-path
|
|
50
65
|
- **Fast-path** (queue small AND every item complexity ≤ 3): inline, solo, minimal receipt,
|
|
@@ -34,6 +34,41 @@ The DoD gate is then mechanical: `task_anchor.py gate --exit-code` (exit 12 = cr
|
|
|
34
34
|
pending) MUST pass before "done" or PR-open. This is the loop's durable working memory for SCOPE,
|
|
35
35
|
the sibling of `loop_journal`'s working memory for ATTEMPTS.
|
|
36
36
|
|
|
37
|
+
### 4a' — Scope/impact gate for dependency-aware tasks
|
|
38
|
+
Before editing, and again if the changed surface expands, make the task's blast radius explicit:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
python3 scripts/impact_audit.py audit <root> \
|
|
42
|
+
--file <seed-you-touch> \
|
|
43
|
+
--cover <reviewed-or-adjusted-files> \
|
|
44
|
+
--fail-on high \
|
|
45
|
+
--json > .orchestrator/impact-audit.json
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The audit maps three things around each seed file:
|
|
49
|
+
|
|
50
|
+
- local dependencies used by the seed
|
|
51
|
+
- reverse dependents/callers that reach the seed, including transitive import chains
|
|
52
|
+
- related tests that prove the same path
|
|
53
|
+
|
|
54
|
+
Default interpretation:
|
|
55
|
+
|
|
56
|
+
- `uncovered_reverse_dependency` is `high` and blocks the task: a caller/dependent file exists
|
|
57
|
+
outside the declared review/edit surface.
|
|
58
|
+
- `uncovered_local_dependency` and `uncovered_related_test` are `medium`: the plan is missing a
|
|
59
|
+
neighbor or proof point that should at least be reviewed.
|
|
60
|
+
|
|
61
|
+
For shared/public contracts, signature changes, DTO/schema changes, or refactors in widely
|
|
62
|
+
imported modules, run the stricter gate:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
python3 scripts/impact_audit.py audit <root> --fail-on medium
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
The final evidence can cite `.orchestrator/impact-audit.json` or summarize the explicit caller/test
|
|
69
|
+
classification. "I changed one file" is not enough when the dependency map says the task reached
|
|
70
|
+
farther.
|
|
71
|
+
|
|
37
72
|
### 4b — WORKS, not just compiles (run-verification, mandatory)
|
|
38
73
|
"Compiles" ≠ "done". Before done it must RUN:
|
|
39
74
|
- New/changed command → invoke for real: `--help` returns 0 AND a minimal happy-path produces the
|
|
@@ -45,6 +80,34 @@ the sibling of `loop_journal`'s working memory for ATTEMPTS.
|
|
|
45
80
|
- Use `validate`/`smoke` if bound. **Front-end change → `web_verify`** (see web-evidence.md):
|
|
46
81
|
screenshot + trace as evidence. An item that compiles but was never run is PARTIAL.
|
|
47
82
|
|
|
83
|
+
### 4b' — Flow coverage gate for front/back/service workspaces
|
|
84
|
+
When a workspace contains frontend, backend, and services under the same root — or the task touches
|
|
85
|
+
any cross-surface user flow — run a structural flow audit before planning and again before done:
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
python3 scripts/flow_audit.py audit <root> --fail-on high --json > .orchestrator/flow-audit.json
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The audit builds a static map of UI actions, frontend HTTP calls, backend endpoints, and backend
|
|
92
|
+
service calls. It fails the default gate on objective high-confidence gaps:
|
|
93
|
+
|
|
94
|
+
- `frontend_call_without_backend_endpoint`: the UI/client calls an API path that no scanned backend
|
|
95
|
+
exposes.
|
|
96
|
+
- `backend_endpoint_stub`: an endpoint body still looks like TODO, `pass`, `NotImplemented`, 501, or
|
|
97
|
+
a thrown "not implemented" error.
|
|
98
|
+
|
|
99
|
+
Medium gaps are still work, not noise. They must be classified in the task anchor or promoted to an
|
|
100
|
+
AC before done: UI action with no observed backend call, backend endpoint with no observed frontend
|
|
101
|
+
caller, or backend local-looking service call with no local endpoint. If the AC promises backend
|
|
102
|
+
integration for a UI flow, run the stricter gate:
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
python3 scripts/flow_audit.py audit <root> --fail-on medium
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
The final evidence must include either `.orchestrator/flow-audit.json` or the human summary. A green
|
|
109
|
+
unit test is not enough when the flow graph still has an unclassified loose end.
|
|
110
|
+
|
|
48
111
|
### 4c — Adversarial verify for MEDIUM+ items (multi-vote)
|
|
49
112
|
Spawn 2–3 INDEPENDENT verifiers, each prompted to REFUTE the implementation AND check each AC.
|
|
50
113
|
Majority-refute → back to fix. TRIVIAL/SMALL keep single self-review. When `simplicio-review` is
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: simplicio-loop
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.16.0
|
|
4
4
|
Summary: The Universal Looping AI Orchestrator — a runtime-agnostic super-plugin (6 skills) that drains any queue of work end-to-end on any LLM/runtime.
|
|
5
5
|
Author-email: Wesley Simplicio <wesleybob4@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -41,6 +41,8 @@ simplicio_loop/_bundle/skills/simplicio-tasks/references/understand-anything-ada
|
|
|
41
41
|
simplicio_loop/_bundle/skills/simplicio-tasks/references/video-evidence.md
|
|
42
42
|
simplicio_loop/_bundle/skills/simplicio-tasks/references/web-evidence.md
|
|
43
43
|
tests/test_action_gate.py
|
|
44
|
+
tests/test_flow_audit.py
|
|
45
|
+
tests/test_impact_audit.py
|
|
44
46
|
tests/test_loop_e2e.py
|
|
45
47
|
tests/test_worker_selftests.py
|
|
46
48
|
tests/test_worker_smoke.py
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import subprocess
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
6
|
+
FLOW = os.path.join(REPO, "scripts", "flow_audit.py")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _write(path, text):
|
|
10
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
11
|
+
path.write_text(text.strip(), encoding="utf-8")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _run(args, cwd):
|
|
15
|
+
return subprocess.run([sys.executable, FLOW] + args, capture_output=True, text=True, cwd=cwd)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_flow_audit_fails_on_frontend_call_without_endpoint(tmp_path):
|
|
19
|
+
_write(tmp_path / "frontend" / "Checkout.tsx", """
|
|
20
|
+
export function Checkout() {
|
|
21
|
+
return <button onClick={() => fetch("/api/checkout", { method: "POST" })}>Pay</button>
|
|
22
|
+
}
|
|
23
|
+
""")
|
|
24
|
+
_write(tmp_path / "backend" / "routes.py", """
|
|
25
|
+
@app.get("/api/health")
|
|
26
|
+
def health():
|
|
27
|
+
return {"ok": True}
|
|
28
|
+
""")
|
|
29
|
+
|
|
30
|
+
r = _run(["audit", str(tmp_path), "--fail-on", "high"], cwd=REPO)
|
|
31
|
+
assert r.returncode == 1, r.stdout
|
|
32
|
+
assert "frontend_call_without_backend_endpoint" in r.stdout, r.stdout
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def test_flow_audit_detects_backend_stub(tmp_path):
|
|
36
|
+
_write(tmp_path / "frontend" / "Login.tsx", """
|
|
37
|
+
export function Login() {
|
|
38
|
+
return <button onClick={() => fetch("/api/login", { method: "POST" })}>Login</button>
|
|
39
|
+
}
|
|
40
|
+
""")
|
|
41
|
+
_write(tmp_path / "backend" / "routes.py", """
|
|
42
|
+
@app.post("/api/login")
|
|
43
|
+
def login():
|
|
44
|
+
raise NotImplementedError("TODO")
|
|
45
|
+
""")
|
|
46
|
+
|
|
47
|
+
r = _run(["audit", str(tmp_path), "--fail-on", "high"], cwd=REPO)
|
|
48
|
+
assert r.returncode == 1, r.stdout
|
|
49
|
+
assert "backend_endpoint_stub" in r.stdout, r.stdout
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def test_flow_audit_passes_matched_non_stub_flow(tmp_path):
|
|
53
|
+
_write(tmp_path / "frontend" / "Login.tsx", """
|
|
54
|
+
export function Login() {
|
|
55
|
+
return <button onClick={() => fetch("/api/login", { method: "POST" })}>Login</button>
|
|
56
|
+
}
|
|
57
|
+
""")
|
|
58
|
+
_write(tmp_path / "backend" / "routes.py", """
|
|
59
|
+
@app.post("/api/login")
|
|
60
|
+
def login():
|
|
61
|
+
return {"ok": True}
|
|
62
|
+
""")
|
|
63
|
+
|
|
64
|
+
r = _run(["audit", str(tmp_path), "--fail-on", "high"], cwd=REPO)
|
|
65
|
+
assert r.returncode == 0, r.stdout
|
|
66
|
+
assert "flow-audit: PASS" in r.stdout, r.stdout
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
if __name__ == "__main__":
|
|
70
|
+
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
71
|
+
from _selfrun import run_module
|
|
72
|
+
run_module(globals(), "test_flow_audit")
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import subprocess
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
7
|
+
IMPACT = os.path.join(REPO, "scripts", "impact_audit.py")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _write(path, text):
|
|
11
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
12
|
+
path.write_text(text.strip() + "\n", encoding="utf-8")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _run(args, cwd):
|
|
16
|
+
return subprocess.run([sys.executable, IMPACT] + args, capture_output=True, text=True, cwd=cwd)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_impact_audit_fails_on_transitive_reverse_dependency(tmp_path):
|
|
20
|
+
_write(tmp_path / "app" / "util.py", """
|
|
21
|
+
def helper():
|
|
22
|
+
return 1
|
|
23
|
+
""")
|
|
24
|
+
_write(tmp_path / "app" / "service.py", """
|
|
25
|
+
from .util import helper
|
|
26
|
+
|
|
27
|
+
def compute():
|
|
28
|
+
return helper()
|
|
29
|
+
""")
|
|
30
|
+
_write(tmp_path / "app" / "controller.py", """
|
|
31
|
+
from app.service import compute
|
|
32
|
+
|
|
33
|
+
def run():
|
|
34
|
+
return compute()
|
|
35
|
+
""")
|
|
36
|
+
|
|
37
|
+
r = _run(["audit", str(tmp_path), "--file", "app/util.py", "--cover", "app/util.py"], cwd=REPO)
|
|
38
|
+
assert r.returncode == 1, r.stdout
|
|
39
|
+
assert "app/service.py" in r.stdout, r.stdout
|
|
40
|
+
assert "app/controller.py" in r.stdout, r.stdout
|
|
41
|
+
assert "uncovered_reverse_dependency" in r.stdout, r.stdout
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_impact_audit_passes_when_callers_and_tests_are_covered(tmp_path):
|
|
45
|
+
_write(tmp_path / "app" / "util.py", """
|
|
46
|
+
def helper():
|
|
47
|
+
return 1
|
|
48
|
+
""")
|
|
49
|
+
_write(tmp_path / "app" / "service.py", """
|
|
50
|
+
from .util import helper
|
|
51
|
+
|
|
52
|
+
def compute():
|
|
53
|
+
return helper()
|
|
54
|
+
""")
|
|
55
|
+
_write(tmp_path / "tests" / "test_service.py", """
|
|
56
|
+
from app.service import compute
|
|
57
|
+
|
|
58
|
+
def test_compute():
|
|
59
|
+
assert compute() == 1
|
|
60
|
+
""")
|
|
61
|
+
|
|
62
|
+
r = _run(
|
|
63
|
+
[
|
|
64
|
+
"audit",
|
|
65
|
+
str(tmp_path),
|
|
66
|
+
"--file",
|
|
67
|
+
"app/util.py",
|
|
68
|
+
"--cover",
|
|
69
|
+
"app/util.py",
|
|
70
|
+
"--cover",
|
|
71
|
+
"app/service.py",
|
|
72
|
+
"--cover",
|
|
73
|
+
"tests/test_service.py",
|
|
74
|
+
],
|
|
75
|
+
cwd=REPO,
|
|
76
|
+
)
|
|
77
|
+
assert r.returncode == 0, r.stdout
|
|
78
|
+
assert "impact-audit: PASS" in r.stdout, r.stdout
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def test_impact_audit_json_ok_tracks_fail_threshold(tmp_path):
|
|
82
|
+
_write(tmp_path / "app" / "service.py", """
|
|
83
|
+
from .util import helper
|
|
84
|
+
|
|
85
|
+
def compute():
|
|
86
|
+
return helper()
|
|
87
|
+
""")
|
|
88
|
+
_write(tmp_path / "app" / "util.py", """
|
|
89
|
+
def helper():
|
|
90
|
+
return 1
|
|
91
|
+
""")
|
|
92
|
+
|
|
93
|
+
r = _run(
|
|
94
|
+
[
|
|
95
|
+
"audit",
|
|
96
|
+
str(tmp_path),
|
|
97
|
+
"--file",
|
|
98
|
+
"app/service.py",
|
|
99
|
+
"--cover",
|
|
100
|
+
"app/service.py",
|
|
101
|
+
"--fail-on",
|
|
102
|
+
"medium",
|
|
103
|
+
"--json",
|
|
104
|
+
],
|
|
105
|
+
cwd=REPO,
|
|
106
|
+
)
|
|
107
|
+
assert r.returncode == 1, r.stdout
|
|
108
|
+
payload = json.loads(r.stdout)
|
|
109
|
+
assert payload["fail_on"] == "medium"
|
|
110
|
+
assert payload["ok"] is False
|
|
111
|
+
assert payload["counts"]["blocking_issues"] >= 1
|
|
112
|
+
assert any(issue["code"] == "uncovered_local_dependency" for issue in payload["blocking_issues"])
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def test_impact_audit_without_seed_is_blocked(tmp_path):
|
|
116
|
+
r = _run(["audit", str(tmp_path)], cwd=REPO)
|
|
117
|
+
assert r.returncode == 2, r.stdout
|
|
118
|
+
assert "BLOCKED" in r.stdout, r.stdout
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
if __name__ == "__main__":
|
|
122
|
+
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
123
|
+
from _selfrun import run_module
|
|
124
|
+
|
|
125
|
+
run_module(globals(), "test_impact_audit")
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"""Aggregate the deterministic `selftest` of every worker that ships one.
|
|
2
2
|
|
|
3
|
-
Each worker
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Each worker with a model-free `selftest` proves its own contract with no external services. This
|
|
4
|
+
runs them as subprocesses and asserts exit 0 + a PASS line — so `python3 scripts/check.py`
|
|
5
|
+
(or pytest) re-proves them on every change.
|
|
6
6
|
"""
|
|
7
7
|
import os
|
|
8
8
|
import subprocess
|
|
@@ -17,6 +17,8 @@ SELFTESTS = [
|
|
|
17
17
|
("scripts/savings_harness.py", "selftest"),
|
|
18
18
|
("scripts/task_anchor.py", "selftest"),
|
|
19
19
|
("scripts/pr_evidence.py", "selftest"),
|
|
20
|
+
("scripts/flow_audit.py", "selftest"),
|
|
21
|
+
("scripts/impact_audit.py", "selftest"),
|
|
20
22
|
]
|
|
21
23
|
|
|
22
24
|
|
|
@@ -56,6 +58,18 @@ def test_pr_evidence_selftest():
|
|
|
56
58
|
assert "PASS" in r.stdout, r.stdout
|
|
57
59
|
|
|
58
60
|
|
|
61
|
+
def test_flow_audit_selftest():
|
|
62
|
+
r = _run("scripts/flow_audit.py", "selftest")
|
|
63
|
+
assert r.returncode == 0, "flow_audit selftest failed:\n%s%s" % (r.stdout, r.stderr)
|
|
64
|
+
assert "PASS" in r.stdout, r.stdout
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def test_impact_audit_selftest():
|
|
68
|
+
r = _run("scripts/impact_audit.py", "selftest")
|
|
69
|
+
assert r.returncode == 0, "impact_audit selftest failed:\n%s%s" % (r.stdout, r.stderr)
|
|
70
|
+
assert "PASS" in r.stdout, r.stdout
|
|
71
|
+
|
|
72
|
+
|
|
59
73
|
if __name__ == "__main__":
|
|
60
74
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
61
75
|
from _selfrun import run_module
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop/_bundle/hooks/hooks.claude.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop/_bundle/hooks/loop_capture.py
RENAMED
|
File without changes
|
{simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop/_bundle/hooks/orient_clamp.py
RENAMED
|
File without changes
|
{simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop/_bundle/hooks/orient_rewrite.py
RENAMED
|
File without changes
|
{simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop/_bundle/hooks/simplicio_dashboard.py
RENAMED
|
File without changes
|
{simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop/_bundle/hooks/simplicio_watch.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{simplicio_loop-3.14.1 → simplicio_loop-3.16.0}/simplicio_loop.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|