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.
Files changed (138) hide show
  1. {simplicio_loop-3.23.0/simplicio_loop.egg-info → simplicio_loop-3.25.1}/PKG-INFO +3 -4
  2. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/PYPI.md +1 -1
  3. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/README.md +62 -77
  4. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/pyproject.toml +4 -6
  5. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/__init__.py +2 -2
  6. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/hooks/action_gate.py +43 -3
  7. simplicio_loop-3.25.1/simplicio_loop/_bundle/hooks/loop_capture.py +31 -0
  8. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/hooks/loop_stop.py +181 -30
  9. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/hooks/pre-commit.py +5 -2
  10. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/hooks/simplicio_dashboard.py +19 -1
  11. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/hooks/simplicio_watch.py +32 -3
  12. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/scripts/hierarchical_planner.py +6 -0
  13. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/SKILL.md +24 -22
  14. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/references/bound-operators.md +13 -11
  15. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/references/orchestration.md +3 -2
  16. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/references/quality-safety-delivery.md +10 -3
  17. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-tasks/references/orchestration.md +3 -2
  18. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-tasks/references/quality-safety-delivery.md +20 -3
  19. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1/simplicio_loop/_bundle}/tests/test_loop_e2e.py +162 -6
  20. simplicio_loop-3.25.1/simplicio_loop/cli.py +651 -0
  21. simplicio_loop-3.25.1/simplicio_loop/delivery.py +225 -0
  22. simplicio_loop-3.25.1/simplicio_loop/drain.py +349 -0
  23. simplicio_loop-3.25.1/simplicio_loop/evidence.py +395 -0
  24. simplicio_loop-3.25.1/simplicio_loop/ops_ledger.py +283 -0
  25. simplicio_loop-3.25.1/simplicio_loop/oracle.py +261 -0
  26. simplicio_loop-3.25.1/simplicio_loop/phase_events.py +139 -0
  27. simplicio_loop-3.25.1/simplicio_loop/runner.py +1930 -0
  28. simplicio_loop-3.25.1/simplicio_loop/source_state.py +122 -0
  29. simplicio_loop-3.25.1/simplicio_loop/task_contract.py +673 -0
  30. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1/simplicio_loop.egg-info}/PKG-INFO +3 -4
  31. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop.egg-info/SOURCES.txt +35 -1
  32. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop.egg-info/requires.txt +1 -2
  33. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_action_gate.py +32 -20
  34. simplicio_loop-3.25.1/tests/test_check_e2e_demo_contract.py +13 -0
  35. simplicio_loop-3.25.1/tests/test_check_e2e_installed.py +97 -0
  36. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_claims_audit.py +44 -0
  37. simplicio_loop-3.25.1/tests/test_clean_env_contract.py +33 -0
  38. simplicio_loop-3.25.1/tests/test_completion_oracle.py +321 -0
  39. simplicio_loop-3.25.1/tests/test_completion_oracle_matrix.py +51 -0
  40. simplicio_loop-3.25.1/tests/test_delivery_source_identity.py +24 -0
  41. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_doctor_smoke.py +18 -2
  42. simplicio_loop-3.25.1/tests/test_drain.py +140 -0
  43. simplicio_loop-3.25.1/tests/test_drain_cli.py +136 -0
  44. simplicio_loop-3.25.1/tests/test_e2e_demo_audit.py +97 -0
  45. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_evidence_chain.py +54 -2
  46. simplicio_loop-3.25.1/tests/test_evidence_receipt.py +252 -0
  47. simplicio_loop-3.25.1/tests/test_fan_out_scheduler.py +193 -0
  48. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_fan_out_unit.py +46 -0
  49. simplicio_loop-3.25.1/tests/test_hbp_topics.py +299 -0
  50. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_hooks_coverage.py +16 -5
  51. simplicio_loop-3.25.1/tests/test_ledger_cli.py +81 -0
  52. simplicio_loop-3.25.1/tests/test_locked_append.py +168 -0
  53. {simplicio_loop-3.23.0/simplicio_loop/_bundle → simplicio_loop-3.25.1}/tests/test_loop_e2e.py +162 -6
  54. simplicio_loop-3.25.1/tests/test_mirror_parity.py +34 -0
  55. simplicio_loop-3.25.1/tests/test_operator_batch.py +119 -0
  56. simplicio_loop-3.25.1/tests/test_operator_worktree_batch.py +142 -0
  57. simplicio_loop-3.25.1/tests/test_ops_ledger.py +163 -0
  58. simplicio_loop-3.25.1/tests/test_phase_events.py +74 -0
  59. simplicio_loop-3.25.1/tests/test_planes_golden_eval.py +89 -0
  60. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_precommit_bundle_sync.py +40 -9
  61. simplicio_loop-3.25.1/tests/test_runner_cli.py +1001 -0
  62. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_system_check.py +19 -2
  63. simplicio_loop-3.25.1/tests/test_task_backlog.py +602 -0
  64. simplicio_loop-3.25.1/tests/test_task_contract_cli.py +79 -0
  65. simplicio_loop-3.25.1/tests/test_task_contract_unit.py +146 -0
  66. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_token_budget.py +5 -3
  67. simplicio_loop-3.25.1/tests/test_watcher_binding.py +23 -0
  68. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_worker_cli_contract.py +8 -1
  69. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_worker_selftests.py +50 -1
  70. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_worker_smoke.py +92 -15
  71. simplicio_loop-3.25.1/tests/test_worktree_queue.py +127 -0
  72. simplicio_loop-3.23.0/simplicio_loop/_bundle/hooks/loop_capture.py +0 -86
  73. simplicio_loop-3.23.0/simplicio_loop/cli.py +0 -180
  74. simplicio_loop-3.23.0/tests/test_task_backlog.py +0 -225
  75. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/LICENSE +0 -0
  76. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/MANIFEST.in +0 -0
  77. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/setup.cfg +0 -0
  78. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/hooks/README.md +0 -0
  79. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/hooks/hooks.claude.json +0 -0
  80. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/hooks/hooks.json +0 -0
  81. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/hooks/orient_clamp.py +0 -0
  82. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/hooks/orient_rewrite.py +0 -0
  83. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/scripts/cross_agent_wiki.py +0 -0
  84. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-autoresearch/SKILL.md +0 -0
  85. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-compress/SKILL.md +0 -0
  86. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-learn/SKILL.md +0 -0
  87. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/references/agentsview-adapter.md +0 -0
  88. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/references/azure-devops-adapter.md +0 -0
  89. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/references/claims-gate.md +0 -0
  90. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/references/cross-agent-wiki.md +0 -0
  91. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/references/cross-repo-integration.md +0 -0
  92. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/references/extension-points.md +0 -0
  93. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/references/hierarchical-planner.md +0 -0
  94. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/references/lmcache-adapter.md +0 -0
  95. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/references/run-journal-stall-detector.md +0 -0
  96. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/references/spindle-handoff.md +0 -0
  97. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/references/standing-loop-247.md +0 -0
  98. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/references/token-capture.md +0 -0
  99. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/references/token-economy.md +0 -0
  100. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/references/triage-verify-detail.md +0 -0
  101. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/references/understand-anything-adapter.md +0 -0
  102. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/references/video-evidence.md +0 -0
  103. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-loop/references/web-evidence.md +0 -0
  104. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-orient/SKILL.md +0 -0
  105. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-review/SKILL.md +0 -0
  106. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-tasks/SKILL.md +0 -0
  107. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-tasks/references/agentsview-adapter.md +0 -0
  108. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-tasks/references/azure-devops-adapter.md +0 -0
  109. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-tasks/references/extension-points.md +0 -0
  110. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-tasks/references/lmcache-adapter.md +0 -0
  111. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-tasks/references/standing-loop-247.md +0 -0
  112. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-tasks/references/token-capture.md +0 -0
  113. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-tasks/references/token-economy.md +0 -0
  114. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-tasks/references/understand-anything-adapter.md +0 -0
  115. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-tasks/references/video-evidence.md +0 -0
  116. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/skills/simplicio-tasks/references/web-evidence.md +0 -0
  117. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/tests/_selfrun.py +0 -0
  118. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop/_bundle/tests/test_cross_agent_wiki.py +0 -0
  119. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop.egg-info/dependency_links.txt +0 -0
  120. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop.egg-info/entry_points.txt +0 -0
  121. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/simplicio_loop.egg-info/top_level.txt +0 -0
  122. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_agentsview_adapter.py +0 -0
  123. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_autoresearch.py +0 -0
  124. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_az_boards_adapter.py +0 -0
  125. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_claims_manifest_unit.py +0 -0
  126. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_cross_agent_wiki.py +0 -0
  127. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_dashboard_hook.py +0 -0
  128. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_fan_out_flow.py +0 -0
  129. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_flow_audit.py +0 -0
  130. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_hierarchical_planner.py +0 -0
  131. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_impact_audit.py +0 -0
  132. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_install_lib.py +0 -0
  133. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_learn_pipeline_removed.py +0 -0
  134. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_repo_conventions_architecture.py +0 -0
  135. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_schema_verify_integration.py +0 -0
  136. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_schema_verify_unit.py +0 -0
  137. {simplicio_loop-3.23.0 → simplicio_loop-3.25.1}/tests/test_toon_codec.py +0 -0
  138. {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.23.0
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-mapper>=0.17.0
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
- ![simplicio-loop](https://raw.githubusercontent.com/wesleysimplicio/simplicio-loop/main/assets/simplicio-loop-hero.jpg)
37
+ ![simplicio-loop](https://raw.githubusercontent.com/wesleysimplicio/simplicio-loop/main/assets/simplicio-loop-hero-2026.png)
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
- ![simplicio-loop](https://raw.githubusercontent.com/wesleysimplicio/simplicio-loop/main/assets/simplicio-loop-hero.jpg)
8
+ ![simplicio-loop](https://raw.githubusercontent.com/wesleysimplicio/simplicio-loop/main/assets/simplicio-loop-hero-2026.png)
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.jpg" alt="simplicio-loop" width="920" />
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 TD
214
- subgraph SRC["1 · Demand sources (any adapter)"]
215
- direction LR
216
- S1["GitHub Issues / PRs / CI"]
217
- S2["Jira · Azure DevOps · Linear · ClickUp · Notion · agentsview · Understand Anything (orient)"]
218
- S3["Assigns · TODO/FIXME · CVE · local files · LMCache (inference accelerator)"]
219
- end
220
- SRC --> PF
221
- subgraph PF["2 · Pre-flight gates"]
222
- direction LR
223
- P1["source auth + scopes"]
224
- P2["runtime/tools ready"]
225
- P3["arm 24/7 watcher + STOP path"]
226
- end
227
- PF --> DISC
228
- subgraph DISC["3 · Discover + normalize"]
229
- direction LR
230
- D1["source_adapter: list metadata only"]
231
- D2["normalize to canonical schema"]
232
- D3["dedup id+title+fingerprint+branch/PR"]
233
- D4["dependency DAG"]
234
- end
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 two loop operators (`simplicio-mapper` + `simplicio-cli`, auto-handling PEP 668 / externally-managed
534
- Python and symlinking the binaries onto `PATH`), the **full Python stack** (the package itself),
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 two loop operators, the 7 skills, the loop hooks, 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.23.0"
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 REQUIRES two operators (see .claude/skills/simplicio-loop/SKILL.md):
25
- # simplicio-mapper -> the repo-survey step (binds `orient`)
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-mapper>=0.17.0",
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`.
@@ -13,6 +13,6 @@ try:
13
13
  try:
14
14
  __version__ = _v("simplicio-loop")
15
15
  except PackageNotFoundError:
16
- __version__ = "3.22.6"
16
+ __version__ = "3.25.1"
17
17
  except Exception: # pragma: no cover
18
- __version__ = "3.22.6"
18
+ __version__ = "3.25.1"
@@ -235,8 +235,46 @@ def gate_command(cmd, staged=False):
235
235
 
236
236
 
237
237
  # ---------------------------------------------------------------------------------------------
238
- def _emit_and_exit(verdict, pretooluse=False):
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
- and build/cache noise (`__pycache__`, `.pyc`) that this very check's own module import can
331
- create — that self-inflicted false-positive is exactly why both are filtered here.
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/") and "__pycache__" not in f and not f.endswith((".pyc", ".pyo"))
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
- payload = json.dumps({
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
- write_handoff("bound operator missing: %s" % ", ".join(missing_ops), meta, body)
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 (capture folded in for single-hook runtimes like Claude).
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
- m = PROMISE_RE.search(resp)
810
- if m and m.group(1).strip() == promise.strip():
811
- # The promise is honored only with evidence AND watcher verification AND no
812
- # acceptance criterion still open in the task anchor AND no open flow-audit gap.
813
- # The watcher-gate ensures the agent's result was independently re-executed and
814
- # matched before the promise is accepted — corrective gate per Asolaria.
815
- if (((not evidence_required) or has_evidence) and watcher_pass
816
- and not anchor_pending() and not flow_gap):
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() # (3) promise fulfilled → stop, no handoff needed
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
- cleanup_and_stop()
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.