simplicio-loop 3.11.0__tar.gz → 3.14.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.11.0/simplicio_loop.egg-info → simplicio_loop-3.14.0}/PKG-INFO +1 -1
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/README.md +1 -1
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/pyproject.toml +1 -1
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/simplicio_loop/__init__.py +2 -2
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/simplicio_loop/_bundle/hooks/loop_capture.py +20 -1
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/simplicio_loop/_bundle/hooks/loop_stop.py +37 -3
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/simplicio_loop/_bundle/skills/simplicio-loop/SKILL.md +36 -15
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/simplicio_loop/_bundle/skills/simplicio-tasks/SKILL.md +27 -3
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/simplicio_loop/_bundle/skills/simplicio-tasks/references/extension-points.md +3 -3
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/simplicio_loop/_bundle/skills/simplicio-tasks/references/quality-safety-delivery.md +23 -0
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/simplicio_loop/_bundle/skills/simplicio-tasks/references/understand-anything-adapter.md +1 -1
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0/simplicio_loop.egg-info}/PKG-INFO +1 -1
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/tests/test_loop_e2e.py +36 -0
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/tests/test_worker_selftests.py +14 -0
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/tests/test_worker_smoke.py +69 -0
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/LICENSE +0 -0
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/MANIFEST.in +0 -0
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/PYPI.md +0 -0
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/setup.cfg +0 -0
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/simplicio_loop/_bundle/hooks/README.md +0 -0
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/simplicio_loop/_bundle/hooks/action_gate.py +0 -0
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/simplicio_loop/_bundle/hooks/hooks.claude.json +0 -0
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/simplicio_loop/_bundle/hooks/hooks.json +0 -0
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/simplicio_loop/_bundle/hooks/learn_stop.py +0 -0
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/simplicio_loop/_bundle/hooks/orient_clamp.py +0 -0
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/simplicio_loop/_bundle/hooks/orient_rewrite.py +0 -0
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/simplicio_loop/_bundle/hooks/simplicio_dashboard.py +0 -0
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/simplicio_loop/_bundle/hooks/simplicio_watch.py +0 -0
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/simplicio_loop/_bundle/skills/simplicio-compress/SKILL.md +0 -0
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/simplicio_loop/_bundle/skills/simplicio-learn/SKILL.md +0 -0
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/simplicio_loop/_bundle/skills/simplicio-orient/SKILL.md +0 -0
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/simplicio_loop/_bundle/skills/simplicio-review/SKILL.md +0 -0
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/simplicio_loop/_bundle/skills/simplicio-tasks/references/agentsview-adapter.md +0 -0
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/simplicio_loop/_bundle/skills/simplicio-tasks/references/azure-devops-adapter.md +0 -0
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/simplicio_loop/_bundle/skills/simplicio-tasks/references/lmcache-adapter.md +0 -0
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/simplicio_loop/_bundle/skills/simplicio-tasks/references/orchestration.md +0 -0
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/simplicio_loop/_bundle/skills/simplicio-tasks/references/standing-loop-247.md +0 -0
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/simplicio_loop/_bundle/skills/simplicio-tasks/references/token-capture.md +0 -0
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/simplicio_loop/_bundle/skills/simplicio-tasks/references/token-economy.md +0 -0
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/simplicio_loop/_bundle/skills/simplicio-tasks/references/video-evidence.md +0 -0
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/simplicio_loop/_bundle/skills/simplicio-tasks/references/web-evidence.md +0 -0
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/simplicio_loop/cli.py +0 -0
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/simplicio_loop.egg-info/SOURCES.txt +0 -0
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/simplicio_loop.egg-info/dependency_links.txt +0 -0
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/simplicio_loop.egg-info/entry_points.txt +0 -0
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/simplicio_loop.egg-info/requires.txt +0 -0
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/simplicio_loop.egg-info/top_level.txt +0 -0
- {simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/tests/test_action_gate.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: simplicio-loop
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.14.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
|
|
@@ -81,7 +81,7 @@ protocol on 11 runtimes**, and it does all of this with **aggressive, honest tok
|
|
|
81
81
|
|
|
82
82
|
---
|
|
83
83
|
|
|
84
|
-
## 📘 Official capability record
|
|
84
|
+
## 📘 Official capability record
|
|
85
85
|
|
|
86
86
|
The complete, official roster of what `simplicio-tasks` ships — every capability below is **real,
|
|
87
87
|
runnable, and tested** (`python3 scripts/check.py`: claims-audit 4/4 + 28 tests). Each links to its
|
|
@@ -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.14.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"
|
{simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/simplicio_loop/_bundle/hooks/loop_capture.py
RENAMED
|
@@ -18,6 +18,7 @@ LOOP_DIR = os.path.join(".orchestrator", "loop")
|
|
|
18
18
|
SCRATCHPAD = os.path.join(LOOP_DIR, "scratchpad.md")
|
|
19
19
|
DONE_FLAG = os.path.join(LOOP_DIR, "done")
|
|
20
20
|
LAST_RESP = os.path.join(LOOP_DIR, "last_response.txt")
|
|
21
|
+
ANCHOR = os.path.join(LOOP_DIR, "anchor.json")
|
|
21
22
|
|
|
22
23
|
PROMISE_RE = re.compile(r"<promise>\s*(.*?)\s*</promise>", re.IGNORECASE | re.DOTALL)
|
|
23
24
|
EVIDENCE_RE = re.compile(
|
|
@@ -26,6 +27,22 @@ EVIDENCE_RE = re.compile(
|
|
|
26
27
|
)
|
|
27
28
|
|
|
28
29
|
|
|
30
|
+
def anchor_pending():
|
|
31
|
+
"""Unverified acceptance-criteria ids from the task anchor, or [] (fail-open).
|
|
32
|
+
|
|
33
|
+
Mirror of loop_stop.anchor_pending: the done flag is raised only when every anchored AC is
|
|
34
|
+
`done`. Reads the anchor JSON directly (no dependency on scripts/, which the lean plugin omits);
|
|
35
|
+
a missing/unreadable/empty anchor returns [] so it never blocks completion.
|
|
36
|
+
"""
|
|
37
|
+
try:
|
|
38
|
+
with open(ANCHOR, encoding="utf-8") as f:
|
|
39
|
+
data = json.load(f)
|
|
40
|
+
return [c.get("id") for c in (data.get("criteria") or [])
|
|
41
|
+
if isinstance(c, dict) and c.get("status") != "done"]
|
|
42
|
+
except Exception:
|
|
43
|
+
return []
|
|
44
|
+
|
|
45
|
+
|
|
29
46
|
def main():
|
|
30
47
|
try:
|
|
31
48
|
raw = sys.stdin.read()
|
|
@@ -53,7 +70,9 @@ def main():
|
|
|
53
70
|
evidence_required = "evidence_required: false" not in content.lower()
|
|
54
71
|
m = PROMISE_RE.search(resp)
|
|
55
72
|
if m and m.group(1).strip() == promise:
|
|
56
|
-
|
|
73
|
+
# Evidence-gated AND anchor-gated: raise `done` only with in-turn evidence and no
|
|
74
|
+
# acceptance criterion still open in the task anchor (mechanical anti-drift).
|
|
75
|
+
if ((not evidence_required) or EVIDENCE_RE.search(resp)) and not anchor_pending():
|
|
57
76
|
try:
|
|
58
77
|
open(DONE_FLAG, "w").close() # raise the flag; stop hook acts
|
|
59
78
|
except OSError:
|
|
@@ -21,6 +21,7 @@ LOOP_DIR = os.path.join(".orchestrator", "loop")
|
|
|
21
21
|
SCRATCHPAD = os.path.join(LOOP_DIR, "scratchpad.md")
|
|
22
22
|
DONE_FLAG = os.path.join(LOOP_DIR, "done")
|
|
23
23
|
LAST_RESP = os.path.join(LOOP_DIR, "last_response.txt")
|
|
24
|
+
ANCHOR = os.path.join(LOOP_DIR, "anchor.json")
|
|
24
25
|
STOP_SIGNAL = os.path.join(".orchestrator", "STOP")
|
|
25
26
|
BUDGET = os.path.join(".orchestrator", "loop-budget.json")
|
|
26
27
|
GATE_LOCK = os.path.join(LOOP_DIR, "gate.lock")
|
|
@@ -127,6 +128,27 @@ def gate_running():
|
|
|
127
128
|
return False
|
|
128
129
|
|
|
129
130
|
|
|
131
|
+
def anchor_pending():
|
|
132
|
+
"""Return the unverified acceptance-criteria ids from the task anchor, or [].
|
|
133
|
+
|
|
134
|
+
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`. Read the anchor JSON DIRECTLY (no dependency on
|
|
136
|
+
`scripts/task_anchor.py`, which the lean marketplace plugin does not ship — the hook must stay
|
|
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.
|
|
141
|
+
"""
|
|
142
|
+
try:
|
|
143
|
+
with open(ANCHOR, encoding="utf-8") as f:
|
|
144
|
+
data = json.load(f)
|
|
145
|
+
crit = data.get("criteria") or []
|
|
146
|
+
return [c.get("id") for c in crit
|
|
147
|
+
if isinstance(c, dict) and c.get("status") != "done"]
|
|
148
|
+
except Exception:
|
|
149
|
+
return [] # fail-open: anchor unreadable ≠ trap
|
|
150
|
+
|
|
151
|
+
|
|
130
152
|
def budget_halted():
|
|
131
153
|
try:
|
|
132
154
|
if not os.path.exists(BUDGET):
|
|
@@ -189,9 +211,12 @@ def main():
|
|
|
189
211
|
m = PROMISE_RE.search(resp)
|
|
190
212
|
if m and m.group(1).strip() == promise.strip():
|
|
191
213
|
has_evidence = bool(EVIDENCE_RE.search(resp))
|
|
192
|
-
|
|
214
|
+
# The promise is honored only with evidence AND no acceptance criterion still open
|
|
215
|
+
# in the task anchor — the mechanical anti-drift gate. Pending ACs ⇒ ignore the
|
|
216
|
+
# promise and keep looping (still bounded by max_iter), never a false "done".
|
|
217
|
+
if ((not evidence_required) or has_evidence) and not anchor_pending():
|
|
193
218
|
cleanup_and_stop() # (3) promise fulfilled → stop
|
|
194
|
-
# promise without evidence → ignore, keep looping
|
|
219
|
+
# promise without evidence, or anchor still has open ACs → ignore, keep looping
|
|
195
220
|
# (3') Cursor capture may have raised the flag.
|
|
196
221
|
if os.path.exists(DONE_FLAG):
|
|
197
222
|
cleanup_and_stop()
|
|
@@ -219,7 +244,16 @@ def main():
|
|
|
219
244
|
if promise
|
|
220
245
|
else ""
|
|
221
246
|
)
|
|
222
|
-
|
|
247
|
+
# Surface the still-open acceptance criteria so the next turn knows exactly what blocks
|
|
248
|
+
# "done" — the anchor gate is why a promise would be ignored, so name the gap.
|
|
249
|
+
pending = anchor_pending()
|
|
250
|
+
ac_hint = (
|
|
251
|
+
" Open acceptance criteria (verify each before the promise): %s."
|
|
252
|
+
% ", ".join(p for p in pending if p)
|
|
253
|
+
if pending
|
|
254
|
+
else ""
|
|
255
|
+
)
|
|
256
|
+
header = "[simplicio-loop iteration %d.%s%s]" % (nxt, promise_hint, ac_hint)
|
|
223
257
|
emit_refeed(header + "\n\n" + (body or ""))
|
|
224
258
|
except Exception:
|
|
225
259
|
allow_stop() # fail-open, always
|
|
@@ -49,25 +49,37 @@ hard dependencies of the `simplicio-loop` package (`pip install simplicio-loop`
|
|
|
49
49
|
|
|
50
50
|
| Operator | CLI (binary) | Binds | Role in the loop |
|
|
51
51
|
|---|---|---|---|
|
|
52
|
-
| **simplicio-mapper** | `simplicio-mapper` | `orient` / `recall` | **Survey** — maps the repo(s) into `.simplicio/*.json` (project-map, precedent-index, symbol-index, call-graph, docs). This survey, not an ad-hoc LLM read, is what feeds the goal each turn. |
|
|
52
|
+
| **simplicio-mapper** | `simplicio-mapper` | `orient` / `recall` | **Survey** — maps the repo(s) into `.simplicio/*.json` (project-map, precedent-index, symbol-index, call-graph, docs). Two-tier (v0.9+): `macro` is an instant shallow skeleton (no content reads), `scan` returns that skeleton now and runs the deep index in the background, `status` reports the deep-pass phase. This survey, not an ad-hoc LLM read, is what feeds the goal each turn. |
|
|
53
53
|
| **simplicio-dev-cli** | `simplicio-dev-cli` | `execute` / `deterministic_edit` / `validate` / `diagnostics` | **Operate** — applies a DECIDED change through its 6-layer contract (mapper context → precedent → prompt → diff → test → verify, ≤3 retries). The CLI edits and verifies; the AI does not hand-write the diff. |
|
|
54
54
|
|
|
55
|
-
**Preflight (MANDATORY, BLOCKING).** Before iteration 1,
|
|
55
|
+
**Preflight (MANDATORY, BLOCKING).** Before iteration 1, auto-update both operators to their latest
|
|
56
|
+
release (so every run uses the newest `simplicio-mapper`/`simplicio-cli`), then confirm both are on
|
|
57
|
+
PATH:
|
|
56
58
|
```bash
|
|
57
|
-
|
|
59
|
+
# Always run the loop on the latest operators. FAIL-OPEN: offline / no-pip / a pin keeps the
|
|
60
|
+
# currently-installed build; this never blocks. Runs ONCE per loop preflight, not per turn.
|
|
61
|
+
python3 -m pip install -qU simplicio-mapper simplicio-cli 2>/dev/null \
|
|
62
|
+
|| python3 -m pip install -qU --user --break-system-packages simplicio-mapper simplicio-cli 2>/dev/null || true
|
|
63
|
+
simplicio-mapper --version # survey operator (now latest)
|
|
58
64
|
simplicio-dev-cli --help # action operator (pkg simplicio-cli; exposes `simplicio-dev-cli`)
|
|
59
65
|
```
|
|
60
|
-
The
|
|
66
|
+
The auto-update is best-effort and offline-safe — a network/pip failure leaves the working version
|
|
67
|
+
in place and the loop proceeds. The action binary is `simplicio-dev-cli` (from `pip install simplicio-cli`) — NOT the bare
|
|
61
68
|
`simplicio`, which is reserved for the separate `simplicio-runtime` and is not what this loop
|
|
62
69
|
binds. `simplicio-dev-cli` has no `--version` subcommand; `--help` exiting 0 is the readiness
|
|
63
70
|
proof. If either operator is missing, do NOT fall back to LLM survey/editing — STOP and emit
|
|
64
71
|
`simplicio-loop: BLOCKED — missing operator <name>; run: pip install simplicio-loop` (the install
|
|
65
72
|
re-pulls `simplicio-mapper` + `simplicio-cli`). This requirement is scoped to the loop drive.
|
|
66
73
|
|
|
67
|
-
**Survey step (each loop start + on any structural change).**
|
|
68
|
-
`simplicio-mapper
|
|
69
|
-
the
|
|
70
|
-
|
|
74
|
+
**Survey step (each loop start + on any structural change).** Prefer the two-tier flow (v0.9+):
|
|
75
|
+
`simplicio-mapper scan . --json` returns an instant `macro` skeleton AND kicks the deep index off in
|
|
76
|
+
the background — the loop starts working immediately instead of blocking on a full crawl. Poll
|
|
77
|
+
`simplicio-mapper status . --json` (`phase`: `deep_running` → terminal) before relying on the deep
|
|
78
|
+
artifacts; pass `--await [--timeout <s>]` to block until terminal, or `scan --sync` (forced when
|
|
79
|
+
`CI=true`) for the old single-shot behavior. `simplicio-mapper index . --json` (add `--watch` for
|
|
80
|
+
long runs) remains the synchronous full (re)build of `.simplicio/`. Read the survey artifacts —
|
|
81
|
+
never re-scan the tree by hand when a fresh map exists. For a multi-repo survey, run the mapper per
|
|
82
|
+
repo root and aggregate the JSON.
|
|
71
83
|
|
|
72
84
|
**Operate step (every turn that mutates code).** Once the AC and the change are DECIDED, delegate
|
|
73
85
|
the mutation to the operator, one decided change at a time:
|
|
@@ -84,9 +96,9 @@ merge/close gates); the operators do survey + apply:
|
|
|
84
96
|
|
|
85
97
|
| Phase | Operator | Command |
|
|
86
98
|
|---|---|---|
|
|
87
|
-
| Preflight (before iteration 1) | both | `simplicio-mapper --version` · `simplicio-dev-cli --help` → BLOCK if missing |
|
|
88
|
-
| Survey (loop start; multi-repo: per root) | mapper | `simplicio-mapper
|
|
89
|
-
| Loop contract step 2 — Triage (every turn) | mapper | re-read `.simplicio/*.json`; `simplicio-mapper
|
|
99
|
+
| Preflight (before iteration 1) | both | `python3 -m pip install -qU simplicio-mapper simplicio-cli` (auto-update to latest, fail-open) → `simplicio-mapper --version` · `simplicio-dev-cli --help` → BLOCK if missing |
|
|
100
|
+
| Survey (loop start; multi-repo: per root) | mapper | `simplicio-mapper scan . --json` (instant macro + deep index in background; `--sync`/`--await` to block) → `.simplicio/*.json`. `index . --json` for a forced synchronous build |
|
|
101
|
+
| Loop contract step 2 — Triage (every turn) | mapper | re-read `.simplicio/*.json`; `simplicio-mapper macro . --json` for an instant skeleton, or `scan`/`status` to refresh if the tree changed |
|
|
90
102
|
| Loop contract step 3 — Work the goal | dev-cli | `simplicio-dev-cli task "<decided change>" --target <file> [--json]` |
|
|
91
103
|
| Evidence-gated `<promise>` / `simplicio-tasks` Step 4b | dev-cli | the operator's passing test+verify pass = in-turn evidence |
|
|
92
104
|
|
|
@@ -151,14 +163,19 @@ detector below. It is the difference between a loop that converges and one that
|
|
|
151
163
|
`.orchestrator/loop-budget.json` $ kill-switch (see `simplicio-tasks` Step 1a/7).
|
|
152
164
|
2. **Triage the live state FIRST (mandatory).** Before any action each turn, re-read the ground
|
|
153
165
|
truth — the **`simplicio-mapper` survey** (`.simplicio/*.json`; refresh it with
|
|
154
|
-
`simplicio-mapper
|
|
166
|
+
`simplicio-mapper macro . --json` for an instant skeleton or `scan . --json` if the tree changed),
|
|
167
|
+
`git status`/`git diff`, the working
|
|
155
168
|
tree, the scratchpad notes, AND the source of record (re-query the open issues/PRs, existing
|
|
156
169
|
branches, the `.orchestrator/loop/done` flag). **Also read the attempt memory FIRST**:
|
|
157
170
|
`python3 scripts/loop_journal.py resume` — it lists what was already tried and the dead-end
|
|
158
171
|
actions to AVOID, so the turn never re-runs a known-failing approach. For **incremental triage**
|
|
159
172
|
(don't re-scan the whole tree every turn), `loop_journal.py since` shows only the delta since the
|
|
160
|
-
last recorded turn's commit.
|
|
161
|
-
|
|
173
|
+
last recorded turn's commit. **And re-read the task anchor** — `python3 scripts/task_anchor.py
|
|
174
|
+
check --goal "<the goal worked this turn>" --exit-code` — so the turn stays on the SAME frozen
|
|
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. The journal is the loop's memory for
|
|
177
|
+
ATTEMPTS; the anchor is its memory for SCOPE. Act only on what is still genuinely open; never redo
|
|
178
|
+
done work or act on a stale picture (idempotency).
|
|
162
179
|
3. **Work the goal** each turn as if fresh, against that triaged state. The model DECIDES the
|
|
163
180
|
AC-scoped change; the **`simplicio-dev-cli` operator APPLIES and verifies it**
|
|
164
181
|
(`simplicio-dev-cli task "<change>" --target <file>`) — do not hand-edit inside the loop. End EVERY
|
|
@@ -245,7 +262,11 @@ only if, in the SAME turn, there is concrete evidence the work is truly done:
|
|
|
245
262
|
|
|
246
263
|
- the run-verification gate passed ("works, not just compiles" — `simplicio-tasks` Step 4b) —
|
|
247
264
|
the `simplicio-dev-cli` operator's passing test+verify pass (its contract step 5/6) satisfies this, or
|
|
248
|
-
- the named acceptance criteria are each checked with a `file:line` or command-output receipt
|
|
265
|
+
- the named acceptance criteria are each checked with a `file:line` or command-output receipt —
|
|
266
|
+
mechanically enforced by the task anchor: `python3 scripts/task_anchor.py gate --exit-code` must
|
|
267
|
+
return READY (every anchored AC `done` with a receipt; exit 12 = still pending) before the promise
|
|
268
|
+
is allowed. An anchor with pending criteria makes the `<promise>` a contract violation, exactly
|
|
269
|
+
like missing evidence, or
|
|
249
270
|
- for a queue, the source re-query confirms the items are actually closed/merged, or
|
|
250
271
|
- a **demo video** of the change running on screen — a deterministic MP4 rendered with
|
|
251
272
|
**hyperframes** via the `video_evidence` producer (below) — whose ledger row + MP4 path prove
|
|
@@ -164,7 +164,11 @@ List candidates by METADATA only; normalize to the canonical schema; dedup by so
|
|
|
164
164
|
normalized-title + fingerprint AND by existing branch/PR (idempotency). Before implementing an
|
|
165
165
|
item, do the MANDATORY deep intake: read full body + ALL comments, extract acceptance criteria
|
|
166
166
|
(an obvious-but-missing AC is a BLOCKER — ask once), orient the existing code (signatures-only
|
|
167
|
-
reads for API surface), then write a short plan with an AC checklist + complexity.
|
|
167
|
+
reads for API surface), then write a short plan with an AC checklist + complexity. **FREEZE the
|
|
168
|
+
acceptance criteria as the task anchor** (`python3 scripts/task_anchor.py set --item <id> --goal
|
|
169
|
+
"<verbatim>" --ac "<AC>" …`) — this is the loop's memory for SCOPE (sibling to `loop_journal`'s
|
|
170
|
+
memory for ATTEMPTS): it is what every later turn re-checks so the run cannot silently narrow or
|
|
171
|
+
wander off the task (the "desvio de tarefas" fix, Step 4 drift guard). Detail:
|
|
168
172
|
`references/orchestration.md`.
|
|
169
173
|
|
|
170
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.
|
|
@@ -203,8 +207,19 @@ Never mark done without green gates + evidence; a failure is NOT a blocker — i
|
|
|
203
207
|
re-trying the same approach: switch strategy, or escalate via the human gate (Step 5) with the
|
|
204
208
|
fingerprint + dead-ends. Start each turn with `loop_journal.py resume` to avoid known dead-ends.
|
|
205
209
|
Delegate to `simplicio-loop` when loaded (§ Run-journal + stall detector).
|
|
210
|
+
- **AC anchor + drift guard (anti-deviation).** Every turn, BEFORE acting, re-read the frozen
|
|
211
|
+
anchor and verify you are still on the SAME task: `python3 scripts/task_anchor.py check --goal
|
|
212
|
+
"<the goal you are working now>" --exit-code` (verdict `DRIFT` ⇒ exit 11 — the goal moved; STOP
|
|
213
|
+
and re-anchor explicitly with `--force`, never drift silently). As each AC is genuinely met,
|
|
214
|
+
record its receipt — `task_anchor.py mark --id ACk --status done --evidence "<file:line / cmd /
|
|
215
|
+
screenshot>"` (a `done` with no evidence is REFUSED). The anchor is the runnable form of
|
|
216
|
+
"never narrow the task": it makes the orchestrator's working memory for SCOPE durable, exactly as
|
|
217
|
+
`loop_journal` does for ATTEMPTS.
|
|
206
218
|
- **4a AC gate (real DoD):** verify EVERY AC explicitly; no placeholder/stub success, no
|
|
207
|
-
`todo!()`/`panic!` in prod paths, reads from context, compiles clean on changed files.
|
|
219
|
+
`todo!()`/`panic!` in prod paths, reads from context, compiles clean on changed files. The gate
|
|
220
|
+
is mechanical: `python3 scripts/task_anchor.py gate --exit-code` (exit 12 = criteria still
|
|
221
|
+
pending) MUST pass before you declare done or open the PR — "done" requires every anchored AC
|
|
222
|
+
verified with a receipt.
|
|
208
223
|
- **4b WORKS, not just compiles:** RUN it (`--help` + happy path / affected tests). Front-end
|
|
209
224
|
change → `web_verify` (screenshot + trace, `references/web-evidence.md`). For moving proof of a UI
|
|
210
225
|
change, `video_evidence verify --url <url>` records the **real session with Playwright** (default
|
|
@@ -234,7 +249,16 @@ Never mark done without green gates + evidence; a failure is NOT a blocker — i
|
|
|
234
249
|
|
|
235
250
|
## Step 6 — Deliver + close + self-audit · Step 6b — Feedback loop
|
|
236
251
|
Per completed item: commit (Conventional Commits, English), push, Draft PR, close in-source with a
|
|
237
|
-
short evidence comment (PR link + verification).
|
|
252
|
+
short evidence comment (PR link + verification). **Assemble the PR body mechanically so it ALWAYS
|
|
253
|
+
carries prints + an item-by-item AC check** — `python3 scripts/pr_evidence.py build --item <id>
|
|
254
|
+
--title "<t>" --summary "<s>" --require-evidence --out .orchestrator/pr_body.md` pulls the
|
|
255
|
+
item-by-item acceptance-criteria checklist from the task anchor (Step 2b/4) AND embeds the
|
|
256
|
+
screenshots/recordings captured by `web_verify`/`video_evidence` under `.orchestrator/tee/web`; with
|
|
257
|
+
`--require-evidence` it EXITS 3 (blocked) rather than open a PR with no prints and no checklist (the
|
|
258
|
+
"PR sem evidência" fix). It honors the discovered `.github/PULL_REQUEST_TEMPLATE.md` (the
|
|
259
|
+
`pr_template` extension point) — appending the checklist + prints under the maintainer's sections —
|
|
260
|
+
and `pr_evidence.py comment --item <id> --pr <N>` emits the matching in-source evidence comment.
|
|
261
|
+
**Verify reality, never trust self-report** — the
|
|
238
262
|
final step re-runs the merged build/test + smoke + a source re-query; the run's status = that
|
|
239
263
|
measured state. Then self-audit (score, fix P0/P1, converge). Pursue the feedback loop until
|
|
240
264
|
merge-ready: CI fail → fix root cause; review comments → adjust; branch behind main → additive
|
|
@@ -36,10 +36,10 @@ fallback with standard tools. The skill depends on the ABSTRACTION, never on a s
|
|
|
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 |
|
|
39
|
-
| `delivery_gate` | One DoD gate: AC check + run-verification + regression guard + diff self-review + delivery certificate | LLM walks the AC checklist, runs affected tests, reviews own diff, writes a certificate into the receipt |
|
|
39
|
+
| `delivery_gate` | One DoD gate: AC check + run-verification + regression guard + diff self-review + delivery certificate. Runnable form: the **task anchor** (`scripts/task_anchor.py`) freezes the ACs at intake, `check` flags goal-drift each turn (anti-deviation), `mark` records a per-AC receipt, and `gate` (exit 12) BLOCKS "done"/PR-open while any AC is unverified. | LLM walks the AC checklist, runs affected tests, reviews own diff, writes a certificate into the receipt |
|
|
40
40
|
| `action_gate` | Risk-classify every mutation (safe/auto/ask) vs allow/deny + hardline blocklist before it runs | LLM pattern-matches action vs irreversible-op list, secret-scans, proceeds/auto-runs/escalates to `human_gate` |
|
|
41
41
|
| `repo_conventions` | LEARN the repo's own playbook, not just what's documented. Worker `scripts/repo_conventions.py learn` mines the git history (branch-name scheme, commit convention + REAL scope list, ticket pattern — by frequency) + merged PRs via `gh` (title pattern, label vocab, PR-body sections) + static config (CONTRIBUTING.md/AGENTS.md/pyproject.toml for a Conventional-Commits/commitizen hint, and the PR template for body-section structure) → one hash-pinned `.orchestrator/conventions.json` (`source=history\|config\|default`, confidence-gated: sparse history degrades to an honest default, never an over-fit guess). Steps 4–6 apply it deterministically via `repo_conventions.py branch`/`commit`. PR bodies are untrusted data; a learned convention never overrides a safety gate. | LLM reads CONTRIBUTING.md + AGENTS.md + .github/ + pyproject.toml AND skims `git log`/`gh pr list` for the dominant branch/commit/PR pattern, emits the same structured conventions summary that shapes Step 4–6 (branch naming, commit scopes, PR checklist, CI/lint/test gates). |
|
|
42
|
-
| `pr_template` | Discover .github/PULL_REQUEST_TEMPLATE.md, parse structured sections (what/why, how to test, checklist), map completed ACs to checklist items, and auto-fill the PR body before creation. Ensures every PR matches the maintainer's expected format on first submission. | LLM reads .github/PULL_REQUEST_TEMPLATE.md, maps completed acceptance criteria to each checklist item, fills in what/why from the item description and the implementation summary, lists changed files with rationale. |
|
|
42
|
+
| `pr_template` | Discover .github/PULL_REQUEST_TEMPLATE.md, parse structured sections (what/why, how to test, checklist), map completed ACs to checklist items, and auto-fill the PR body before creation. Ensures every PR matches the maintainer's expected format on first submission. Runnable form: `scripts/pr_evidence.py build --require-evidence` assembles the body with the **item-by-item AC checklist** (from the task anchor) + the **prints/recordings** captured under `.orchestrator/tee/web`, honoring the discovered template; it FAILS CLOSED (exit 3) rather than open an evidence-less PR. | LLM reads .github/PULL_REQUEST_TEMPLATE.md, maps completed acceptance criteria to each checklist item, fills in what/why from the item description and the implementation summary, lists changed files with rationale. |
|
|
43
43
|
| `reuse_precedent` | Match item by fingerprint to a prior SOLVED run → reuse not regenerate → ingest the new solution back | LLM greps past PRs/closed issues/solved-patterns journal for the fingerprint, applies it, appends new solution |
|
|
44
44
|
| `sibling_search` | Given a changed file/function/pattern, find all other call sites or implementations with the same pattern across the repo. Enumerate sibling paths so the fix covers the entire bug class, not just one reported instance. Critical for projects where maintainers reject single-site fixes. | LLM uses `grep -rn` / `rg` for the symbol/pattern, traces each candidate with `git log -p -S`, reads each sibling to confirm the same bug class, then includes all confirmed siblings in the fix scope. |
|
|
45
45
|
| `source_adapter` | Uniform source connector contract (list_ready/get_details/claim/update/attach/close) bound per source | LLM calls the source CLI/REST per verb; lockfile/label claim with TTL for cross-session safety |
|
|
@@ -66,7 +66,7 @@ them to two installed CLIs (hard deps of `pip install simplicio-loop`) and BLOCK
|
|
|
66
66
|
|
|
67
67
|
| Point(s) | Bound CLI | What replaces the LLM fallback |
|
|
68
68
|
|---|---|---|
|
|
69
|
-
| `orient` / `recall` | `simplicio-mapper` (`simplicio-mapper index . --json`) | the repo SURVEY — `.simplicio/*.json` (project-map, precedent-index, symbol-index, call-graph) instead of ad-hoc LLM reads |
|
|
69
|
+
| `orient` / `recall` | `simplicio-mapper` (`simplicio-mapper scan . --json`; `macro` for an instant skeleton, `status` for the deep-pass phase, `index . --json` for a forced synchronous build) | the repo SURVEY — `.simplicio/*.json` (project-map, precedent-index, symbol-index, call-graph) instead of ad-hoc LLM reads |
|
|
70
70
|
| `execute` / `deterministic_edit` / `validate` / `diagnostics` | `simplicio-dev-cli task` (binary `simplicio-dev-cli`, pkg `simplicio-cli`) | the OPERATOR — applies a decided change via its 6-layer contract (mapper→precedent→prompt→diff→test→verify, ≤3 retries); the AI never hand-writes the diff inside the loop |
|
|
71
71
|
|
|
72
72
|
This is the one place the abstraction is realized by a REQUIRED binding rather than an optional
|
|
@@ -24,6 +24,16 @@ DoD per item:
|
|
|
24
24
|
```
|
|
25
25
|
Done only when fully green. "N/A" on a real AC → mark `partial`, note what's missing.
|
|
26
26
|
|
|
27
|
+
**Anchor the ACs — don't re-derive them (anti-deviation).** The acceptance criteria are frozen
|
|
28
|
+
ONCE at intake as the task anchor (`task_anchor.py set`, Step 2b) and re-checked every turn so the
|
|
29
|
+
run cannot silently narrow or wander off the task. Per turn: `task_anchor.py check --goal "<goal
|
|
30
|
+
worked now>" --exit-code` (verdict `DRIFT`/exit 11 ⇒ the goal moved — STOP, re-anchor with `--force`
|
|
31
|
+
only if the task genuinely changed). As each AC is met: `task_anchor.py mark --id ACk --status done
|
|
32
|
+
--evidence "<file:line / command output / screenshot path>"` (a `done` with no receipt is REFUSED).
|
|
33
|
+
The DoD gate is then mechanical: `task_anchor.py gate --exit-code` (exit 12 = criteria still
|
|
34
|
+
pending) MUST pass before "done" or PR-open. This is the loop's durable working memory for SCOPE,
|
|
35
|
+
the sibling of `loop_journal`'s working memory for ATTEMPTS.
|
|
36
|
+
|
|
27
37
|
### 4b — WORKS, not just compiles (run-verification, mandatory)
|
|
28
38
|
"Compiles" ≠ "done". Before done it must RUN:
|
|
29
39
|
- New/changed command → invoke for real: `--help` returns 0 AND a minimal happy-path produces the
|
|
@@ -91,6 +101,19 @@ sections + label vocabulary. Then push, Draft PR, close the item in its source w
|
|
|
91
101
|
comment (PR link + verification summary). When the profile is `source=default` (no clear repo
|
|
92
102
|
history), fall back to Conventional Commits and say so.
|
|
93
103
|
|
|
104
|
+
**Every PR carries prints + an item-by-item AC check (the `pr_evidence` worker).** Do NOT hand-write
|
|
105
|
+
the PR body and risk forgetting the proof — assemble it mechanically:
|
|
106
|
+
`python3 scripts/pr_evidence.py build --item <id> --title "<t>" --summary "<s>" --require-evidence
|
|
107
|
+
--out .orchestrator/pr_body.md`. It pulls the item-by-item checklist from the task anchor (one line
|
|
108
|
+
per AC, with its status + the receipt that verified it) AND embeds every screenshot/recording
|
|
109
|
+
captured by `web_verify`/`video_evidence` under `.orchestrator/tee/web`. With `--require-evidence`
|
|
110
|
+
it FAILS CLOSED — exit 3 (`blocked`), never a body — when there is neither a checklist nor a single
|
|
111
|
+
print, so an evidence-less PR cannot be opened by accident. It honors a discovered
|
|
112
|
+
`.github/PULL_REQUEST_TEMPLATE.md` (keeps the maintainer's sections, appends the checklist + prints
|
|
113
|
+
below). `pr_evidence.py comment --item <id> --pr <N>` emits the matching in-source evidence comment
|
|
114
|
+
(PR link + per-AC check + a count of attached prints). Write surrounding comment PROSE in the user's
|
|
115
|
+
language; keep paths/identifiers in English.
|
|
116
|
+
|
|
94
117
|
**Verify in the workflow, never trust self-report.** When a fan-out drove the run, its FINAL step
|
|
95
118
|
re-verifies reality: the merged build/test, the `smoke` gate, and a source re-query confirming
|
|
96
119
|
items are actually closed. The run's status = that measured state, not the sum of agent claims.
|
|
@@ -38,7 +38,7 @@ file — no LLM invocation, no re-scan. The full analysis is re-run only intenti
|
|
|
38
38
|
|
|
39
39
|
| Extension point | Default (`simplicio-mapper`) | Understand Anything adapter |
|
|
40
40
|
|---|---|---|
|
|
41
|
-
| `orient` | `simplicio-mapper index . --json` → `.simplicio/*.json` | Read `.understand-anything/knowledge-graph.json` + `jq` queries |
|
|
41
|
+
| `orient` | `simplicio-mapper scan . --json` (or `index . --json` synchronous) → `.simplicio/*.json` | Read `.understand-anything/knowledge-graph.json` + `jq` queries |
|
|
42
42
|
| `recall` | `.simplicio/precedent-index.json` (past resolutions) | Guided tours + semantic search over the graph + prior `/understand-diff` snapshots |
|
|
43
43
|
|
|
44
44
|
### When to use which
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: simplicio-loop
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.14.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
|
|
@@ -5,6 +5,8 @@ the loop stops on **evidence**, not on a bare promise, and not by accident. We d
|
|
|
5
5
|
|
|
6
6
|
• promise + evidence → STOP (state cleaned up) ← the success exit
|
|
7
7
|
• promise WITHOUT evidence → CONTINUE (re-feed, ignored) ← the anti-false-done guard
|
|
8
|
+
• promise + evidence, AC pending → CONTINUE (re-feed, ignored) ← the anti-DRIFT anchor gate
|
|
9
|
+
• promise + evidence, ACs done → STOP ← anchor satisfied
|
|
8
10
|
• no promise, under cap → CONTINUE (iteration bumped)
|
|
9
11
|
• iteration >= max_iterations → STOP by cap ← distinct from the evidence exit
|
|
10
12
|
• .orchestrator/STOP signal → STOP immediately
|
|
@@ -79,6 +81,40 @@ def test_bare_promise_without_evidence_continues(tmp_path):
|
|
|
79
81
|
assert _iteration(root) == 2
|
|
80
82
|
|
|
81
83
|
|
|
84
|
+
def _write_anchor(root, criteria):
|
|
85
|
+
loop = os.path.join(root, ".orchestrator", "loop")
|
|
86
|
+
os.makedirs(loop, exist_ok=True)
|
|
87
|
+
with open(os.path.join(loop, "anchor.json"), "w", encoding="utf-8") as f:
|
|
88
|
+
json.dump({"item": "1", "goal": "g", "goal_fp": "x", "criteria": criteria}, f)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def test_promise_with_evidence_but_pending_anchor_continues(tmp_path):
|
|
92
|
+
# The mechanical anti-drift gate: even WITH evidence, a promise must NOT stop the loop while the
|
|
93
|
+
# task anchor still has an unverified acceptance criterion — it re-feeds instead, naming the gap.
|
|
94
|
+
root = str(tmp_path)
|
|
95
|
+
_arm(root, iteration=1, max_iter=5)
|
|
96
|
+
_write_anchor(root, [{"id": "AC1", "status": "done"}, {"id": "AC2", "status": "pending"}])
|
|
97
|
+
r = _tick(root, "Looks done. <promise>SIMPLICIO_DONE</promise> tests pass ✓ "
|
|
98
|
+
"https://github.com/o/r/pull/9")
|
|
99
|
+
assert r.returncode == 0
|
|
100
|
+
assert "followup_message" in r.stdout or "block" in r.stdout, \
|
|
101
|
+
"a promise with an open AC must be ignored, not honored:\n%s" % r.stdout
|
|
102
|
+
assert os.path.exists(_scratchpad(root)), "loop wrongly stopped with an open AC"
|
|
103
|
+
assert "AC2" in r.stdout, "re-feed should name the open acceptance criterion:\n%s" % r.stdout
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def test_promise_with_evidence_all_acs_done_stops(tmp_path):
|
|
107
|
+
# Once every anchored AC is verified, the evidence-backed promise stops the loop as before.
|
|
108
|
+
root = str(tmp_path)
|
|
109
|
+
_arm(root, iteration=1, max_iter=5)
|
|
110
|
+
_write_anchor(root, [{"id": "AC1", "status": "done"}, {"id": "AC2", "status": "done"}])
|
|
111
|
+
r = _tick(root, "All green. <promise>SIMPLICIO_DONE</promise> tests pass ✓ "
|
|
112
|
+
"https://github.com/o/r/pull/9")
|
|
113
|
+
assert r.returncode == 0
|
|
114
|
+
assert r.stdout.strip() == "", "expected STOP (every AC verified), got: %s" % r.stdout
|
|
115
|
+
assert not os.path.exists(_scratchpad(root)), "state should be cleaned up on a verified stop"
|
|
116
|
+
|
|
117
|
+
|
|
82
118
|
def test_no_promise_continues_and_bumps_iteration(tmp_path):
|
|
83
119
|
root = str(tmp_path)
|
|
84
120
|
_arm(root, iteration=2, max_iter=5)
|
|
@@ -15,6 +15,8 @@ SELFTESTS = [
|
|
|
15
15
|
("scripts/loop_journal.py", "selftest"),
|
|
16
16
|
("scripts/billing_aggregator.py", "selftest"),
|
|
17
17
|
("scripts/savings_harness.py", "selftest"),
|
|
18
|
+
("scripts/task_anchor.py", "selftest"),
|
|
19
|
+
("scripts/pr_evidence.py", "selftest"),
|
|
18
20
|
]
|
|
19
21
|
|
|
20
22
|
|
|
@@ -42,6 +44,18 @@ def test_savings_harness_selftest():
|
|
|
42
44
|
assert "FAIL" not in r.stdout.upper() or "PASS" in r.stdout.upper(), r.stdout
|
|
43
45
|
|
|
44
46
|
|
|
47
|
+
def test_task_anchor_selftest():
|
|
48
|
+
r = _run("scripts/task_anchor.py", "selftest")
|
|
49
|
+
assert r.returncode == 0, "task_anchor selftest failed:\n%s%s" % (r.stdout, r.stderr)
|
|
50
|
+
assert "PASS" in r.stdout, r.stdout
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_pr_evidence_selftest():
|
|
54
|
+
r = _run("scripts/pr_evidence.py", "selftest")
|
|
55
|
+
assert r.returncode == 0, "pr_evidence selftest failed:\n%s%s" % (r.stdout, r.stderr)
|
|
56
|
+
assert "PASS" in r.stdout, r.stdout
|
|
57
|
+
|
|
58
|
+
|
|
45
59
|
if __name__ == "__main__":
|
|
46
60
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
47
61
|
from _selfrun import run_module
|
|
@@ -97,6 +97,75 @@ def test_repo_conventions_formatters_default():
|
|
|
97
97
|
assert c.stdout.strip() == "feat(auth): add SSO", c.stdout
|
|
98
98
|
|
|
99
99
|
|
|
100
|
+
def _run_anchor(args, env):
|
|
101
|
+
return subprocess.run([sys.executable, os.path.join(REPO, "scripts", "task_anchor.py")] + args,
|
|
102
|
+
capture_output=True, text=True, cwd=REPO, env=env)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def test_task_anchor_gate_and_drift(tmp_path):
|
|
106
|
+
# The anti-deviation guard: a freshly anchored task must BLOCK the done-gate (nothing verified),
|
|
107
|
+
# flag a CHANGED goal as DRIFT, and only go READY once every AC is marked with a receipt.
|
|
108
|
+
env = dict(os.environ, SIMPLICIO_ANCHOR_FILE=str(tmp_path / "anchor.json"))
|
|
109
|
+
s = _run_anchor(["set", "--item", "9", "--goal", "Add SSO login",
|
|
110
|
+
"--ac", "Renders an SSO button", "--ac", "Redirects to the IdP"], env)
|
|
111
|
+
assert s.returncode == 0, s.stdout + s.stderr
|
|
112
|
+
|
|
113
|
+
g = _run_anchor(["gate", "--exit-code"], env)
|
|
114
|
+
assert g.returncode == 12, "expected BLOCKED exit 12, got %d:\n%s" % (g.returncode, g.stdout)
|
|
115
|
+
assert "blocked" in g.stdout.lower(), g.stdout
|
|
116
|
+
|
|
117
|
+
d = _run_anchor(["check", "--goal", "refactor the database layer", "--exit-code"], env)
|
|
118
|
+
assert d.returncode == 11, "expected DRIFT exit 11, got %d:\n%s" % (d.returncode, d.stdout)
|
|
119
|
+
assert "drift" in d.stdout.lower(), d.stdout
|
|
120
|
+
|
|
121
|
+
# marking done WITHOUT a receipt must be refused (no fake "done") and not record progress
|
|
122
|
+
nm = _run_anchor(["mark", "--id", "AC1", "--status", "done"], env)
|
|
123
|
+
assert nm.returncode == 12, nm.stdout
|
|
124
|
+
assert "blocked" in nm.stdout.lower() and "requires --evidence" in nm.stdout.lower(), nm.stdout
|
|
125
|
+
# the refused mark must NOT have advanced the gate
|
|
126
|
+
still = _run_anchor(["gate", "--exit-code"], env)
|
|
127
|
+
assert still.returncode == 12, "refused mark leaked progress:\n%s" % still.stdout
|
|
128
|
+
|
|
129
|
+
_run_anchor(["mark", "--id", "AC1", "--status", "done", "--evidence", "a.png"], env)
|
|
130
|
+
_run_anchor(["mark", "--id", "AC2", "--status", "done", "--evidence", "b.png"], env)
|
|
131
|
+
ok = _run_anchor(["gate", "--exit-code"], env)
|
|
132
|
+
assert ok.returncode == 0, "expected READY, got %d:\n%s" % (ok.returncode, ok.stdout)
|
|
133
|
+
assert "ready" in ok.stdout.lower(), ok.stdout
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def test_pr_evidence_blocks_without_evidence(tmp_path):
|
|
137
|
+
# The "PR sem evidência" fix: with --require-evidence and neither a checklist nor a print,
|
|
138
|
+
# building the PR body MUST block (exit 3) and never emit a body / claim done.
|
|
139
|
+
env = dict(os.environ, SIMPLICIO_ANCHOR_FILE=str(tmp_path / "none.json"))
|
|
140
|
+
r = subprocess.run([sys.executable, os.path.join(REPO, "scripts", "pr_evidence.py"), "build",
|
|
141
|
+
"--require-evidence", "--anchor", str(tmp_path / "none.json"),
|
|
142
|
+
"--shots-dir", str(tmp_path / "empty")],
|
|
143
|
+
capture_output=True, text=True, cwd=REPO, env=env)
|
|
144
|
+
assert r.returncode == 3, "expected BLOCKED exit 3, got %d:\n%s" % (r.returncode, r.stdout)
|
|
145
|
+
assert "blocked" in r.stdout.lower(), r.stdout
|
|
146
|
+
assert "# " not in r.stdout, "leaked a PR body while blocked:\n%s" % r.stdout
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def test_pr_evidence_builds_with_checklist_and_prints(tmp_path):
|
|
150
|
+
# With an anchor + a captured print, the body MUST contain the item-by-item checklist and embed
|
|
151
|
+
# the screenshot — the two things the client said were missing.
|
|
152
|
+
anchor = str(tmp_path / "anchor.json")
|
|
153
|
+
env = dict(os.environ, SIMPLICIO_ANCHOR_FILE=anchor)
|
|
154
|
+
_run_anchor(["set", "--item", "9", "--goal", "Add SSO login",
|
|
155
|
+
"--ac", "Renders an SSO button"], env)
|
|
156
|
+
_run_anchor(["mark", "--id", "AC1", "--status", "done", "--evidence", "login.png"], env)
|
|
157
|
+
shots = tmp_path / "shots"
|
|
158
|
+
shots.mkdir()
|
|
159
|
+
(shots / "login.png").write_bytes(b"PNG")
|
|
160
|
+
r = subprocess.run([sys.executable, os.path.join(REPO, "scripts", "pr_evidence.py"), "build",
|
|
161
|
+
"--title", "Add SSO login", "--item", "9", "--require-evidence",
|
|
162
|
+
"--anchor", anchor, "--shots-dir", str(shots)],
|
|
163
|
+
capture_output=True, text=True, cwd=REPO, env=env)
|
|
164
|
+
assert r.returncode == 0, r.stdout + r.stderr
|
|
165
|
+
assert "[x] **AC1**" in r.stdout, "missing item-by-item checklist:\n%s" % r.stdout
|
|
166
|
+
assert "login.png" in r.stdout, "missing embedded print:\n%s" % r.stdout
|
|
167
|
+
|
|
168
|
+
|
|
100
169
|
if __name__ == "__main__":
|
|
101
170
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
102
171
|
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.11.0 → simplicio_loop-3.14.0}/simplicio_loop/_bundle/hooks/hooks.claude.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/simplicio_loop/_bundle/hooks/orient_clamp.py
RENAMED
|
File without changes
|
{simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/simplicio_loop/_bundle/hooks/orient_rewrite.py
RENAMED
|
File without changes
|
{simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/simplicio_loop/_bundle/hooks/simplicio_dashboard.py
RENAMED
|
File without changes
|
{simplicio_loop-3.11.0 → simplicio_loop-3.14.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
|
|
File without changes
|
|
File without changes
|
{simplicio_loop-3.11.0 → simplicio_loop-3.14.0}/simplicio_loop.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|