simplicio-loop 3.23.0__tar.gz → 3.25.1__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.23.0/simplicio_loop.egg-info → simplicio_loop-3.25.1}/PKG-INFO +3 -4
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/PYPI.md +1 -1
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/README.md +62 -77
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/pyproject.toml +4 -6
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/__init__.py +2 -2
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/hooks/action_gate.py +43 -3
- simplicio_loop-3.25.1/simplicio_loop/_bundle/hooks/loop_capture.py +31 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/hooks/loop_stop.py +181 -30
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/hooks/pre-commit.py +5 -2
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/hooks/simplicio_dashboard.py +19 -1
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/hooks/simplicio_watch.py +32 -3
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/scripts/hierarchical_planner.py +6 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/SKILL.md +24 -22
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/references/bound-operators.md +13 -11
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/references/orchestration.md +3 -2
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/references/quality-safety-delivery.md +10 -3
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-tasks/references/orchestration.md +3 -2
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-tasks/references/quality-safety-delivery.md +20 -3
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1/simplicio_loop/_bundle}/tests/test_loop_e2e.py +162 -6
- simplicio_loop-3.25.1/simplicio_loop/cli.py +651 -0
- simplicio_loop-3.25.1/simplicio_loop/delivery.py +225 -0
- simplicio_loop-3.25.1/simplicio_loop/drain.py +349 -0
- simplicio_loop-3.25.1/simplicio_loop/evidence.py +395 -0
- simplicio_loop-3.25.1/simplicio_loop/ops_ledger.py +283 -0
- simplicio_loop-3.25.1/simplicio_loop/oracle.py +261 -0
- simplicio_loop-3.25.1/simplicio_loop/phase_events.py +139 -0
- simplicio_loop-3.25.1/simplicio_loop/runner.py +1930 -0
- simplicio_loop-3.25.1/simplicio_loop/source_state.py +122 -0
- simplicio_loop-3.25.1/simplicio_loop/task_contract.py +673 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1/simplicio_loop.egg-info}/PKG-INFO +3 -4
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop.egg-info/SOURCES.txt +35 -1
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop.egg-info/requires.txt +1 -2
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_action_gate.py +32 -20
- simplicio_loop-3.25.1/tests/test_check_e2e_demo_contract.py +13 -0
- simplicio_loop-3.25.1/tests/test_check_e2e_installed.py +97 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_claims_audit.py +44 -0
- simplicio_loop-3.25.1/tests/test_clean_env_contract.py +33 -0
- simplicio_loop-3.25.1/tests/test_completion_oracle.py +321 -0
- simplicio_loop-3.25.1/tests/test_completion_oracle_matrix.py +51 -0
- simplicio_loop-3.25.1/tests/test_delivery_source_identity.py +24 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_doctor_smoke.py +18 -2
- simplicio_loop-3.25.1/tests/test_drain.py +140 -0
- simplicio_loop-3.25.1/tests/test_drain_cli.py +136 -0
- simplicio_loop-3.25.1/tests/test_e2e_demo_audit.py +97 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_evidence_chain.py +54 -2
- simplicio_loop-3.25.1/tests/test_evidence_receipt.py +252 -0
- simplicio_loop-3.25.1/tests/test_fan_out_scheduler.py +193 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_fan_out_unit.py +46 -0
- simplicio_loop-3.25.1/tests/test_hbp_topics.py +299 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_hooks_coverage.py +16 -5
- simplicio_loop-3.25.1/tests/test_ledger_cli.py +81 -0
- simplicio_loop-3.25.1/tests/test_locked_append.py +168 -0
- {simplicio_loop-3.23.0/simplicio_loop/_bundle → simplicio_loop-3.25.1}/tests/test_loop_e2e.py +162 -6
- simplicio_loop-3.25.1/tests/test_mirror_parity.py +34 -0
- simplicio_loop-3.25.1/tests/test_operator_batch.py +119 -0
- simplicio_loop-3.25.1/tests/test_operator_worktree_batch.py +142 -0
- simplicio_loop-3.25.1/tests/test_ops_ledger.py +163 -0
- simplicio_loop-3.25.1/tests/test_phase_events.py +74 -0
- simplicio_loop-3.25.1/tests/test_planes_golden_eval.py +89 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_precommit_bundle_sync.py +40 -9
- simplicio_loop-3.25.1/tests/test_runner_cli.py +1001 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_system_check.py +19 -2
- simplicio_loop-3.25.1/tests/test_task_backlog.py +602 -0
- simplicio_loop-3.25.1/tests/test_task_contract_cli.py +79 -0
- simplicio_loop-3.25.1/tests/test_task_contract_unit.py +146 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_token_budget.py +5 -3
- simplicio_loop-3.25.1/tests/test_watcher_binding.py +23 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_worker_cli_contract.py +8 -1
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_worker_selftests.py +50 -1
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_worker_smoke.py +92 -15
- simplicio_loop-3.25.1/tests/test_worktree_queue.py +127 -0
- simplicio_loop-3.23.0/simplicio_loop/_bundle/hooks/loop_capture.py +0 -86
- simplicio_loop-3.23.0/simplicio_loop/cli.py +0 -180
- simplicio_loop-3.23.0/tests/test_task_backlog.py +0 -225
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/LICENSE +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/MANIFEST.in +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/setup.cfg +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/hooks/README.md +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/hooks/hooks.claude.json +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/hooks/hooks.json +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/hooks/orient_clamp.py +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/hooks/orient_rewrite.py +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/scripts/cross_agent_wiki.py +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-autoresearch/SKILL.md +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-compress/SKILL.md +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-learn/SKILL.md +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/references/agentsview-adapter.md +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/references/azure-devops-adapter.md +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/references/claims-gate.md +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/references/cross-agent-wiki.md +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/references/cross-repo-integration.md +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/references/extension-points.md +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/references/hierarchical-planner.md +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/references/lmcache-adapter.md +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/references/run-journal-stall-detector.md +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/references/spindle-handoff.md +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/references/standing-loop-247.md +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/references/token-capture.md +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/references/token-economy.md +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/references/triage-verify-detail.md +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/references/understand-anything-adapter.md +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/references/video-evidence.md +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/references/web-evidence.md +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-orient/SKILL.md +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-review/SKILL.md +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-tasks/SKILL.md +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-tasks/references/agentsview-adapter.md +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-tasks/references/azure-devops-adapter.md +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-tasks/references/extension-points.md +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-tasks/references/lmcache-adapter.md +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-tasks/references/standing-loop-247.md +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-tasks/references/token-capture.md +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-tasks/references/token-economy.md +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-tasks/references/understand-anything-adapter.md +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-tasks/references/video-evidence.md +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-tasks/references/web-evidence.md +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/tests/_selfrun.py +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/tests/test_cross_agent_wiki.py +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop.egg-info/dependency_links.txt +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop.egg-info/entry_points.txt +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop.egg-info/top_level.txt +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_agentsview_adapter.py +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_autoresearch.py +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_az_boards_adapter.py +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_claims_manifest_unit.py +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_cross_agent_wiki.py +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_dashboard_hook.py +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_fan_out_flow.py +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_flow_audit.py +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_hierarchical_planner.py +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_impact_audit.py +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_install_lib.py +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_learn_pipeline_removed.py +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_repo_conventions_architecture.py +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_schema_verify_integration.py +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_schema_verify_unit.py +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_toon_codec.py +0 -0
- {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_verify_adapters_integration.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: simplicio-loop
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.25.1
|
|
4
4
|
Summary: The Universal Looping AI Orchestrator — a runtime-agnostic super-plugin (7 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
|
|
@@ -19,8 +19,7 @@ Classifier: Operating System :: OS Independent
|
|
|
19
19
|
Requires-Python: >=3.8
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
License-File: LICENSE
|
|
22
|
-
Requires-Dist: simplicio-
|
|
23
|
-
Requires-Dist: simplicio-cli>=0.9.6
|
|
22
|
+
Requires-Dist: simplicio-cli>=0.11.0
|
|
24
23
|
Provides-Extra: dev
|
|
25
24
|
Requires-Dist: pytest>=7; extra == "dev"
|
|
26
25
|
Provides-Extra: ml
|
|
@@ -35,7 +34,7 @@ drains any queue of work end-to-end on **any LLM / runtime**:
|
|
|
35
34
|
`discover → implement → verify → merge → close → watch 24/7`, behind safety gates and evidence
|
|
36
35
|
checks, at up to **96% fewer tokens**. Not a chatbot. A worker.
|
|
37
36
|
|
|
38
|
-

|
|
39
38
|
|
|
40
39
|
## Install
|
|
41
40
|
|
|
@@ -5,7 +5,7 @@ drains any queue of work end-to-end on **any LLM / runtime**:
|
|
|
5
5
|
`discover → implement → verify → merge → close → watch 24/7`, behind safety gates and evidence
|
|
6
6
|
checks, at up to **96% fewer tokens**. Not a chatbot. A worker.
|
|
7
7
|
|
|
8
|
-

|
|
9
9
|
|
|
10
10
|
## Install
|
|
11
11
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# 🔁 simplicio-loop — The Universal Looping AI Orchestrator
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
|
-
<img src="assets/simplicio-loop-hero.
|
|
4
|
+
<img src="assets/simplicio-loop-hero-2026.png" alt="simplicio-loop autonomous parallel evidence-gated orchestration" width="920" />
|
|
5
5
|
</p>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
@@ -48,6 +48,41 @@
|
|
|
48
48
|
|
|
49
49
|
---
|
|
50
50
|
|
|
51
|
+
<!-- visual-story:start -->
|
|
52
|
+
## 🚀 The new generation — an operating system for verified agent work
|
|
53
|
+
|
|
54
|
+
**simplicio-loop has evolved far beyond a repeat-until-done prompt.** It now compiles intent into a frozen task contract, maps the repository, schedules dependency-aware work, fans execution out into isolated worktrees, collects structured receipts, verifies independently, rolls back safely, remembers every attempt, and keeps the source of record synchronized through delivery.
|
|
55
|
+
|
|
56
|
+
- **Contract first** — acceptance criteria, dependencies, risks, source state, and the completion oracle are explicit before execution.
|
|
57
|
+
- **Parallel without corruption** — ready tasks run in isolated lanes/worktrees and converge through an operational ledger.
|
|
58
|
+
- **Proof before completion** — tests, impact/flow checks, watcher challenges, delivery receipts, and HBP evidence reject false done states.
|
|
59
|
+
- **Memory that changes behavior** — the journal, stall detector, checkpoints, and cross-agent wiki prevent oscillation and make handoffs durable.
|
|
60
|
+
|
|
61
|
+
<p align="center">
|
|
62
|
+
<img src="assets/simplicio-loop-parallel-worktrees.png" alt="simplicio-loop parallel isolated worktree execution" width="920" />
|
|
63
|
+
</p>
|
|
64
|
+
|
|
65
|
+
<p align="center"><em>Dependency-aware fan-out: isolated workers execute concurrently, return evidence, and converge into one verified delivery.</em></p>
|
|
66
|
+
|
|
67
|
+
<p align="center">
|
|
68
|
+
<img src="assets/simplicio-loop-lifecycle-2026.svg" alt="simplicio-loop lifecycle from intake to durable memory" width="920" />
|
|
69
|
+
</p>
|
|
70
|
+
|
|
71
|
+
<p align="center"><em>Every stage is explicit, bounded, observable, and reversible.</em></p>
|
|
72
|
+
|
|
73
|
+
<p align="center">
|
|
74
|
+
<img src="assets/simplicio-loop-evidence-memory.png" alt="simplicio-loop evidence memory verification rollback and completion" width="920" />
|
|
75
|
+
</p>
|
|
76
|
+
|
|
77
|
+
<p align="center"><em>Evidence and memory are part of the execution path—not a report written after the fact.</em></p>
|
|
78
|
+
|
|
79
|
+
That architecture lets one goal become a governed delivery system: from a single hard task to an entire backlog, across sessions and runtimes, with local-first operators and receipts strong enough for humans, CI, or another agent to audit.
|
|
80
|
+
|
|
81
|
+
<p align="center">
|
|
82
|
+
<img src="assets/simplicio-loop-architecture-2026.svg" alt="simplicio-loop control execution evidence and delivery planes" width="920" />
|
|
83
|
+
</p>
|
|
84
|
+
<!-- visual-story:end -->
|
|
85
|
+
|
|
51
86
|
## ⚡ TL;DR
|
|
52
87
|
|
|
53
88
|
**simplicio-loop** is a runtime-agnostic **super-plugin** — one autonomous looping
|
|
@@ -210,79 +245,28 @@ Every layer the orchestrator acts on, in order — from reading the demand (issu
|
|
|
210
245
|
to delivering merged, evidenced work, then looping 24/7 for more.
|
|
211
246
|
|
|
212
247
|
```mermaid
|
|
213
|
-
flowchart
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
DISC --> INTK
|
|
236
|
-
subgraph INTK["4 · Deep intake (per item)"]
|
|
237
|
-
direction LR
|
|
238
|
-
I1["body + ALL comments"]
|
|
239
|
-
I2["extract acceptance criteria"]
|
|
240
|
-
I3["orient code · signatures-only reads or Understand Anything knowledge graph"]
|
|
241
|
-
I4["plan + AC checklist + complexity"]
|
|
242
|
-
end
|
|
243
|
-
INTK --> RT{"5 · Route"}
|
|
244
|
-
RT -->|"small and every item complexity at most 3"| FAST["Fast-path: solo, one targeted test"]
|
|
245
|
-
RT -->|"large queue or any medium+"| POOL
|
|
246
|
-
subgraph POOL["6 · Continuous worker pool (autoscaled, conflict-aware)"]
|
|
247
|
-
direction LR
|
|
248
|
-
W1["claim · branch · worktree if overlap"]
|
|
249
|
-
W2["deterministic_edit"]
|
|
250
|
-
W3["quality loop: edit-lint-test-fix"]
|
|
251
|
-
end
|
|
252
|
-
FAST --> QG
|
|
253
|
-
POOL --> QG
|
|
254
|
-
subgraph QG["7 · Quality gates"]
|
|
255
|
-
direction LR
|
|
256
|
-
Q1["AC gate + impact_audit = real DoD"]
|
|
257
|
-
Q2["WORKS not just compiles · web_verify · video_evidence · flow_audit"]
|
|
258
|
-
Q3["adversarial review · thermos rubrics"]
|
|
259
|
-
end
|
|
260
|
-
QG --> SG
|
|
261
|
-
subgraph SG["8 · Safety gates (non-negotiable)"]
|
|
262
|
-
direction LR
|
|
263
|
-
G1["secret-scan"]
|
|
264
|
-
G2["irreversible-op human gate"]
|
|
265
|
-
G3["4-state verdict · attestation"]
|
|
266
|
-
end
|
|
267
|
-
SG --> DEL
|
|
268
|
-
subgraph DEL["9 · Deliver"]
|
|
269
|
-
direction LR
|
|
270
|
-
L1["commit · push · Draft PR"]
|
|
271
|
-
L2["close in-source + evidence"]
|
|
272
|
-
L3["verify reality, not self-report"]
|
|
273
|
-
end
|
|
274
|
-
DEL --> FB
|
|
275
|
-
subgraph FB["10 · Feedback loop to merge-ready"]
|
|
276
|
-
direction LR
|
|
277
|
-
F1["CI fail -> fix root cause"]
|
|
278
|
-
F2["review comments -> adjust"]
|
|
279
|
-
F3["branch behind main -> additive rebase"]
|
|
280
|
-
end
|
|
281
|
-
FB -->|"merged and closed"| DONE(["done + evidence + measured savings (only if a receipt exists)"])
|
|
282
|
-
WATCH["11 · 24/7 watcher · simplicio-loop evidence-gated promise · max-iterations cap · STOP path · LMCache KV cache warm"]
|
|
283
|
-
FB -. "poll new work / comments / checks" .-> WATCH
|
|
284
|
-
DONE -. "idle until new work" .-> WATCH
|
|
285
|
-
WATCH -. "re-feed the goal" .-> DISC
|
|
248
|
+
flowchart LR
|
|
249
|
+
IN["Intent: issue · task · queue"] --> CONTRACT["1 · Freeze task contract"]
|
|
250
|
+
CONTRACT --> MAP["2 · Map source + normalize"]
|
|
251
|
+
MAP --> PLAN["3 · Dependency DAG + acceptance criteria"]
|
|
252
|
+
PLAN --> ROUTE{"4 · Ready task?"}
|
|
253
|
+
ROUTE -->|"solo / small"| SOLO["Targeted lane"]
|
|
254
|
+
ROUTE -->|"parallel / medium+"| FAN["Bounded fan-out"]
|
|
255
|
+
FAN --> A["Isolated worktree A"]
|
|
256
|
+
FAN --> B["Isolated worktree B"]
|
|
257
|
+
FAN --> C["Isolated worktree C"]
|
|
258
|
+
SOLO --> VERIFY["5 · Test + impact/flow evidence"]
|
|
259
|
+
A --> VERIFY
|
|
260
|
+
B --> VERIFY
|
|
261
|
+
C --> VERIFY
|
|
262
|
+
VERIFY --> RECEIPT["Watcher challenge + evidence receipt"]
|
|
263
|
+
RECEIPT --> ORACLE{"6 · Completion oracle"}
|
|
264
|
+
ORACLE -->|"pending / blocked"| RECOVER["Journal · checkpoint · rollback · backlog-only maintenance"]
|
|
265
|
+
RECOVER --> PLAN
|
|
266
|
+
ORACLE -->|"verified / measured"| DELIVER["7 · Source sync · PR · merge"]
|
|
267
|
+
DELIVER --> MEMORY["8 · Ledger · wiki · durable attempt memory"]
|
|
268
|
+
MEMORY --> WATCH["9 · Re-feed · watcher · STOP path"]
|
|
269
|
+
WATCH -->|"new work"| IN
|
|
286
270
|
```
|
|
287
271
|
|
|
288
272
|
---
|
|
@@ -530,8 +514,9 @@ pwsh scripts/install.ps1 <runtime> [-Global] # Windows
|
|
|
530
514
|
```
|
|
531
515
|
|
|
532
516
|
**The repo installer is full-stack by default — it installs everything.** One command sets up the whole stack:
|
|
533
|
-
the
|
|
534
|
-
|
|
517
|
+
the loop operator package (`simplicio-cli`, which exposes `simplicio-dev-cli` and also brings
|
|
518
|
+
`simplicio-mapper` transitively, auto-handling PEP 668 / externally-managed Python and symlinking
|
|
519
|
+
the binaries onto `PATH`), the **full Python stack** (the package itself),
|
|
535
520
|
the **7 skills + hooks** with the loop's Stop hook wired, and the **always-on capture proxy**
|
|
536
521
|
with Claude + Codex + Hermes **routed and measured** in the background. The **dashboard opens once** on a
|
|
537
522
|
fresh install, then it's on-demand (`simplicio-loop dashboard` / `simplicio-economy.sh monitor`); the
|
|
@@ -556,7 +541,7 @@ python3 scripts/doctor.py --repair # install/wire what's fixable; make everyth
|
|
|
556
541
|
# also: bash scripts/simplicio-economy.sh doctor [--repair]
|
|
557
542
|
```
|
|
558
543
|
|
|
559
|
-
`doctor` separates **REQUIRED** (python3, the
|
|
544
|
+
`doctor` separates **REQUIRED** (python3, the loop operator package plus its runtime bins, the 7 skills, the loop hooks, the
|
|
560
545
|
capture proxy — `--repair` installs/wires them) from **OPTIONAL** accelerators (the tray dep).
|
|
561
546
|
**Missing an optional piece is never a failure and
|
|
562
547
|
never blocks** — the Python engine + the deterministic path cover everything; the exit code is 0 as
|
|
@@ -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.25.1"
|
|
8
8
|
description = "The Universal Looping AI Orchestrator — a runtime-agnostic super-plugin (7 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"
|
|
@@ -21,12 +21,10 @@ classifiers = [
|
|
|
21
21
|
"Topic :: Utilities",
|
|
22
22
|
"Operating System :: OS Independent",
|
|
23
23
|
]
|
|
24
|
-
# The simplicio-loop drive
|
|
25
|
-
#
|
|
26
|
-
# simplicio-cli -> the operator that applies+verifies changes (binds `execute`/`deterministic_edit`)
|
|
24
|
+
# The simplicio-loop drive requires the operator package (`simplicio-cli`), which exposes
|
|
25
|
+
# `simplicio-dev-cli` and also brings the survey binary `simplicio-mapper` transitively.
|
|
27
26
|
dependencies = [
|
|
28
|
-
"simplicio-
|
|
29
|
-
"simplicio-cli>=0.9.6",
|
|
27
|
+
"simplicio-cli>=0.11.0",
|
|
30
28
|
]
|
|
31
29
|
|
|
32
30
|
# Optional: the real embedding backend for `simplicio-cli semantic --ml` / `simplicio-cli rag --ml`.
|
|
@@ -235,8 +235,46 @@ def gate_command(cmd, staged=False):
|
|
|
235
235
|
|
|
236
236
|
|
|
237
237
|
# ---------------------------------------------------------------------------------------------
|
|
238
|
-
def
|
|
238
|
+
def _hbp_append_gate_blocked(reason, cmd=""):
|
|
239
|
+
"""Record a gate BLOCK into the runtime's HBP tamper-evident hash chain (#128) — fail-open,
|
|
240
|
+
`simplicio`-only, mirrors `hooks/loop_stop.py`'s `_call_simplicio_hbp_append*` family. A
|
|
241
|
+
blocked mutation is exactly the kind of decision that should be provable later (a later
|
|
242
|
+
`simplicio hbp verify` can show this exact command/reason was recorded in order), not just
|
|
243
|
+
printed to stderr and forgotten. Absent binary or any failure: silent no-op, never blocks
|
|
244
|
+
the gate's own allow/block decision (that decision already happened before this is called).
|
|
245
|
+
"""
|
|
246
|
+
binary = shutil.which("simplicio")
|
|
247
|
+
if not binary:
|
|
248
|
+
return
|
|
249
|
+
try:
|
|
250
|
+
import hashlib
|
|
251
|
+
# minimal payload contract shared by the #128 topics: fingerprint + attempt id.
|
|
252
|
+
fp = hashlib.sha1((reason or "").encode("utf-8", "replace")).hexdigest()[:12]
|
|
253
|
+
attempt = None
|
|
254
|
+
try: # attempt id = live loop iteration from the scratchpad frontmatter, when armed
|
|
255
|
+
with open(os.path.join(".orchestrator", "loop", "scratchpad.md"),
|
|
256
|
+
encoding="utf-8") as f:
|
|
257
|
+
m = re.search(r"^iteration:\s*(\d+)", f.read(), re.M)
|
|
258
|
+
attempt = int(m.group(1)) if m else None
|
|
259
|
+
except Exception:
|
|
260
|
+
attempt = None
|
|
261
|
+
payload = json.dumps({"fingerprint": fp, "attempt": attempt,
|
|
262
|
+
"reason": (reason or "")[:200], "command": (cmd or "")[:200]})
|
|
263
|
+
subprocess.run(
|
|
264
|
+
[binary, "hbp", "append",
|
|
265
|
+
"--topic", "loop-gate-blocked",
|
|
266
|
+
"--payload", payload,
|
|
267
|
+
"--provenance", "simplicio-loop",
|
|
268
|
+
"--json"],
|
|
269
|
+
capture_output=True, timeout=15,
|
|
270
|
+
)
|
|
271
|
+
except Exception:
|
|
272
|
+
pass
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def _emit_and_exit(verdict, pretooluse=False, cmd=""):
|
|
239
276
|
if verdict["action"] == "block":
|
|
277
|
+
_hbp_append_gate_blocked(verdict.get("reason", ""), cmd)
|
|
240
278
|
if pretooluse:
|
|
241
279
|
# Claude PreToolUse: exit 2 blocks the call; reason on stderr is fed back to the model.
|
|
242
280
|
sys.stderr.write("action_gate BLOCK — " + verdict["reason"] + "\n")
|
|
@@ -251,7 +289,7 @@ def _emit_and_exit(verdict, pretooluse=False):
|
|
|
251
289
|
|
|
252
290
|
def cmd_check(opts):
|
|
253
291
|
cmd = opts.get("command", "")
|
|
254
|
-
_emit_and_exit(gate_command(cmd, staged=bool(opts.get("staged"))))
|
|
292
|
+
_emit_and_exit(gate_command(cmd, staged=bool(opts.get("staged"))), cmd=cmd)
|
|
255
293
|
|
|
256
294
|
|
|
257
295
|
def cmd_scan_diff(opts):
|
|
@@ -271,6 +309,8 @@ def cmd_scan_diff(opts):
|
|
|
271
309
|
for label, n in hits:
|
|
272
310
|
print(" secret: %s (line %d)" % (label, n))
|
|
273
311
|
print("block")
|
|
312
|
+
_hbp_append_gate_blocked("secret in diff (%s)" % ", ".join(sorted({h[0] for h in hits})),
|
|
313
|
+
"scan-diff %s" % (src or "-"))
|
|
274
314
|
sys.exit(2)
|
|
275
315
|
print("allow")
|
|
276
316
|
sys.exit(0)
|
|
@@ -292,7 +332,7 @@ def from_pretooluse():
|
|
|
292
332
|
cmd = (data.get("tool_input", {}) or {}).get("command", "")
|
|
293
333
|
if not cmd:
|
|
294
334
|
sys.exit(0)
|
|
295
|
-
_emit_and_exit(gate_command(cmd), pretooluse=True)
|
|
335
|
+
_emit_and_exit(gate_command(cmd), pretooluse=True, cmd=cmd)
|
|
296
336
|
|
|
297
337
|
|
|
298
338
|
def cmd_selftest(_opts):
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""simplicio-loop — capture hook (Cursor `afterAgentResponse`).
|
|
3
|
+
|
|
4
|
+
Transport only: stash the latest assistant response so the shared completion oracle
|
|
5
|
+
can evaluate it later in the stop hook. This hook never decides completion and never
|
|
6
|
+
raises `done` on its own.
|
|
7
|
+
"""
|
|
8
|
+
import json
|
|
9
|
+
import os
|
|
10
|
+
import sys
|
|
11
|
+
|
|
12
|
+
LOOP_DIR = os.path.join(".orchestrator", "loop")
|
|
13
|
+
LAST_RESP = os.path.join(LOOP_DIR, "last_response.txt")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def main():
|
|
17
|
+
try:
|
|
18
|
+
raw = sys.stdin.read()
|
|
19
|
+
data = json.loads(raw) if raw.strip() else {}
|
|
20
|
+
resp = data.get("text", "") or ""
|
|
21
|
+
if resp:
|
|
22
|
+
os.makedirs(LOOP_DIR, exist_ok=True)
|
|
23
|
+
with open(LAST_RESP, "w", encoding="utf-8") as f:
|
|
24
|
+
f.write(resp)
|
|
25
|
+
except Exception:
|
|
26
|
+
pass
|
|
27
|
+
sys.exit(0)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
if __name__ == "__main__":
|
|
31
|
+
main()
|
|
@@ -26,6 +26,14 @@ import subprocess
|
|
|
26
26
|
import sys
|
|
27
27
|
import time
|
|
28
28
|
import uuid
|
|
29
|
+
from pathlib import Path
|
|
30
|
+
|
|
31
|
+
try: # Windows consoles default to cp1252; hook protocols and evidence are UTF-8.
|
|
32
|
+
sys.stdin.reconfigure(encoding="utf-8", errors="replace")
|
|
33
|
+
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
|
34
|
+
sys.stderr.reconfigure(encoding="utf-8", errors="replace")
|
|
35
|
+
except Exception:
|
|
36
|
+
pass
|
|
29
37
|
|
|
30
38
|
LOOP_DIR = os.path.join(".orchestrator", "loop")
|
|
31
39
|
SCRATCHPAD = os.path.join(LOOP_DIR, "scratchpad.md")
|
|
@@ -222,6 +230,7 @@ def write_handoff(reason, meta=None, body=None):
|
|
|
222
230
|
anchor = read_anchor() or {}
|
|
223
231
|
criteria = anchor.get("criteria") or []
|
|
224
232
|
attempts = tail_journal()
|
|
233
|
+
completion = latest_completion_receipt() or {}
|
|
225
234
|
lines = [
|
|
226
235
|
"# simplicio-loop handoff",
|
|
227
236
|
"",
|
|
@@ -262,6 +271,16 @@ def write_handoff(reason, meta=None, body=None):
|
|
|
262
271
|
attempt_suffix(a),
|
|
263
272
|
)
|
|
264
273
|
)
|
|
274
|
+
if completion:
|
|
275
|
+
lines += [
|
|
276
|
+
"",
|
|
277
|
+
"## Completion oracle",
|
|
278
|
+
"",
|
|
279
|
+
"- verdict: %s" % completion.get("verdict", "DELIVERY_PENDING"),
|
|
280
|
+
"- reason_code: %s" % completion.get("reason_code", "oracle_incomplete"),
|
|
281
|
+
"- tag: %s" % completion.get("tag", "UNVERIFIED"),
|
|
282
|
+
"- receipt: %s" % completion.get("_path", "(unknown)"),
|
|
283
|
+
]
|
|
265
284
|
lines += [
|
|
266
285
|
"",
|
|
267
286
|
"## Resume",
|
|
@@ -326,9 +345,12 @@ def _changed_files():
|
|
|
326
345
|
commit). A heuristic, not a precise "since loop start" diff — fail-open: {} on any error.
|
|
327
346
|
|
|
328
347
|
Excludes `.orchestrator/` (the loop's own state files — never source, and would otherwise
|
|
329
|
-
make the receipt's own write, or a sibling state write, look like a "later" source change)
|
|
330
|
-
|
|
331
|
-
|
|
348
|
+
make the receipt's own write, or a sibling state write, look like a "later" source change),
|
|
349
|
+
`.simplicio/` (the simplicio runtime/dev-cli's own state — checkpoints, events.jsonl, survey
|
|
350
|
+
artifacts — written by this very hook's fire-and-forget CLI callouts mid-turn, the same
|
|
351
|
+
self-inflicted false-positive class), and build/cache noise (`__pycache__`, `.pyc`) that this
|
|
352
|
+
very check's own module import can create — those false-positives are exactly why all three
|
|
353
|
+
are filtered here.
|
|
332
354
|
"""
|
|
333
355
|
out = set()
|
|
334
356
|
for args in (
|
|
@@ -344,7 +366,8 @@ def _changed_files():
|
|
|
344
366
|
continue
|
|
345
367
|
return {
|
|
346
368
|
f for f in out
|
|
347
|
-
if not f.startswith(".orchestrator/"
|
|
369
|
+
if not f.startswith((".orchestrator/", ".simplicio/"))
|
|
370
|
+
and "__pycache__" not in f and not f.endswith((".pyc", ".pyo"))
|
|
348
371
|
}
|
|
349
372
|
|
|
350
373
|
|
|
@@ -518,6 +541,31 @@ def _call_simplicio_checkpoint(iteration):
|
|
|
518
541
|
pass
|
|
519
542
|
|
|
520
543
|
|
|
544
|
+
def _call_simplicio_hbp_append_topic(topic, payload_dict):
|
|
545
|
+
"""Generic fail-open append into the runtime's HBP tamper-evident hash chain
|
|
546
|
+
(`simplicio hbp append`), when the native `simplicio` binary is on PATH (#128).
|
|
547
|
+
|
|
548
|
+
Same contract as the promise-verified append below: `simplicio`-only, best-effort — absent
|
|
549
|
+
binary or any failure is a silent no-op that never blocks the caller's own decision. Every
|
|
550
|
+
new HBP point in this hook family (stall detected, gate blocked, run blocked) goes through
|
|
551
|
+
here so the topics all carry the same provenance and failure discipline.
|
|
552
|
+
"""
|
|
553
|
+
binary = shutil.which("simplicio")
|
|
554
|
+
if not binary:
|
|
555
|
+
return
|
|
556
|
+
try:
|
|
557
|
+
subprocess.run(
|
|
558
|
+
[binary, "hbp", "append",
|
|
559
|
+
"--topic", topic,
|
|
560
|
+
"--payload", json.dumps(payload_dict),
|
|
561
|
+
"--provenance", "simplicio-loop",
|
|
562
|
+
"--json"],
|
|
563
|
+
capture_output=True, timeout=15,
|
|
564
|
+
)
|
|
565
|
+
except Exception:
|
|
566
|
+
pass
|
|
567
|
+
|
|
568
|
+
|
|
521
569
|
def _call_simplicio_hbp_append(iteration, promise, watcher_tag):
|
|
522
570
|
"""Record the promise-verification decision into the runtime's HBP
|
|
523
571
|
tamper-evident hash chain (`simplicio hbp append`), when the native
|
|
@@ -531,29 +579,44 @@ def _call_simplicio_hbp_append(iteration, promise, watcher_tag):
|
|
|
531
579
|
recorded in order, without trusting this process's own say-so after the
|
|
532
580
|
fact. Absent binary or any failure: silent no-op, never blocks the stop.
|
|
533
581
|
"""
|
|
534
|
-
binary = shutil.which("simplicio")
|
|
535
|
-
if not binary:
|
|
536
|
-
return
|
|
537
582
|
try:
|
|
538
583
|
anchor = read_anchor() or {}
|
|
539
|
-
|
|
584
|
+
_call_simplicio_hbp_append_topic("loop-promise-verified", {
|
|
540
585
|
"iteration": iteration,
|
|
541
586
|
"promise": promise,
|
|
542
587
|
"watcher_tag": watcher_tag,
|
|
543
588
|
"goal_fp": anchor.get("goal_fp", ""),
|
|
544
589
|
})
|
|
545
|
-
subprocess.run(
|
|
546
|
-
[binary, "hbp", "append",
|
|
547
|
-
"--topic", "loop-promise-verified",
|
|
548
|
-
"--payload", payload,
|
|
549
|
-
"--provenance", "simplicio-loop",
|
|
550
|
-
"--json"],
|
|
551
|
-
capture_output=True, timeout=15,
|
|
552
|
-
)
|
|
553
590
|
except Exception:
|
|
554
591
|
pass
|
|
555
592
|
|
|
556
593
|
|
|
594
|
+
def _journal_stall(k=3):
|
|
595
|
+
"""Trailing same-fingerprint failure streak from the journal — (fingerprint, streak).
|
|
596
|
+
|
|
597
|
+
Mirrors `scripts/loop_journal.py`'s `analyze()` trailing-streak math over the tail of the
|
|
598
|
+
journal, locally and fail-open (("", 0) on any error) — the hook must never import the
|
|
599
|
+
worker to decide whether to emit the `loop-stall-detected` HBP record (#128).
|
|
600
|
+
"""
|
|
601
|
+
try:
|
|
602
|
+
rows = tail_journal(n=max(k * 3, 12))
|
|
603
|
+
if not rows:
|
|
604
|
+
return "", 0
|
|
605
|
+
last = rows[-1]
|
|
606
|
+
fp = last.get("fingerprint", "")
|
|
607
|
+
if last.get("gate") == "pass" or not fp:
|
|
608
|
+
return "", 0
|
|
609
|
+
streak = 0
|
|
610
|
+
for r in reversed(rows):
|
|
611
|
+
if r.get("gate") != "pass" and r.get("fingerprint") == fp:
|
|
612
|
+
streak += 1
|
|
613
|
+
else:
|
|
614
|
+
break
|
|
615
|
+
return fp, streak
|
|
616
|
+
except Exception:
|
|
617
|
+
return "", 0
|
|
618
|
+
|
|
619
|
+
|
|
557
620
|
def read_watcher_challenge():
|
|
558
621
|
"""Return the current per-iteration watcher challenge dict, or None. Fail-open."""
|
|
559
622
|
try:
|
|
@@ -635,6 +698,70 @@ def watcher_verify():
|
|
|
635
698
|
return False, "UNVERIFIED"
|
|
636
699
|
|
|
637
700
|
|
|
701
|
+
def latest_run_dir():
|
|
702
|
+
try:
|
|
703
|
+
runs = Path(".orchestrator") / "runs"
|
|
704
|
+
if not runs.exists():
|
|
705
|
+
return ""
|
|
706
|
+
candidates = sorted([p for p in runs.iterdir() if p.is_dir()], key=lambda p: p.name)
|
|
707
|
+
return str(candidates[-1]) if candidates else ""
|
|
708
|
+
except Exception:
|
|
709
|
+
return ""
|
|
710
|
+
|
|
711
|
+
|
|
712
|
+
def latest_completion_receipt():
|
|
713
|
+
try:
|
|
714
|
+
run_dir = latest_run_dir()
|
|
715
|
+
if not run_dir:
|
|
716
|
+
return None
|
|
717
|
+
path = Path(run_dir) / "completion-receipt.json"
|
|
718
|
+
if not path.exists():
|
|
719
|
+
return None
|
|
720
|
+
with open(path, encoding="utf-8") as f:
|
|
721
|
+
payload = json.load(f)
|
|
722
|
+
payload["_path"] = str(path)
|
|
723
|
+
return payload
|
|
724
|
+
except Exception:
|
|
725
|
+
return None
|
|
726
|
+
|
|
727
|
+
|
|
728
|
+
def completion_oracle_payload(response_text="", flow_gap=""):
|
|
729
|
+
try:
|
|
730
|
+
repo_root = os.getcwd()
|
|
731
|
+
script = os.path.join(repo_root, "scripts", "completion_oracle.py")
|
|
732
|
+
if not os.path.exists(script):
|
|
733
|
+
source_repo = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
734
|
+
script = os.path.join(source_repo, "scripts", "completion_oracle.py")
|
|
735
|
+
if not os.path.exists(script):
|
|
736
|
+
return {"ready": False, "reason_code": "oracle_script_missing", "tag": "UNVERIFIED"}
|
|
737
|
+
run_dir = latest_run_dir()
|
|
738
|
+
cmd = [sys.executable, script, "--loop-dir", LOOP_DIR]
|
|
739
|
+
if run_dir:
|
|
740
|
+
cmd += ["--run-dir", run_dir]
|
|
741
|
+
if response_text:
|
|
742
|
+
cmd += ["--response-text", response_text]
|
|
743
|
+
if flow_gap:
|
|
744
|
+
cmd += ["--flow-gap", flow_gap]
|
|
745
|
+
if run_dir:
|
|
746
|
+
cmd += ["--write-receipt"]
|
|
747
|
+
r = subprocess.run(
|
|
748
|
+
cmd,
|
|
749
|
+
capture_output=True,
|
|
750
|
+
stdin=subprocess.DEVNULL,
|
|
751
|
+
text=True,
|
|
752
|
+
encoding="utf-8",
|
|
753
|
+
errors="replace",
|
|
754
|
+
timeout=15,
|
|
755
|
+
cwd=repo_root,
|
|
756
|
+
)
|
|
757
|
+
payload = json.loads(r.stdout) if (r.stdout or "").strip() else {}
|
|
758
|
+
if isinstance(payload, dict):
|
|
759
|
+
return payload
|
|
760
|
+
return {"ready": False, "reason_code": "oracle_invalid_payload", "tag": "UNVERIFIED"}
|
|
761
|
+
except Exception:
|
|
762
|
+
return {"ready": False, "reason_code": "oracle_error", "tag": "UNVERIFIED"}
|
|
763
|
+
|
|
764
|
+
|
|
638
765
|
def spindle_latched():
|
|
639
766
|
"""True when a spindle handoff exists with an unreleased latch.
|
|
640
767
|
|
|
@@ -781,7 +908,11 @@ def main():
|
|
|
781
908
|
# hand-survey/hand-edit.
|
|
782
909
|
missing_ops = missing_bound_operators()
|
|
783
910
|
if missing_ops:
|
|
784
|
-
|
|
911
|
+
reason = "bound operator missing: %s" % ", ".join(missing_ops)
|
|
912
|
+
_call_simplicio_hbp_append_topic("loop-run-blocked", {
|
|
913
|
+
"fingerprint": _journal_stall()[0], "attempt": iteration, "reason": reason,
|
|
914
|
+
})
|
|
915
|
+
write_handoff(reason, meta, body)
|
|
785
916
|
cleanup_and_stop()
|
|
786
917
|
|
|
787
918
|
stdin = read_stdin_json()
|
|
@@ -792,6 +923,15 @@ def main():
|
|
|
792
923
|
# this turn even if the agent forgot the manual `loop_journal.py record` call.
|
|
793
924
|
auto_record_journal(iteration, has_evidence)
|
|
794
925
|
|
|
926
|
+
# HBP point: stall detected (#128) — when the trailing journal streak crosses the same
|
|
927
|
+
# K=3 threshold the stall detector/planner use, record it into the tamper-evident chain
|
|
928
|
+
# so a later `simplicio hbp verify` can prove WHEN the loop knew it was stuck. Fail-open.
|
|
929
|
+
stall_fp, stall_streak = _journal_stall()
|
|
930
|
+
if stall_streak >= 3:
|
|
931
|
+
_call_simplicio_hbp_append_topic("loop-stall-detected", {
|
|
932
|
+
"fingerprint": stall_fp, "attempt": iteration, "streak": stall_streak,
|
|
933
|
+
})
|
|
934
|
+
|
|
795
935
|
# HRM-style hierarchical planner: re-assess phase on stall or every N iterations.
|
|
796
936
|
# Runs BEFORE the promise gate so the phase context is available.
|
|
797
937
|
_call_hierarchical_planner()
|
|
@@ -804,26 +944,37 @@ def main():
|
|
|
804
944
|
# Pre-promise: front→back flow-audit gate (#80) — mechanical, not prose-only.
|
|
805
945
|
flow_gap = flow_audit_gap()
|
|
806
946
|
|
|
807
|
-
# Completion detection
|
|
947
|
+
# Completion detection is centralized in the shared oracle (#138). The stop hook can still
|
|
948
|
+
# surface watcher/flow state in the re-feed header below, but it no longer decides COMPLETE
|
|
949
|
+
# on its own.
|
|
808
950
|
if promise and resp:
|
|
809
|
-
|
|
810
|
-
if
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
#
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
951
|
+
oracle = completion_oracle_payload(resp, flow_gap or "")
|
|
952
|
+
if oracle.get("ready") and os.path.exists(str(oracle.get("receipt_path") or "")):
|
|
953
|
+
_call_simplicio_hbp_append(iteration, promise, watcher_tag)
|
|
954
|
+
refresh_cross_agent_wiki(include_handoff=False)
|
|
955
|
+
cleanup_and_stop() # (3) promise fulfilled → stop, no handoff needed
|
|
956
|
+
# Legacy converge fixtures and lightweight installs predate persisted run artifacts.
|
|
957
|
+
# Preserve their evidence-gated contract only when the oracle explicitly reports that
|
|
958
|
+
# no run directory exists; every persisted run remains exclusively oracle-authorized.
|
|
959
|
+
if oracle.get("reason_code") == "run_dir_missing":
|
|
960
|
+
match = PROMISE_RE.search(resp)
|
|
961
|
+
if (match and match.group(1).strip() == promise.strip()
|
|
962
|
+
and ((not evidence_required) or has_evidence)
|
|
963
|
+
and watcher_pass and not anchor_pending() and not flow_gap):
|
|
817
964
|
_call_simplicio_hbp_append(iteration, promise, watcher_tag)
|
|
818
965
|
refresh_cross_agent_wiki(include_handoff=False)
|
|
819
|
-
cleanup_and_stop()
|
|
820
|
-
# promise without evidence, or watcher disagrees, or anchor still has open ACs,
|
|
821
|
-
# or a flow-audit gap remains → ignore, keep looping
|
|
966
|
+
cleanup_and_stop()
|
|
822
967
|
# (3') Cursor capture may have raised the flag.
|
|
823
968
|
if os.path.exists(DONE_FLAG) or os.path.exists(LEGACY_DONE_FLAG):
|
|
824
|
-
|
|
969
|
+
oracle = completion_oracle_payload(flow_gap=flow_gap or "")
|
|
970
|
+
if oracle.get("ready") and os.path.exists(str(oracle.get("receipt_path") or "")):
|
|
971
|
+
cleanup_and_stop()
|
|
825
972
|
# (4) Iteration cap — incomplete stop, hand off.
|
|
826
973
|
if max_iter > 0 and iteration >= max_iter:
|
|
974
|
+
_call_simplicio_hbp_append_topic("loop-run-blocked", {
|
|
975
|
+
"fingerprint": _journal_stall()[0], "attempt": iteration,
|
|
976
|
+
"reason": "max_iterations cap reached",
|
|
977
|
+
})
|
|
827
978
|
write_handoff("max_iterations cap reached", meta, body)
|
|
828
979
|
cleanup_and_stop()
|
|
829
980
|
# (5) Spindle handoff — latched handoff overrides re-feed.
|