arkaos 4.0.2 → 4.1.1

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 (214) hide show
  1. package/README.md +2 -2
  2. package/VERSION +1 -1
  3. package/arka/SKILL.md +16 -22
  4. package/arka/skills/flow/SKILL.md +108 -234
  5. package/config/claude-agents/eduardo-copy.md +53 -0
  6. package/config/claude-agents/francisca-tech.md +52 -0
  7. package/config/claude-agents/marta-cqo.md +52 -0
  8. package/config/claude-agents/paulo-tech-lead.md +50 -0
  9. package/config/cognition/schedules.yaml +15 -0
  10. package/config/constitution.yaml +17 -21
  11. package/config/hooks/_lib/workflow-classifier.sh +1 -1
  12. package/config/hooks/post-tool-use.sh +52 -536
  13. package/config/hooks/pre-tool-use.sh +46 -280
  14. package/config/hooks/session-start.sh +8 -10
  15. package/config/hooks/stop.sh +44 -424
  16. package/config/hooks/user-prompt-submit.sh +55 -457
  17. package/core/__pycache__/__init__.cpython-314.pyc +0 -0
  18. package/core/cognition/__pycache__/dreaming.cpython-313.pyc +0 -0
  19. package/core/cognition/dreaming.py +5 -0
  20. package/core/cognition/scheduler/__pycache__/daemon.cpython-313.pyc +0 -0
  21. package/core/cognition/scheduler/daemon.py +10 -0
  22. package/core/forge/__init__.py +2 -0
  23. package/core/forge/__pycache__/__init__.cpython-313.pyc +0 -0
  24. package/core/forge/__pycache__/orchestrator.cpython-313.pyc +0 -0
  25. package/core/forge/__pycache__/persistence.cpython-313.pyc +0 -0
  26. package/core/forge/__pycache__/renderer.cpython-313.pyc +0 -0
  27. package/core/forge/__pycache__/runtime_dispatcher.cpython-313.pyc +0 -0
  28. package/core/forge/__pycache__/schema.cpython-313.pyc +0 -0
  29. package/core/forge/orchestrator.py +50 -27
  30. package/core/forge/persistence.py +16 -0
  31. package/core/forge/renderer.py +16 -0
  32. package/core/forge/runtime_dispatcher.py +34 -19
  33. package/core/forge/schema.py +12 -0
  34. package/core/governance/__pycache__/__init__.cpython-312.pyc +0 -0
  35. package/core/governance/__pycache__/__init__.cpython-314.pyc +0 -0
  36. package/core/governance/__pycache__/closing_marker_check.cpython-313.pyc +0 -0
  37. package/core/governance/__pycache__/closing_marker_check.cpython-314.pyc +0 -0
  38. package/core/governance/__pycache__/compliance_telemetry.cpython-313.pyc +0 -0
  39. package/core/governance/__pycache__/constitution.cpython-312.pyc +0 -0
  40. package/core/governance/__pycache__/constitution.cpython-314.pyc +0 -0
  41. package/core/governance/__pycache__/evidence_checks.cpython-313.pyc +0 -0
  42. package/core/governance/__pycache__/kb_cite_check.cpython-314.pyc +0 -0
  43. package/core/governance/__pycache__/meta_tag_check.cpython-313.pyc +0 -0
  44. package/core/governance/__pycache__/meta_tag_check.cpython-314.pyc +0 -0
  45. package/core/governance/__pycache__/qg_verdict.cpython-313.pyc +0 -0
  46. package/core/governance/__pycache__/review_workflow.cpython-313.pyc +0 -0
  47. package/core/governance/__pycache__/skill_proposer.cpython-313.pyc +0 -0
  48. package/core/governance/__pycache__/skill_proposer.cpython-314.pyc +0 -0
  49. package/core/governance/__pycache__/sycophancy_detector.cpython-314.pyc +0 -0
  50. package/core/governance/closing_marker_check.py +9 -5
  51. package/core/governance/compliance_telemetry.py +1 -1
  52. package/core/governance/compliance_telemetry_cli.py +1 -1
  53. package/core/governance/evidence_checks.py +421 -0
  54. package/core/governance/meta_tag_check.py +36 -0
  55. package/core/governance/qg_verdict.py +78 -0
  56. package/core/governance/review_workflow.py +34 -1
  57. package/core/governance/skill_proposer.py +2 -1
  58. package/core/hooks/__init__.py +12 -0
  59. package/core/hooks/__pycache__/__init__.cpython-312.pyc +0 -0
  60. package/core/hooks/__pycache__/__init__.cpython-313.pyc +0 -0
  61. package/core/hooks/__pycache__/__init__.cpython-314.pyc +0 -0
  62. package/core/hooks/__pycache__/_shared.cpython-312.pyc +0 -0
  63. package/core/hooks/__pycache__/_shared.cpython-313.pyc +0 -0
  64. package/core/hooks/__pycache__/_shared.cpython-314.pyc +0 -0
  65. package/core/hooks/__pycache__/post_tool_use.cpython-312.pyc +0 -0
  66. package/core/hooks/__pycache__/post_tool_use.cpython-313.pyc +0 -0
  67. package/core/hooks/__pycache__/pre_tool_use.cpython-312.pyc +0 -0
  68. package/core/hooks/__pycache__/pre_tool_use.cpython-313.pyc +0 -0
  69. package/core/hooks/__pycache__/pre_tool_use.cpython-314.pyc +0 -0
  70. package/core/hooks/__pycache__/stop.cpython-312.pyc +0 -0
  71. package/core/hooks/__pycache__/stop.cpython-313.pyc +0 -0
  72. package/core/hooks/__pycache__/stop.cpython-314.pyc +0 -0
  73. package/core/hooks/__pycache__/user_prompt_submit.cpython-312.pyc +0 -0
  74. package/core/hooks/__pycache__/user_prompt_submit.cpython-313.pyc +0 -0
  75. package/core/hooks/_shared.py +110 -0
  76. package/core/hooks/post_tool_use.py +689 -0
  77. package/core/hooks/pre_tool_use.py +267 -0
  78. package/core/hooks/stop.py +395 -0
  79. package/core/hooks/user_prompt_submit.py +600 -0
  80. package/core/jobs/__pycache__/__init__.cpython-312.pyc +0 -0
  81. package/core/jobs/__pycache__/__init__.cpython-314.pyc +0 -0
  82. package/core/jobs/__pycache__/auto_doc_worker.cpython-312.pyc +0 -0
  83. package/core/jobs/__pycache__/auto_doc_worker.cpython-314.pyc +0 -0
  84. package/core/jobs/__pycache__/manager.cpython-312.pyc +0 -0
  85. package/core/jobs/__pycache__/manager.cpython-314.pyc +0 -0
  86. package/core/knowledge/__pycache__/embedder.cpython-312.pyc +0 -0
  87. package/core/knowledge/__pycache__/embedder.cpython-313.pyc +0 -0
  88. package/core/knowledge/__pycache__/vector_store.cpython-312.pyc +0 -0
  89. package/core/knowledge/__pycache__/vector_store.cpython-313.pyc +0 -0
  90. package/core/knowledge/embedder.py +118 -11
  91. package/core/knowledge/vector_store.py +111 -14
  92. package/core/memory/__pycache__/session_store.cpython-314.pyc +0 -0
  93. package/core/obsidian/__pycache__/relator.cpython-313.pyc +0 -0
  94. package/core/obsidian/relator.py +18 -8
  95. package/core/runtime/__pycache__/__init__.cpython-312.pyc +0 -0
  96. package/core/runtime/__pycache__/__init__.cpython-313.pyc +0 -0
  97. package/core/runtime/__pycache__/__init__.cpython-314.pyc +0 -0
  98. package/core/runtime/__pycache__/base.cpython-312.pyc +0 -0
  99. package/core/runtime/__pycache__/base.cpython-313.pyc +0 -0
  100. package/core/runtime/__pycache__/base.cpython-314.pyc +0 -0
  101. package/core/runtime/__pycache__/capabilities_cli.cpython-312.pyc +0 -0
  102. package/core/runtime/__pycache__/capabilities_cli.cpython-313.pyc +0 -0
  103. package/core/runtime/__pycache__/claude_code.cpython-312.pyc +0 -0
  104. package/core/runtime/__pycache__/claude_code.cpython-313.pyc +0 -0
  105. package/core/runtime/__pycache__/claude_code.cpython-314.pyc +0 -0
  106. package/core/runtime/__pycache__/codex_cli.cpython-312.pyc +0 -0
  107. package/core/runtime/__pycache__/codex_cli.cpython-313.pyc +0 -0
  108. package/core/runtime/__pycache__/codex_cli.cpython-314.pyc +0 -0
  109. package/core/runtime/__pycache__/cost_governor.cpython-312.pyc +0 -0
  110. package/core/runtime/__pycache__/cost_governor.cpython-313.pyc +0 -0
  111. package/core/runtime/__pycache__/cursor.cpython-312.pyc +0 -0
  112. package/core/runtime/__pycache__/cursor.cpython-313.pyc +0 -0
  113. package/core/runtime/__pycache__/cursor.cpython-314.pyc +0 -0
  114. package/core/runtime/__pycache__/gemini_cli.cpython-312.pyc +0 -0
  115. package/core/runtime/__pycache__/gemini_cli.cpython-313.pyc +0 -0
  116. package/core/runtime/__pycache__/gemini_cli.cpython-314.pyc +0 -0
  117. package/core/runtime/__pycache__/llm_cost_telemetry.cpython-314.pyc +0 -0
  118. package/core/runtime/__pycache__/llm_provider.cpython-312.pyc +0 -0
  119. package/core/runtime/__pycache__/llm_provider.cpython-313.pyc +0 -0
  120. package/core/runtime/__pycache__/llm_provider.cpython-314.pyc +0 -0
  121. package/core/runtime/__pycache__/llm_retry.cpython-312.pyc +0 -0
  122. package/core/runtime/__pycache__/llm_retry.cpython-313.pyc +0 -0
  123. package/core/runtime/__pycache__/llm_retry.cpython-314.pyc +0 -0
  124. package/core/runtime/__pycache__/native_usage.cpython-312.pyc +0 -0
  125. package/core/runtime/__pycache__/native_usage.cpython-313.pyc +0 -0
  126. package/core/runtime/__pycache__/native_usage.cpython-314.pyc +0 -0
  127. package/core/runtime/__pycache__/ollama_provider.cpython-314.pyc +0 -0
  128. package/core/runtime/__pycache__/path_resolver.cpython-312.pyc +0 -0
  129. package/core/runtime/__pycache__/pricing.cpython-312.pyc +0 -0
  130. package/core/runtime/__pycache__/pricing.cpython-313.pyc +0 -0
  131. package/core/runtime/__pycache__/pricing.cpython-314.pyc +0 -0
  132. package/core/runtime/__pycache__/registry.cpython-312.pyc +0 -0
  133. package/core/runtime/__pycache__/registry.cpython-313.pyc +0 -0
  134. package/core/runtime/__pycache__/registry.cpython-314.pyc +0 -0
  135. package/core/runtime/__pycache__/squad_orchestrator.cpython-312.pyc +0 -0
  136. package/core/runtime/base.py +29 -0
  137. package/core/runtime/capabilities_cli.py +74 -0
  138. package/core/runtime/claude_code.py +8 -0
  139. package/core/runtime/codex_cli.py +205 -37
  140. package/core/runtime/cost_governor.py +184 -0
  141. package/core/runtime/cursor.py +8 -0
  142. package/core/runtime/gemini_cli.py +8 -0
  143. package/core/runtime/llm_provider.py +32 -13
  144. package/core/runtime/llm_retry.py +233 -0
  145. package/core/runtime/native_usage.py +186 -0
  146. package/core/runtime/pricing.py +9 -0
  147. package/core/runtime/registry.py +3 -0
  148. package/core/shared/__pycache__/__init__.cpython-314.pyc +0 -0
  149. package/core/shared/__pycache__/safe_session_id.cpython-314.pyc +0 -0
  150. package/core/shared/__pycache__/test_evidence.cpython-313-pytest-9.1.1.pyc +0 -0
  151. package/core/shared/__pycache__/test_evidence.cpython-313.pyc +0 -0
  152. package/core/shared/test_evidence.py +34 -0
  153. package/core/synapse/__pycache__/__init__.cpython-314.pyc +0 -0
  154. package/core/synapse/__pycache__/agent_experiences_layer.cpython-312.pyc +0 -0
  155. package/core/synapse/__pycache__/cache.cpython-314.pyc +0 -0
  156. package/core/synapse/__pycache__/engine.cpython-312.pyc +0 -0
  157. package/core/synapse/__pycache__/engine.cpython-313.pyc +0 -0
  158. package/core/synapse/__pycache__/engine.cpython-314.pyc +0 -0
  159. package/core/synapse/__pycache__/graph_context_layer.cpython-313.pyc +0 -0
  160. package/core/synapse/__pycache__/kb_cache.cpython-312.pyc +0 -0
  161. package/core/synapse/__pycache__/kb_cache.cpython-313.pyc +0 -0
  162. package/core/synapse/__pycache__/kb_cache.cpython-314.pyc +0 -0
  163. package/core/synapse/__pycache__/layers.cpython-312.pyc +0 -0
  164. package/core/synapse/__pycache__/layers.cpython-313.pyc +0 -0
  165. package/core/synapse/__pycache__/layers.cpython-314.pyc +0 -0
  166. package/core/synapse/engine.py +7 -0
  167. package/core/synapse/graph_context_layer.py +212 -0
  168. package/core/synapse/kb_cache.py +3 -0
  169. package/core/synapse/layers.py +104 -20
  170. package/core/workflow/__pycache__/__init__.cpython-314.pyc +0 -0
  171. package/core/workflow/__pycache__/enforcer.cpython-313.pyc +0 -0
  172. package/core/workflow/__pycache__/engine.cpython-314.pyc +0 -0
  173. package/core/workflow/__pycache__/flow_enforcer.cpython-312.pyc +0 -0
  174. package/core/workflow/__pycache__/flow_enforcer.cpython-313.pyc +0 -0
  175. package/core/workflow/__pycache__/flow_enforcer.cpython-314.pyc +0 -0
  176. package/core/workflow/__pycache__/gate_checkpoint.cpython-312.pyc +0 -0
  177. package/core/workflow/__pycache__/gate_checkpoint.cpython-313.pyc +0 -0
  178. package/core/workflow/__pycache__/gate_checkpoint.cpython-314.pyc +0 -0
  179. package/core/workflow/__pycache__/loader.cpython-314.pyc +0 -0
  180. package/core/workflow/__pycache__/marker_cache.cpython-314.pyc +0 -0
  181. package/core/workflow/__pycache__/research_gate.cpython-313.pyc +0 -0
  182. package/core/workflow/__pycache__/research_gate.cpython-314.pyc +0 -0
  183. package/core/workflow/__pycache__/rules_registry.cpython-313.pyc +0 -0
  184. package/core/workflow/__pycache__/schema.cpython-314.pyc +0 -0
  185. package/core/workflow/__pycache__/specialist_enforcer.cpython-312.pyc +0 -0
  186. package/core/workflow/__pycache__/specialist_enforcer.cpython-313.pyc +0 -0
  187. package/core/workflow/__pycache__/state.cpython-314.pyc +0 -0
  188. package/core/workflow/enforcer.py +5 -16
  189. package/core/workflow/flow_enforcer.py +36 -11
  190. package/core/workflow/gate_checkpoint.py +149 -0
  191. package/core/workflow/research_gate.py +15 -3
  192. package/core/workflow/rules_registry.py +137 -389
  193. package/core/workflow/specialist_enforcer.py +21 -4
  194. package/dashboard/app/pages/knowledge/index.vue +4 -1
  195. package/departments/ops/skills/n8n-flow/SKILL.md +13 -0
  196. package/departments/ops/skills/workflow-automate/SKILL.md +16 -0
  197. package/departments/quality/SKILL.md +45 -5
  198. package/departments/quality/agents/copy-director.yaml +3 -1
  199. package/departments/quality/agents/tech-director.yaml +3 -1
  200. package/installer/adapters/claude-code.js +46 -2
  201. package/installer/config-seed.js +39 -14
  202. package/installer/doctor.js +8 -0
  203. package/installer/graphify.js +153 -0
  204. package/installer/index.js +20 -0
  205. package/installer/init.js +11 -0
  206. package/installer/update.js +19 -0
  207. package/package.json +1 -1
  208. package/pyproject.toml +3 -1
  209. package/scripts/__pycache__/dashboard-api.cpython-313.pyc +0 -0
  210. package/scripts/__pycache__/knowledge-index.cpython-313.pyc +0 -0
  211. package/scripts/__pycache__/synapse-bridge.cpython-312.pyc +0 -0
  212. package/scripts/__pycache__/synapse-bridge.cpython-313.pyc +0 -0
  213. package/scripts/knowledge-index.py +8 -2
  214. package/scripts/synapse-bridge.py +102 -26
@@ -0,0 +1,149 @@
1
+ """Gate checkpoint persistence for the 4-gate evidence flow (v4.1.0).
2
+
3
+ Invoked by the Stop hook once per assistant turn. Scans recent assistant
4
+ messages for `[arka:gate:N]` markers and persists the furthest gate
5
+ observed to BOTH stores:
6
+
7
+ - ``~/.arkaos/workflow-state.json`` via :mod:`core.workflow.state`
8
+ - ``~/.arkaos/sessions/<id>/workflow-state.json`` via
9
+ :class:`core.memory.session_store.SessionStore` (consumed by
10
+ :mod:`core.memory.rehydrator` at SessionStart)
11
+
12
+ This is what makes a rate-limit or context-exhaustion interruption
13
+ resumable at the correct gate instead of restarting the flow. See ADR
14
+ ``docs/adr/2026-07-04-evidence-flow.md``.
15
+
16
+ Never raises: checkpointing must not break a Stop hook.
17
+ """
18
+
19
+ import json
20
+ import re
21
+ import sys
22
+
23
+ from core.memory.session_store import SessionStore, WorkflowSnapshot
24
+ from core.shared.safe_session_id import safe_session_id
25
+ from core.workflow import state
26
+ from core.workflow.flow_enforcer import _load_last_assistant_messages
27
+
28
+ GATES: tuple[str, ...] = (
29
+ "gate-1-context",
30
+ "gate-2-plan",
31
+ "gate-3-execute",
32
+ "gate-4-review",
33
+ )
34
+
35
+ GATE_MARKER_RE = re.compile(r"\[arka:gate:([1-4])\]", re.IGNORECASE)
36
+ GATE3_EVIDENCE_RE = re.compile(
37
+ r"\[arka:gate:3\]\s*evidence:\s*(?P<evidence>.+)", re.IGNORECASE
38
+ )
39
+
40
+ _SCAN_WINDOW = 20
41
+
42
+
43
+ def extract_latest_gate(messages: list[str]) -> int | None:
44
+ """Return the highest gate number observed across the messages."""
45
+ latest: int | None = None
46
+ for text in messages:
47
+ for match in GATE_MARKER_RE.finditer(text):
48
+ number = int(match.group(1))
49
+ if latest is None or number > latest:
50
+ latest = number
51
+ return latest
52
+
53
+
54
+ def extract_gate3_evidence(messages: list[str]) -> str | None:
55
+ """Return the most recent Gate-3 evidence line, if any."""
56
+ evidence: str | None = None
57
+ for text in messages:
58
+ for match in GATE3_EVIDENCE_RE.finditer(text):
59
+ evidence = match.group("evidence").strip()
60
+ return evidence
61
+
62
+
63
+ def _phase_statuses(latest: int) -> dict[str, str]:
64
+ """Map each gate to completed/in_progress/pending given the latest gate."""
65
+ statuses: dict[str, str] = {}
66
+ for index, gate in enumerate(GATES, start=1):
67
+ if index < latest:
68
+ statuses[gate] = "completed"
69
+ elif index == latest:
70
+ statuses[gate] = "in_progress"
71
+ else:
72
+ statuses[gate] = "pending"
73
+ return statuses
74
+
75
+
76
+ def _persist_global_state(latest: int, project: str, evidence: str | None) -> None:
77
+ current = state.get_state()
78
+ if current is None or current.get("workflow") != "evidence-flow":
79
+ state.init_workflow("evidence-flow", project, list(GATES))
80
+ for gate, status in _phase_statuses(latest).items():
81
+ artifact = None
82
+ if gate == GATES[2] and evidence:
83
+ artifact = evidence
84
+ state.update_phase(gate, status, artifact=artifact)
85
+
86
+
87
+ def _persist_session_snapshot(
88
+ session_id: str, latest: int, evidence: str | None
89
+ ) -> None:
90
+ safe = safe_session_id(session_id)
91
+ if safe is None:
92
+ return
93
+ store = SessionStore(safe)
94
+ snapshot = WorkflowSnapshot(
95
+ workflow_id=safe,
96
+ workflow_name="evidence-flow",
97
+ current_phase=GATES[latest - 1],
98
+ phases=_phase_statuses(latest),
99
+ artifacts=[evidence] if evidence else [],
100
+ )
101
+ store.save_workflow_snapshot(snapshot)
102
+
103
+
104
+ def checkpoint(
105
+ transcript_path: str,
106
+ session_id: str,
107
+ project: str = "",
108
+ messages: list[str] | None = None,
109
+ ) -> dict | None:
110
+ """Persist the furthest observed gate. Returns a summary dict or None.
111
+
112
+ ``messages`` (PR-6 hook consolidation): pre-parsed assistant messages;
113
+ when None the transcript is read from ``transcript_path``.
114
+ """
115
+ try:
116
+ if messages is None:
117
+ messages = _load_last_assistant_messages(
118
+ transcript_path, _SCAN_WINDOW
119
+ )
120
+ else:
121
+ messages = messages[-_SCAN_WINDOW:]
122
+ latest = extract_latest_gate(messages)
123
+ if latest is None:
124
+ return None
125
+ evidence = extract_gate3_evidence(messages)
126
+ _persist_global_state(latest, project, evidence)
127
+ _persist_session_snapshot(session_id, latest, evidence)
128
+ return {
129
+ "gate": latest,
130
+ "current_phase": GATES[latest - 1],
131
+ "evidence": evidence,
132
+ }
133
+ except Exception: # noqa: BLE001 — a Stop hook must never break the turn
134
+ return None
135
+
136
+
137
+ def main(argv: list[str]) -> int:
138
+ """CLI: gate_checkpoint <transcript_path> <session_id> [project]."""
139
+ if len(argv) < 2:
140
+ return 0
141
+ project = argv[2] if len(argv) > 2 else ""
142
+ result = checkpoint(argv[0], argv[1], project)
143
+ if result is not None:
144
+ print(json.dumps(result))
145
+ return 0
146
+
147
+
148
+ if __name__ == "__main__":
149
+ raise SystemExit(main(sys.argv[1:]))
@@ -2,7 +2,8 @@
2
2
 
3
3
  Intercepts calls to Context7, WebSearch, WebFetch and Firecrawl MCP tools.
4
4
  Runs AFTER flow_enforcer in the PreToolUse hook chain. Independent gate:
5
- both must pass. Feature-flagged behind `hooks.kbFirst` (default false).
5
+ both must pass. Feature-flagged behind `hooks.kbFirst` (default TRUE
6
+ since v4.1 — see `_feature_flag_on`).
6
7
 
7
8
  Behaviour contract (plan 2026-04-20-intelligence-v2, §Épico B):
8
9
  1. Tool not in RESEARCH_EXTERNAL_TOOLS → allow.
@@ -106,13 +107,24 @@ _safe_session_id = _safe_session_id_module.safe_session_id
106
107
 
107
108
 
108
109
  def _feature_flag_on() -> bool:
110
+ """Resolve the `hooks.kbFirst` flag.
111
+
112
+ Default-when-missing semantics (operator decision, PR-3 v4.1):
113
+ KB-first is ON out of the box. The installer template
114
+ (installer/config-seed.js) seeds `hooks.kbFirst = true`, so a missing
115
+ config file or a missing key now defaults to TRUE — matching the
116
+ seeded template instead of contradicting it. An explicit
117
+ `"kbFirst": false` in the user's config always wins. A corrupt/
118
+ unreadable config returns False (fail-open): the gate must never
119
+ deny tool calls based on intent it could not read.
120
+ """
109
121
  if not CONFIG_PATH.exists():
110
- return False
122
+ return True
111
123
  try:
112
124
  data = json.loads(CONFIG_PATH.read_text(encoding="utf-8"))
113
125
  except (json.JSONDecodeError, OSError):
114
126
  return False
115
- return bool(data.get("hooks", {}).get("kbFirst", False))
127
+ return bool(data.get("hooks", {}).get("kbFirst", True))
116
128
 
117
129
 
118
130
  def _bypass_env_active() -> bool: