arkaos 4.3.0 → 4.3.2

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 (62) hide show
  1. package/README.md +1 -1
  2. package/VERSION +1 -1
  3. package/arka/SKILL.md +4 -4
  4. package/arka/skills/costs/SKILL.md +1 -1
  5. package/arka/skills/fusion/SKILL.md +13 -6
  6. package/config/claude-agents/marta-cqo.md +1 -1
  7. package/config/claude-agents/paulo-tech-lead.md +1 -1
  8. package/config/constitution.yaml +5 -5
  9. package/config/hooks/_lib/arka_python.ps1 +43 -0
  10. package/config/hooks/_lib/arka_python.sh +63 -0
  11. package/config/hooks/agent-provision.sh +8 -1
  12. package/config/hooks/cwd-changed.sh +6 -2
  13. package/config/hooks/post-tool-use-v2.sh +6 -2
  14. package/config/hooks/post-tool-use.sh +8 -17
  15. package/config/hooks/pre-tool-use.ps1 +8 -6
  16. package/config/hooks/pre-tool-use.sh +8 -17
  17. package/config/hooks/session-start.sh +40 -15
  18. package/config/hooks/stop.ps1 +7 -5
  19. package/config/hooks/stop.sh +8 -17
  20. package/config/hooks/token-hygiene.sh +5 -1
  21. package/config/hooks/user-prompt-submit-v2.sh +13 -9
  22. package/config/hooks/user-prompt-submit.sh +8 -17
  23. package/core/fusion/__pycache__/cli.cpython-313.pyc +0 -0
  24. package/core/fusion/__pycache__/panel_builder.cpython-313.pyc +0 -0
  25. package/core/fusion/cli.py +57 -6
  26. package/core/fusion/panel_builder.py +58 -0
  27. package/core/hooks/__pycache__/post_tool_use.cpython-313.pyc +0 -0
  28. package/core/hooks/__pycache__/pre_tool_use.cpython-313.pyc +0 -0
  29. package/core/hooks/__pycache__/pre_tool_use.cpython-314.pyc +0 -0
  30. package/core/hooks/__pycache__/user_prompt_submit.cpython-313.pyc +0 -0
  31. package/core/hooks/post_tool_use.py +26 -12
  32. package/core/hooks/pre_tool_use.py +4 -0
  33. package/core/hooks/user_prompt_submit.py +7 -0
  34. package/core/memory/__pycache__/rehydrator.cpython-312.pyc +0 -0
  35. package/core/memory/__pycache__/session_store.cpython-312.pyc +0 -0
  36. package/core/runtime/__pycache__/llm_provider.cpython-312.pyc +0 -0
  37. package/core/runtime/__pycache__/model_router.cpython-313.pyc +0 -0
  38. package/core/runtime/__pycache__/model_routing_context.cpython-313.pyc +0 -0
  39. package/core/runtime/__pycache__/openrouter_provider.cpython-312.pyc +0 -0
  40. package/core/runtime/__pycache__/runtime_models.cpython-313.pyc +0 -0
  41. package/core/runtime/model_router.py +17 -0
  42. package/core/runtime/model_routing_context.py +82 -0
  43. package/core/runtime/runtime_models.py +51 -0
  44. package/core/workflow/__pycache__/flow_authorization.cpython-313.pyc +0 -0
  45. package/core/workflow/__pycache__/flow_authorization.cpython-314.pyc +0 -0
  46. package/core/workflow/__pycache__/flow_enforcer.cpython-313.pyc +0 -0
  47. package/core/workflow/__pycache__/flow_enforcer.cpython-314.pyc +0 -0
  48. package/core/workflow/flow_authorization.py +181 -0
  49. package/core/workflow/flow_enforcer.py +56 -11
  50. package/core/workflow/state_reader.sh +12 -5
  51. package/dashboard/app/pages/models.vue +23 -4
  52. package/departments/ops/skills/update/SKILL.md +2 -2
  53. package/departments/ops/skills/update/references/sync-engine.md +1 -1
  54. package/departments/quality/SKILL.md +1 -1
  55. package/installer/cli.js +21 -0
  56. package/installer/index.js +14 -0
  57. package/installer/update.js +13 -0
  58. package/package.json +5 -2
  59. package/pyproject.toml +1 -1
  60. package/scripts/__pycache__/dashboard-api.cpython-313.pyc +0 -0
  61. package/scripts/dashboard-api.py +13 -2
  62. package/scripts/seed_initial_patterns.py +1 -1
package/README.md CHANGED
@@ -491,7 +491,7 @@ npx arkaos uninstall # Remove ArkaOS
491
491
  **Release pipeline (operator-side, headless):**
492
492
 
493
493
  ```
494
- python -m core.release.preflight_cli --expected-npm-user <user>
494
+ ~/.arkaos/bin/arka-py -m core.release.preflight_cli --expected-npm-user <user>
495
495
  # Step 0 NON-NEGOTIABLE before any tag/push/publish (v2.43.0+).
496
496
  # Six checks: version-alignment / npm-auth / npm-publish-capability /
497
497
  # gh-auth / git-remote / git-clean. Plus no-client-name-leaks
package/VERSION CHANGED
@@ -1 +1 @@
1
- 4.3.0
1
+ 4.3.2
package/arka/SKILL.md CHANGED
@@ -180,10 +180,10 @@ violation (squad-routing, arka-supremacy, spec-driven, mandatory-qa).
180
180
  | Command | Description |
181
181
  |---------|-------------|
182
182
  | `/arka status` | System status (version, departments, agents, active projects). Includes **LLM costs (24h)** section: top-line cost + cache hit rate + call count from `core.runtime.llm_cost_telemetry.summarise(period="today")`. Also includes **Enforcement (24h)** section: total calls, block rate, top blocked tools/reasons from `core.governance.enforcement_telemetry.summarise(period="today")` (PR19 v2.41.0). Plus **Reorganization (today)** section: today's proposal path + artifact count from `core.cognition.reorganizer_scheduler.status_summary()` (PR24 v2.46.0). |
183
- | `/arka costs [period]` | LLM cost visibility — aggregates telemetry by day/week/month/all, with top expensive sessions. See `arka/skills/costs/SKILL.md`. Shells out to `python -m core.runtime.llm_cost_telemetry_cli <period>`. |
184
- | `/arka enforcement [period]` | Enforcement compliance — aggregates flow-marker enforcement telemetry by day/week/month/all. Shows block rate, top blocked tools, top reasons. Shells out to `python -m core.governance.enforcement_telemetry_cli <period>`. |
185
- | `/arka compliance [period]` | Behavior compliance summary (PR29 v2.48.0) — aggregates stop-hook telemetry by day/week/month/all. Shows rates for the four contracts: closing marker, `[arka:meta]` tag, KB citation pass, sycophancy clean. Shells out to `python -m core.governance.compliance_telemetry_cli <period>`. |
186
- | `/arka reorganize [--since-days N]` | Dreaming → Agent reorganizer. Aggregates recent KB pattern/anti-pattern/lesson artifacts (default last 7 days) into a markdown proposal at `~/.arkaos/reorganize-proposals/<date>.md`. **Propose-only** — never modifies agent YAMLs. Sanitizes client identifiers from titles and body excerpts; drops `tags:` field entirely to prevent project-name leaks. **Auto-fires on session start when today's proposal is missing** (PR24 v2.46.0 stale-aware trigger, 30s timeout, background). Shells out to `python -m core.cognition.reorganizer_cli [--since-days N] [--dry-run]`. |
183
+ | `/arka costs [period]` | LLM cost visibility — aggregates telemetry by day/week/month/all, with top expensive sessions. See `arka/skills/costs/SKILL.md`. Shells out to `~/.arkaos/bin/arka-py -m core.runtime.llm_cost_telemetry_cli <period>`. |
184
+ | `/arka enforcement [period]` | Enforcement compliance — aggregates flow-marker enforcement telemetry by day/week/month/all. Shows block rate, top blocked tools, top reasons. Shells out to `~/.arkaos/bin/arka-py -m core.governance.enforcement_telemetry_cli <period>`. |
185
+ | `/arka compliance [period]` | Behavior compliance summary (PR29 v2.48.0) — aggregates stop-hook telemetry by day/week/month/all. Shows rates for the four contracts: closing marker, `[arka:meta]` tag, KB citation pass, sycophancy clean. Shells out to `~/.arkaos/bin/arka-py -m core.governance.compliance_telemetry_cli <period>`. |
186
+ | `/arka reorganize [--since-days N]` | Dreaming → Agent reorganizer. Aggregates recent KB pattern/anti-pattern/lesson artifacts (default last 7 days) into a markdown proposal at `~/.arkaos/reorganize-proposals/<date>.md`. **Propose-only** — never modifies agent YAMLs. Sanitizes client identifiers from titles and body excerpts; drops `tags:` field entirely to prevent project-name leaks. **Auto-fires on session start when today's proposal is missing** (PR24 v2.46.0 stale-aware trigger, 30s timeout, background). Shells out to `~/.arkaos/bin/arka-py -m core.cognition.reorganizer_cli [--since-days N] [--dry-run]`. |
187
187
  | `/arka standup` | Daily standup (projects, priorities, blockers, updates) |
188
188
  | `/arka monitor` | System health monitoring |
189
189
  | `/arka onboard <path>` | Onboard an existing project into ArkaOS |
@@ -42,7 +42,7 @@ surfaces usage and emits soft advisories. It never blocks a call.
42
42
  This skill shells out to the Python CLI:
43
43
 
44
44
  ```bash
45
- python -m core.runtime.llm_cost_telemetry_cli <period>
45
+ ~/.arkaos/bin/arka-py -m core.runtime.llm_cost_telemetry_cli <period>
46
46
  ```
47
47
 
48
48
  Source:
@@ -24,9 +24,9 @@ mechanical work economises.
24
24
  Run and read all three:
25
25
 
26
26
  ```bash
27
- python -m core.runtime.model_router_cli discover # Ollama + local models
28
- python -m core.runtime.model_router_cli --json # current routing
29
- python -m core.runtime.model_router_cli usage --period week --json
27
+ ~/.arkaos/bin/arka-py -m core.runtime.model_router_cli discover # Ollama + local models
28
+ ~/.arkaos/bin/arka-py -m core.runtime.model_router_cli --json # current routing
29
+ ~/.arkaos/bin/arka-py -m core.runtime.model_router_cli usage --period week --json
30
30
  ```
31
31
 
32
32
  Also check key presence (never print values): `OPENROUTER_API_KEY` /
@@ -75,11 +75,18 @@ Produce a table: role → provider/model + ONE-line rationale each. Rules:
75
75
 
76
76
  ```bash
77
77
  npx arkaos models set <role> <provider>/<model> --effort <effort>
78
+ npx arkaos fusion --save "<a first question>" # persist a panel + run it
78
79
  ```
79
80
 
80
- For fusion panel/judge and aliases, edit `~/.arkaos/models.yaml` directly
81
- (keep comments). Show the final `npx arkaos models` table as proof and
82
- remind: the dashboard Models page shows the same state + live usage.
81
+ `npx arkaos fusion --show` prints the panel that would run (a sensible
82
+ default is built from the machine's models when none is configured);
83
+ `npx arkaos fusion --save "question"` writes the panel into
84
+ `~/.arkaos/models.yaml` and runs it; `npx arkaos fusion "question"` runs
85
+ without persisting. For fine-grained panel/judge edits, edit
86
+ `~/.arkaos/models.yaml` directly (keep comments). Show the final
87
+ `npx arkaos models` table as proof and remind: the dashboard Models page
88
+ shows the same state + live usage, and the SessionStart hook now injects
89
+ the routing so it governs agent dispatch.
83
90
 
84
91
  ## Hard rules
85
92
 
@@ -23,7 +23,7 @@ evidence, not from model size.
23
23
  ## Review Rubric (evidence interpretation, not role-play)
24
24
 
25
25
  1. Run the engine first — no verdict without a report:
26
- `python -m core.governance.evidence_checks <project_dir> [--changed-files ...] [--test-command '...'] --json`
26
+ `~/.arkaos/bin/arka-py -m core.governance.evidence_checks <project_dir> [--changed-files ...] [--test-command '...'] --json`
27
27
  2. Dispatch Eduardo (spellcheck + changed copy) and Francisca
28
28
  (lint/typecheck/tests/coverage/security-grep) with the report and the
29
29
  structured output schema `QG_VERDICT_JSON_SCHEMA` from
@@ -24,7 +24,7 @@ not cut corners to look fast.
24
24
  dispatch with `[arka:dispatch] paulo -> <specialist>`.
25
25
  3. Evidence over narration: a task is done when its tests RAN and exited 0
26
26
  on record — never when a subagent says it is done. Run
27
- `python -m core.governance.evidence_checks <project_dir> --json` before
27
+ `~/.arkaos/bin/arka-py -m core.governance.evidence_checks <project_dir> --json` before
28
28
  claiming completion.
29
29
  4. Submit to the Quality Gate (marta-cqo) with the evidence report; expect
30
30
  a structured `QGVerdict` back. REJECTED means loop, not negotiate.
@@ -142,7 +142,7 @@ enforcement_levels:
142
142
  scope: "All technical: code quality, tests, UX/UI, data integrity, security, performance"
143
143
  veto_power: true
144
144
  process:
145
- - "Evidence engine runs first: python -m core.governance.evidence_checks — the verdict derives from its report"
145
+ - "Evidence engine runs first: ~/.arkaos/bin/arka-py -m core.governance.evidence_checks — the verdict derives from its report"
146
146
  - "Marta receives all output from execution phase plus the evidence report"
147
147
  - "Marta dispatches Eduardo (text) and Francisca (technical) in parallel, each in an INDEPENDENT subagent context (clean context, no stake in the work being approved)"
148
148
  - "Each reviewer returns a structured QGVerdict (APPROVED or REJECTED) with specific issues; the verdict MUST name the benchmark used (reference_companies.application) and enumerate the concrete rejections a top-tier lead would raise; evidence overall=fail forces REJECTED"
@@ -197,7 +197,7 @@ enforcement_levels:
197
197
  # ─── Rule added in PR3 Squad Intelligence Upgrade (2026-05-28) ───────
198
198
  - id: agent-experience-persistence
199
199
  rule: "Quality Gate REJECTED verdicts MUST result in an Experience record for the failing agent, written to ~/.arkaos/agents/<agent_id>/experiences.jsonl via core.governance.cqo_experience_recorder. Next dispatch of that agent loads the experiences via the Synapse L2.6 layer (core.synapse.agent_experiences_layer) so the lesson carries across sessions. Without this, REJECTED reports evaporate as conversation turns and the same structural mistakes recur."
200
- enforcement: "PR3 v3.74.0 ships the storage, parser, and Synapse layer. v3.74.1 will wire a PostToolUse hook on the Agent tool that auto-records on REJECTED verdicts. Until then orchestrators MUST call cqo_experience_recorder.record_from_verdict() after every CQO dispatch. Audit via python -m core.governance.agent_experiences_cli list <agent_id>."
200
+ enforcement: "PR3 v3.74.0 ships the storage, parser, and Synapse layer. v3.74.1 will wire a PostToolUse hook on the Agent tool that auto-records on REJECTED verdicts. Until then orchestrators MUST call cqo_experience_recorder.record_from_verdict() after every CQO dispatch. Audit via ~/.arkaos/bin/arka-py -m core.governance.agent_experiences_cli list <agent_id>."
201
201
 
202
202
  should:
203
203
  description: "Best practices. Encouraged but not enforced."
@@ -221,17 +221,17 @@ enforcement_levels:
221
221
  # ─── Rule added in PR6 Squad Intelligence Upgrade (2026-05-28) ───────
222
222
  - id: design-system-locked
223
223
  rule: "Each project SHOULD declare a `design-system.yaml` at its root listing tokens (colors, spacing, fonts), allowed_components, file_globs to scan, and forbidden_patterns with suggestions. The per-project linter (core.governance.design_system_lint) detects UI/UX drift — hex literals outside the palette, inline style attributes, raw HTML where a Nuxt UI component exists, etc. Adoption is opt-in per project (no YAML at project root → no violations). v3.77.0 is advisory-only; pre-commit hook integration lands in v3.77.x once the rule sets stabilise across the operator's projects."
224
- enforcement: "Run via `python -m core.governance.design_system_lint_cli <project_path>` (text or JSON output, optional --exit-on-violations). Example template at `docs/examples/design-system-example.yaml`."
224
+ enforcement: "Run via `~/.arkaos/bin/arka-py -m core.governance.design_system_lint_cli <project_path>` (text or JSON output, optional --exit-on-violations). Example template at `docs/examples/design-system-example.yaml`."
225
225
 
226
226
  # ─── Rule added in PR5 Squad Intelligence Upgrade (2026-05-28) ───────
227
227
  - id: dna-fidelity-warn
228
228
  rule: "Agent outputs are compared against the `signature_markers` block in each agent's YAML at the end of every turn. Forbidden patterns (avoid_patterns) and missing opening phrases (opening_phrases) generate FidelityViolation records in ~/.arkaos/telemetry/dna-fidelity.jsonl. v3.76.0 is soft-warn — violations are recorded for telemetry and operator review, not blocked. Hard-block mode lands later once the marker set is calibrated against real production usage."
229
- enforcement: "Stop hook (config/hooks/stop.sh) calls core.governance.dna_fidelity.check_fidelity at turn end. Telemetry inspection: python -m core.governance.dna_fidelity_cli list|summary. Agents in scope for v3.76.0: tech-lead-paulo, cqo-marta, copy-director-eduardo, tech-director-francisca. Remaining 61 agent YAMLs get signature_markers in v3.76.x curation work."
229
+ enforcement: "Stop hook (config/hooks/stop.sh) calls core.governance.dna_fidelity.check_fidelity at turn end. Telemetry inspection: ~/.arkaos/bin/arka-py -m core.governance.dna_fidelity_cli list|summary. Agents in scope for v3.76.0: tech-lead-paulo, cqo-marta, copy-director-eduardo, tech-director-francisca. Remaining 61 agent YAMLs get signature_markers in v3.76.x curation work."
230
230
 
231
231
  # ─── Rule added in PR4 Squad Intelligence Upgrade (2026-05-28) ───────
232
232
  - id: pattern-library-first
233
233
  rule: "Before designing any new feature, consult the Pattern Library (core.knowledge.pattern_cards). If a similar pattern exists, reuse it or explicitly document why divergence is justified in the spec. New patterns SHOULD be registered with record_pattern() after Quality Gate APPROVED so future feature work inherits the prior art."
234
- enforcement: "Synapse layer L7.5 (core.synapse.pattern_library_layer) auto-injects top matching cards as context. Audit via python -m core.knowledge.pattern_cards_cli list. SHOULD level — promoted from advisory once telemetry shows consistent consultation."
234
+ enforcement: "Synapse layer L7.5 (core.synapse.pattern_library_layer) auto-injects top matching cards as context. Audit via ~/.arkaos/bin/arka-py -m core.knowledge.pattern_cards_cli list. SHOULD level — promoted from advisory once telemetry shows consistent consultation."
235
235
 
236
236
  tier_hierarchy:
237
237
  description: "Agent authority levels inspired by SpaceX/Google/Anthropic org structures"
@@ -0,0 +1,43 @@
1
+ # ArkaOS shared Python resolver (PowerShell side) — single source of truth.
2
+ #
3
+ # Windows mirror of config/hooks/_lib/arka_python.sh and
4
+ # installer/python-resolver.js. Prefers the ~/.arkaos venv (has pyyaml/
5
+ # pydantic); never silently falls back to a system python that lacks ArkaOS
6
+ # deps. Dot-source this file: it sets $env:ARKA_PY.
7
+ #
8
+ # Resolution order:
9
+ # 1. $env:ARKAOS_PYTHON (explicit override), if it exists.
10
+ # 2. The ArkaOS venv — Scripts\python.exe (Windows) then bin/python.
11
+ # 3. Any python3/python/py on PATH that can `import yaml`.
12
+ # 4. Bare `python` as a last resort.
13
+
14
+ function Resolve-ArkaPython {
15
+ if ($env:ARKAOS_PYTHON -and (Test-Path -LiteralPath $env:ARKAOS_PYTHON)) {
16
+ return $env:ARKAOS_PYTHON
17
+ }
18
+
19
+ $base = if ($env:USERPROFILE) { $env:USERPROFILE } elseif ($HOME) { $HOME } else { "" }
20
+ if ($base) {
21
+ $candidates = @(
22
+ (Join-Path $base ".arkaos\venv\Scripts\python.exe"),
23
+ (Join-Path $base ".arkaos/venv/bin/python"),
24
+ (Join-Path $base ".arkaos\.venv\Scripts\python.exe"),
25
+ (Join-Path $base ".arkaos/.venv/bin/python")
26
+ )
27
+ foreach ($c in $candidates) {
28
+ if (Test-Path -LiteralPath $c) { return $c }
29
+ }
30
+ }
31
+
32
+ foreach ($name in @("python3", "python", "py")) {
33
+ $cmd = Get-Command $name -ErrorAction SilentlyContinue
34
+ if ($cmd) {
35
+ & $cmd.Source -c "import yaml" 2>$null
36
+ if ($LASTEXITCODE -eq 0) { return $cmd.Source }
37
+ }
38
+ }
39
+
40
+ return "python"
41
+ }
42
+
43
+ $env:ARKA_PY = Resolve-ArkaPython
@@ -0,0 +1,63 @@
1
+ # shellcheck shell=bash
2
+ # ArkaOS shared Python resolver (shell side) — single source of truth.
3
+ #
4
+ # Mirrors installer/python-resolver.js (getArkaosPython). Every hook, the
5
+ # state reader, the agent-provision helper, and the `arka-py` shim source
6
+ # THIS file instead of probing for `python3` on their own. Rationale: an
7
+ # unrelated project venv first on PATH, or a system python without pyyaml/
8
+ # pydantic, silently degrades every gate. The ArkaOS venv is authoritative.
9
+ #
10
+ # Contract of arka_resolve_python():
11
+ # Echoes the interpreter path and returns 0 when a provisioned ArkaOS
12
+ # interpreter is found; echoes a best-effort fallback and returns 1 when
13
+ # only a bare `python3` remains (caller degrades gracefully).
14
+ #
15
+ # Resolution order:
16
+ # 1. $ARKAOS_PYTHON (explicit operator override), if executable.
17
+ # 2. The ArkaOS venv — ~/.arkaos/venv then ~/.arkaos/.venv, python then
18
+ # python3. `[ -x ]` follows symlinks, so a Homebrew-rotated broken
19
+ # symlink is skipped automatically.
20
+ # 3. Any python3 on PATH / known locations that can `import yaml`.
21
+ # 4. Bare `python3` as a last resort (return 1).
22
+
23
+ arka_resolve_python() {
24
+ local cand
25
+
26
+ if [ -n "${ARKAOS_PYTHON:-}" ] && [ -x "${ARKAOS_PYTHON}" ]; then
27
+ printf '%s\n' "${ARKAOS_PYTHON}"
28
+ return 0
29
+ fi
30
+
31
+ for cand in \
32
+ "$HOME/.arkaos/venv/bin/python" \
33
+ "$HOME/.arkaos/venv/bin/python3" \
34
+ "$HOME/.arkaos/.venv/bin/python" \
35
+ "$HOME/.arkaos/.venv/bin/python3"; do
36
+ if [ -x "$cand" ]; then
37
+ printf '%s\n' "$cand"
38
+ return 0
39
+ fi
40
+ done
41
+
42
+ for cand in python3 /opt/homebrew/bin/python3 /usr/local/bin/python3 /usr/bin/python3; do
43
+ if command -v "$cand" >/dev/null 2>&1 && "$cand" -c "import yaml" >/dev/null 2>&1; then
44
+ printf '%s\n' "$cand"
45
+ return 0
46
+ fi
47
+ done
48
+
49
+ printf '%s\n' "python3"
50
+ return 1
51
+ }
52
+
53
+ # Sourcing this file exports ARKA_PY once, so hooks can `exec "$ARKA_PY" ...`
54
+ # without each re-running the probe. Callers that need the return code call
55
+ # arka_resolve_python directly.
56
+ #
57
+ # `|| true` is REQUIRED: arka_resolve_python returns 1 on the last-resort
58
+ # fallback, and under `set -e` a failing command substitution in an
59
+ # assignment aborts the sourcing file mid-way (reproduced on bash 3.2.57).
60
+ # We still want ARKA_PY set to the fallback value, so swallow the status and
61
+ # keep the export unconditional.
62
+ ARKA_PY="$(arka_resolve_python)" || true
63
+ export ARKA_PY
@@ -7,6 +7,13 @@
7
7
 
8
8
  set -euo pipefail
9
9
 
10
+ # ─── Shared Python resolver (exports ARKA_PY) ──────────────────────────
11
+ # The resolver guards its own ARKA_PY assignment with `|| true`, so sourcing
12
+ # is set -e-safe even on the last-resort fallback. The trailing `|| true`
13
+ # here is belt-and-suspenders for a partially-updated lib.
14
+ _ARKA_LIB="$(dirname "${BASH_SOURCE[0]:-$0}")/_lib/arka_python.sh"
15
+ if [ -f "$_ARKA_LIB" ]; then . "$_ARKA_LIB" || true; else ARKA_PY="python3"; fi
16
+
10
17
  # Hook contract: stdin is a JSON payload with fields tool_name + tool_input.
11
18
  payload="$(cat)"
12
19
 
@@ -44,7 +51,7 @@ fi
44
51
  if [ -d "$core_root/departments" ]; then
45
52
  mkdir -p "$project_agents_dir"
46
53
  set +e
47
- python3 - "$core_root" "$subagent_type" "$target" <<'PY'
54
+ "$ARKA_PY" - "$core_root" "$subagent_type" "$target" <<'PY'
48
55
  import os, re, sys
49
56
  from pathlib import Path
50
57
 
@@ -8,6 +8,10 @@
8
8
  input=$(cat)
9
9
  NEW_CWD=$(echo "$input" | jq -r '.cwd // ""' 2>/dev/null)
10
10
 
11
+ # ─── Shared Python resolver (exports ARKA_PY) ──────────────────────────
12
+ _ARKA_LIB="$(dirname "${BASH_SOURCE[0]:-$0}")/_lib/arka_python.sh"
13
+ if [ -f "$_ARKA_LIB" ]; then . "$_ARKA_LIB"; else ARKA_PY="python3"; fi
14
+
11
15
  if [ -z "$NEW_CWD" ] || [ ! -d "$NEW_CWD" ]; then
12
16
  exit 0
13
17
  fi
@@ -23,8 +27,8 @@ fi
23
27
  ECOSYSTEM=""
24
28
  ECOSYSTEM_NAME=""
25
29
 
26
- if [ -f "$ECOSYSTEMS_FILE" ] && command -v python3 &>/dev/null; then
27
- eval "$(python3 -c "
30
+ if [ -f "$ECOSYSTEMS_FILE" ] && command -v "$ARKA_PY" >/dev/null 2>&1; then
31
+ eval "$("$ARKA_PY" -c "
28
32
  import json, os, sys
29
33
 
30
34
  cwd = '$NEW_CWD'
@@ -7,6 +7,10 @@
7
7
 
8
8
  input=$(cat)
9
9
 
10
+ # ─── Shared Python resolver (exports ARKA_PY) ──────────────────────────
11
+ _ARKA_LIB="$(dirname "${BASH_SOURCE[0]:-$0}")/_lib/arka_python.sh"
12
+ if [ -f "$_ARKA_LIB" ]; then . "$_ARKA_LIB"; else ARKA_PY="python3"; fi
13
+
10
14
  ARKAOS_HOME="${HOME}/.arkaos"
11
15
  GOTCHAS_FILE="$ARKAOS_HOME/gotchas.json"
12
16
 
@@ -74,8 +78,8 @@ project=$(basename "$cwd")
74
78
  echo "[]" > "$GOTCHAS_FILE"
75
79
  fi
76
80
 
77
- if command -v python3 &>/dev/null; then
78
- python3 -c "
81
+ if command -v "$ARKA_PY" >/dev/null 2>&1; then
82
+ "$ARKA_PY" -c "
79
83
  import json, sys
80
84
  try:
81
85
  with open('$GOTCHAS_FILE') as f:
@@ -31,7 +31,11 @@ if [ -z "${ARKAOS_ROOT:-}" ]; then
31
31
  fi
32
32
  export ARKAOS_ROOT
33
33
 
34
- if ! command -v python3 &>/dev/null; then
34
+ # ─── Shared Python resolver (exports ARKA_PY) ──────────────────────────
35
+ _ARKA_LIB="$(dirname "${BASH_SOURCE[0]:-$0}")/_lib/arka_python.sh"
36
+ if [ -f "$_ARKA_LIB" ]; then . "$_ARKA_LIB"; else ARKA_PY="python3"; fi
37
+
38
+ if ! command -v "$ARKA_PY" >/dev/null 2>&1; then
35
39
  echo '{}'
36
40
  exit 0
37
41
  fi
@@ -49,20 +53,7 @@ if [ ! -f "$ARKAOS_ROOT/core/hooks/post_tool_use.py" ]; then
49
53
  fi
50
54
  fi
51
55
 
52
- # Interpreter resolution: prefer the ArkaOS venv (has pyyaml/pydantic);
53
- # otherwise probe an unrelated project venv first on PATH would give a
54
- # python3 without yaml and silently degrade every gate.
55
- _PY=""
56
- for _cand in "$HOME/.arkaos/venv/bin/python3" "$HOME/.arkaos/.venv/bin/python3"; do
57
- if [ -x "$_cand" ]; then _PY="$_cand"; break; fi
58
- done
59
- if [ -z "$_PY" ]; then
60
- _PY="python3"
61
- if ! "$_PY" -c "import yaml" 2>/dev/null; then
62
- for _cand in /opt/homebrew/bin/python3 /usr/local/bin/python3 /usr/bin/python3; do
63
- if [ -x "$_cand" ] && "$_cand" -c "import yaml" 2>/dev/null; then _PY="$_cand"; break; fi
64
- done
65
- fi
66
- fi
56
+ # Interpreter resolution handled by the shared resolver (ARKA_PY): prefers
57
+ # the ArkaOS venv (has pyyaml/pydantic), falls back to a yaml-capable python3.
67
58
  PYTHONPATH="$ARKAOS_ROOT${PYTHONPATH:+:$PYTHONPATH}" \
68
- exec "$_PY" -m core.hooks.post_tool_use
59
+ exec "$ARKA_PY" -m core.hooks.post_tool_use
@@ -39,9 +39,11 @@ if ([string]::IsNullOrWhiteSpace($env:ARKAOS_ROOT)) {
39
39
  }
40
40
  }
41
41
 
42
- $python = Get-Command python3 -ErrorAction SilentlyContinue
43
- if (-not $python) { $python = Get-Command python -ErrorAction SilentlyContinue }
44
- if (-not $python) { exit 0 }
42
+ # Shared resolver (venv-first, yaml-verified). Mirrors _lib/arka_python.sh.
43
+ $arkaLib = Join-Path $PSScriptRoot "_lib\arka_python.ps1"
44
+ if (Test-Path -LiteralPath $arkaLib) { . $arkaLib }
45
+ $pythonExe = $env:ARKA_PY
46
+ if (-not $pythonExe) { exit 0 }
45
47
 
46
48
  # --- KB-first gate (Task #6, runs before flow-marker gate) ---
47
49
  $queryHint = ""
@@ -91,7 +93,7 @@ print(json.dumps({
91
93
  }))
92
94
  '@
93
95
 
94
- $kbDecisionJson = $kbScript | & $python.Source -
96
+ $kbDecisionJson = $kbScript | & $pythonExe -
95
97
  if (-not [string]::IsNullOrWhiteSpace($kbDecisionJson)) {
96
98
  try {
97
99
  $kbDecision = $kbDecisionJson | ConvertFrom-Json
@@ -172,7 +174,7 @@ print(json.dumps({
172
174
  }))
173
175
  '@
174
176
 
175
- $spDecisionJson = $spScript | & $python.Source -
177
+ $spDecisionJson = $spScript | & $pythonExe -
176
178
  if (-not [string]::IsNullOrWhiteSpace($spDecisionJson)) {
177
179
  try {
178
180
  $spDecision = $spDecisionJson | ConvertFrom-Json
@@ -241,7 +243,7 @@ print(json.dumps({
241
243
  }))
242
244
  '@
243
245
 
244
- $decisionJson = $pyScript | & $python.Source -
246
+ $decisionJson = $pyScript | & $pythonExe -
245
247
  if ([string]::IsNullOrWhiteSpace($decisionJson)) { exit 0 }
246
248
 
247
249
  try {
@@ -34,8 +34,12 @@ if [ -z "${ARKAOS_ROOT:-}" ]; then
34
34
  fi
35
35
  export ARKAOS_ROOT
36
36
 
37
+ # ─── Shared Python resolver (exports ARKA_PY) ──────────────────────────
38
+ _ARKA_LIB="$(dirname "${BASH_SOURCE[0]:-$0}")/_lib/arka_python.sh"
39
+ if [ -f "$_ARKA_LIB" ]; then . "$_ARKA_LIB"; else ARKA_PY="python3"; fi
40
+
37
41
  # ─── Degrade gracefully (fail open, same as before) ─────────────────────
38
- if ! command -v python3 &>/dev/null; then
42
+ if ! command -v "$ARKA_PY" >/dev/null 2>&1; then
39
43
  exit 0
40
44
  fi
41
45
  # Self-root fallback: the wrapper ships next to its python entrypoint, so
@@ -52,20 +56,7 @@ if [ ! -f "$ARKAOS_ROOT/core/hooks/pre_tool_use.py" ]; then
52
56
  fi
53
57
 
54
58
  # ─── Single python process; stdin/stdout/stderr/exit-code pass through ──
55
- # Interpreter resolution: prefer the ArkaOS venv (has pyyaml/pydantic);
56
- # otherwise probe an unrelated project venv first on PATH would give a
57
- # python3 without yaml and silently degrade every gate.
58
- _PY=""
59
- for _cand in "$HOME/.arkaos/venv/bin/python3" "$HOME/.arkaos/.venv/bin/python3"; do
60
- if [ -x "$_cand" ]; then _PY="$_cand"; break; fi
61
- done
62
- if [ -z "$_PY" ]; then
63
- _PY="python3"
64
- if ! "$_PY" -c "import yaml" 2>/dev/null; then
65
- for _cand in /opt/homebrew/bin/python3 /usr/local/bin/python3 /usr/bin/python3; do
66
- if [ -x "$_cand" ] && "$_cand" -c "import yaml" 2>/dev/null; then _PY="$_cand"; break; fi
67
- done
68
- fi
69
- fi
59
+ # Interpreter resolution handled by the shared resolver (ARKA_PY): prefers
60
+ # the ArkaOS venv (has pyyaml/pydantic), falls back to a yaml-capable python3.
70
61
  PYTHONPATH="$ARKAOS_ROOT${PYTHONPATH:+:$PYTHONPATH}" \
71
- exec "$_PY" -m core.hooks.pre_tool_use
62
+ exec "$ARKA_PY" -m core.hooks.pre_tool_use
@@ -9,9 +9,13 @@ NAME="founder"
9
9
  COMPANY="WizardingCode"
10
10
  VERSION="2.x"
11
11
 
12
- if [ -f "$HOME/.arkaos/profile.json" ] && command -v python3 &>/dev/null; then
13
- NAME=$(python3 -c "import json; p=json.load(open('$HOME/.arkaos/profile.json')); print(p.get('name', p.get('role', 'founder')))" 2>/dev/null || echo "founder")
14
- COMPANY=$(python3 -c "import json; print(json.load(open('$HOME/.arkaos/profile.json')).get('company', 'WizardingCode'))" 2>/dev/null || echo "WizardingCode")
12
+ # ─── Shared Python resolver (exports ARKA_PY) ──────────────────────────
13
+ _ARKA_LIB="$(dirname "${BASH_SOURCE[0]:-$0}")/_lib/arka_python.sh"
14
+ if [ -f "$_ARKA_LIB" ]; then . "$_ARKA_LIB"; else ARKA_PY="python3"; fi
15
+
16
+ if [ -f "$HOME/.arkaos/profile.json" ] && command -v "$ARKA_PY" >/dev/null 2>&1; then
17
+ NAME=$("$ARKA_PY" -c "import json; p=json.load(open('$HOME/.arkaos/profile.json')); print(p.get('name', p.get('role', 'founder')))" 2>/dev/null || echo "founder")
18
+ COMPANY=$("$ARKA_PY" -c "import json; print(json.load(open('$HOME/.arkaos/profile.json')).get('company', 'WizardingCode'))" 2>/dev/null || echo "WizardingCode")
15
19
  fi
16
20
 
17
21
  if [ -f "$HOME/.arkaos/.repo-path" ]; then
@@ -29,7 +33,7 @@ SYNC_STATE="$HOME/.arkaos/sync-state.json"
29
33
  DRIFT=""
30
34
 
31
35
  if [ -f "$SYNC_STATE" ]; then
32
- SYNCED=$(python3 -c "import json; print(json.load(open('$SYNC_STATE'))['version'])" 2>/dev/null || echo "none")
36
+ SYNCED=$("$ARKA_PY" -c "import json; print(json.load(open('$SYNC_STATE'))['version'])" 2>/dev/null || echo "none")
33
37
  if [ "$SYNCED" != "$VERSION" ]; then
34
38
  DRIFT="\\n[arka:update-available] Core v${VERSION} != synced v${SYNCED}. Run /arka update."
35
39
  fi
@@ -71,11 +75,11 @@ _FORGE_LINE=""
71
75
  if [ -f "$_FORGE_ACTIVE" ]; then
72
76
  _FORGE_ID=$(cat "$_FORGE_ACTIVE" 2>/dev/null)
73
77
  _FORGE_FILE="$_FORGE_PLANS/${_FORGE_ID}.yaml"
74
- if [ -f "$_FORGE_FILE" ] && command -v python3 &>/dev/null; then
75
- _FORGE_NAME=$(FORGE_FILE="$_FORGE_FILE" python3 -c "import yaml,os; d=yaml.safe_load(open(os.environ['FORGE_FILE'])); print(d.get('name',''))" 2>/dev/null)
76
- _FORGE_STATUS=$(FORGE_FILE="$_FORGE_FILE" python3 -c "import yaml,os; d=yaml.safe_load(open(os.environ['FORGE_FILE'])); print(d.get('status',''))" 2>/dev/null)
77
- _FORGE_PHASES=$(FORGE_FILE="$_FORGE_FILE" python3 -c "import yaml,os; d=yaml.safe_load(open(os.environ['FORGE_FILE'])); print(len(d.get('plan_phases',[])))" 2>/dev/null)
78
- _FORGE_BRANCH=$(FORGE_FILE="$_FORGE_FILE" python3 -c "import yaml,os; d=yaml.safe_load(open(os.environ['FORGE_FILE'])); print(d.get('governance',{}).get('branch_strategy',''))" 2>/dev/null)
78
+ if [ -f "$_FORGE_FILE" ] && command -v "$ARKA_PY" >/dev/null 2>&1; then
79
+ _FORGE_NAME=$(FORGE_FILE="$_FORGE_FILE" "$ARKA_PY" -c "import yaml,os; d=yaml.safe_load(open(os.environ['FORGE_FILE'])); print(d.get('name',''))" 2>/dev/null)
80
+ _FORGE_STATUS=$(FORGE_FILE="$_FORGE_FILE" "$ARKA_PY" -c "import yaml,os; d=yaml.safe_load(open(os.environ['FORGE_FILE'])); print(d.get('status',''))" 2>/dev/null)
81
+ _FORGE_PHASES=$(FORGE_FILE="$_FORGE_FILE" "$ARKA_PY" -c "import yaml,os; d=yaml.safe_load(open(os.environ['FORGE_FILE'])); print(len(d.get('plan_phases',[])))" 2>/dev/null)
82
+ _FORGE_BRANCH=$(FORGE_FILE="$_FORGE_FILE" "$ARKA_PY" -c "import yaml,os; d=yaml.safe_load(open(os.environ['FORGE_FILE'])); print(d.get('governance',{}).get('branch_strategy',''))" 2>/dev/null)
79
83
 
80
84
  if [ "$_FORGE_STATUS" = "approved" ]; then
81
85
  _FORGE_LINE=" ⚒ Forge plan pending: ${_FORGE_NAME} | Phases: ${_FORGE_PHASES} | /forge resume"
@@ -105,16 +109,37 @@ MSG+="\\nFields: kb=N (Obsidian/KB notes consulted), research=X (MCPs invoked: p
105
109
  MSG+="\\nMandatory after: EFFECT tool calls, plan/recommendation outputs, QG verdicts. Optional for pure read-only status replies."
106
110
  MSG+="\\nAbsence is measured by the Stop hook (warn-only in v2.34.0) before promotion to hard enforcement."
107
111
 
112
+ # ─── Model Fabric routing (consumption layer) ───────────────────────────
113
+ # Feed the operator's ~/.arkaos/models.yaml back to the orchestrator so
114
+ # dashboard/CLI model choices actually govern agent dispatch. Cheap
115
+ # (single python read); skipped silently if the config can't be resolved.
116
+ if [ -n "$REPO" ]; then
117
+ # ARKA_PY already resolves to the venv python (has pydantic + yaml); the
118
+ # ambient python3 usually lacks them and routing_directive() would return "".
119
+ if command -v "$ARKA_PY" >/dev/null 2>&1 || [ -x "$ARKA_PY" ]; then
120
+ _MF_DIRECTIVE=$(cd "$REPO" && PYTHONPATH="$REPO" "$ARKA_PY" -c "
121
+ try:
122
+ from core.runtime.model_routing_context import routing_directive
123
+ print(routing_directive())
124
+ except Exception:
125
+ pass
126
+ " 2>/dev/null)
127
+ if [ -n "$_MF_DIRECTIVE" ]; then
128
+ MSG+="\\n\\n${_MF_DIRECTIVE}"
129
+ fi
130
+ fi
131
+ fi
132
+
108
133
  # ─── Stale-aware reorganizer trigger (PR24 v2.46.0) ─────────────────────
109
134
  # If today's proposal file is missing, fire the reorganizer in the
110
135
  # background with a 30s timeout. Best-effort, never blocks session
111
136
  # start. Multiple sessions per day no-op because the file now exists.
112
- if [ -n "$REPO" ] && command -v python3 &>/dev/null; then
137
+ if [ -n "$REPO" ] && command -v "$ARKA_PY" >/dev/null 2>&1; then
113
138
  _PROPOSAL_DIR="$HOME/.arkaos/reorganize-proposals"
114
139
  _TODAY="$(date -u +%Y-%m-%d).md"
115
140
  if [ ! -f "$_PROPOSAL_DIR/$_TODAY" ]; then
116
141
  (
117
- cd "$REPO" && timeout 30s python3 -m core.cognition.reorganizer_cli >/dev/null 2>&1
142
+ cd "$REPO" && timeout 30s "$ARKA_PY" -m core.cognition.reorganizer_cli >/dev/null 2>&1
118
143
  ) &
119
144
  disown 2>/dev/null || true
120
145
  fi
@@ -126,7 +151,7 @@ fi
126
151
  # starts API+UI. Background + disown so it never touches the 5s budget.
127
152
  # Toggle off: ~/.arkaos/config.json -> {"dashboard": {"ensure_on_session": false}}
128
153
  if [ -n "$REPO" ] && [ -f "$REPO/scripts/start-dashboard.sh" ]; then
129
- _DASH_ENSURE=$(python3 -c "import json; print(json.load(open('$HOME/.arkaos/config.json')).get('dashboard',{}).get('ensure_on_session', True))" 2>/dev/null || echo "True")
154
+ _DASH_ENSURE=$("$ARKA_PY" -c "import json; print(json.load(open('$HOME/.arkaos/config.json')).get('dashboard',{}).get('ensure_on_session', True))" 2>/dev/null || echo "True")
130
155
  if [ "$_DASH_ENSURE" = "True" ] || [ "$_DASH_ENSURE" = "true" ]; then
131
156
  mkdir -p "$HOME/.arkaos/logs"
132
157
  (
@@ -143,8 +168,8 @@ if [ -n "$REPO" ] && [ -f "$REPO/scripts/start-dashboard.sh" ]; then
143
168
  fi
144
169
 
145
170
  # --- Session Memory Resume Context ---
146
- if command -v python3 &>/dev/null && [ -n "$REPO" ]; then
147
- _SESSION_CTX=$(cd "$REPO" && python3 -c "
171
+ if command -v "$ARKA_PY" >/dev/null 2>&1 && [ -n "$REPO" ]; then
172
+ _SESSION_CTX=$(cd "$REPO" && "$ARKA_PY" -c "
148
173
  import sys
149
174
  sys.path.insert(0, '$REPO')
150
175
  try:
@@ -159,7 +184,7 @@ except Exception:
159
184
  fi
160
185
 
161
186
  # ─── Output as systemMessage (same protocol as claude-mem) ─────────────
162
- python3 -c "
187
+ "$ARKA_PY" -c "
163
188
  import json
164
189
  msg = '''$(echo -e "$MSG")'''
165
190
  print(json.dumps({'systemMessage': msg}))
@@ -43,9 +43,11 @@ if ([string]::IsNullOrWhiteSpace($env:ARKAOS_ROOT)) {
43
43
  $enforcerPy = Join-Path $env:ARKAOS_ROOT "core/workflow/flow_enforcer.py"
44
44
  if (-not (Test-Path $enforcerPy)) { exit 0 }
45
45
 
46
- $python = Get-Command python3 -ErrorAction SilentlyContinue
47
- if (-not $python) { $python = Get-Command python -ErrorAction SilentlyContinue }
48
- if (-not $python) { exit 0 }
46
+ # Shared resolver (venv-first, yaml-verified). Mirrors _lib/arka_python.sh.
47
+ $arkaLib = Join-Path $PSScriptRoot "_lib\arka_python.ps1"
48
+ if (Test-Path -LiteralPath $arkaLib) { . $arkaLib }
49
+ $pythonExe = $env:ARKA_PY
50
+ if (-not $pythonExe) { exit 0 }
49
51
 
50
52
  $env:SESSION_ID_VAL = $sessionId
51
53
  $env:TRANSCRIPT_PATH_VAL = $transcriptPath
@@ -102,7 +104,7 @@ except Exception:
102
104
  pass
103
105
  '@
104
106
 
105
- $pyScript | & $python.Source - | Out-Null
107
+ $pyScript | & $pythonExe - | Out-Null
106
108
 
107
109
  # ─── Auto-documentor enqueue (fire-and-forget) ─────────────────────────
108
110
  # Queues a background job when classifier flagged flow, QG approved, and
@@ -178,7 +180,7 @@ except Exception:
178
180
  '@
179
181
 
180
182
  try {
181
- $autoDocScript | & $python.Source - | Out-Null
183
+ $autoDocScript | & $pythonExe - | Out-Null
182
184
  } catch {
183
185
  # Swallow — enqueue is fire-and-forget.
184
186
  }
@@ -33,7 +33,11 @@ if [ -z "${ARKAOS_ROOT:-}" ]; then
33
33
  fi
34
34
  export ARKAOS_ROOT
35
35
 
36
- if ! command -v python3 &>/dev/null; then
36
+ # ─── Shared Python resolver (exports ARKA_PY) ──────────────────────────
37
+ _ARKA_LIB="$(dirname "${BASH_SOURCE[0]:-$0}")/_lib/arka_python.sh"
38
+ if [ -f "$_ARKA_LIB" ]; then . "$_ARKA_LIB"; else ARKA_PY="python3"; fi
39
+
40
+ if ! command -v "$ARKA_PY" >/dev/null 2>&1; then
37
41
  exit 0
38
42
  fi
39
43
  # Self-root fallback: the wrapper ships next to its python entrypoint, so
@@ -49,21 +53,8 @@ if [ ! -f "$ARKAOS_ROOT/core/hooks/stop.py" ]; then
49
53
  fi
50
54
  fi
51
55
 
52
- # Interpreter resolution: prefer the ArkaOS venv (has pyyaml/pydantic);
53
- # otherwise probe an unrelated project venv first on PATH would give a
54
- # python3 without yaml and silently degrade every gate.
55
- _PY=""
56
- for _cand in "$HOME/.arkaos/venv/bin/python3" "$HOME/.arkaos/.venv/bin/python3"; do
57
- if [ -x "$_cand" ]; then _PY="$_cand"; break; fi
58
- done
59
- if [ -z "$_PY" ]; then
60
- _PY="python3"
61
- if ! "$_PY" -c "import yaml" 2>/dev/null; then
62
- for _cand in /opt/homebrew/bin/python3 /usr/local/bin/python3 /usr/bin/python3; do
63
- if [ -x "$_cand" ] && "$_cand" -c "import yaml" 2>/dev/null; then _PY="$_cand"; break; fi
64
- done
65
- fi
66
- fi
56
+ # Interpreter resolution handled by the shared resolver (ARKA_PY): prefers
57
+ # the ArkaOS venv (has pyyaml/pydantic), falls back to a yaml-capable python3.
67
58
  PYTHONPATH="$ARKAOS_ROOT${PYTHONPATH:+:$PYTHONPATH}" \
68
- "$_PY" -m core.hooks.stop
59
+ "$ARKA_PY" -m core.hooks.stop
69
60
  exit 0