arkaos 4.44.0 → 4.46.0
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.
- package/THE-ARKAOS-GUIDE.md +1 -1
- package/VERSION +1 -1
- package/config/output-styles/arkaos.md +1 -1
- package/core/egress/__init__.py +20 -0
- package/core/egress/allowlist.py +123 -0
- package/core/egress/audit.py +127 -0
- package/core/egress/policy.py +261 -0
- package/core/egress/redact.py +46 -0
- package/core/governance/evidence_checks.py +116 -37
- package/core/harness/__init__.py +22 -0
- package/core/harness/drift.py +317 -0
- package/core/harness/json_store.py +119 -0
- package/core/harness/manifest.py +87 -0
- package/core/harness/paths.py +72 -0
- package/core/harness/spec.py +182 -0
- package/core/hooks/session_start.py +17 -0
- package/core/runtime/opencode_hooks.py +13 -10
- package/core/synapse/layers.py +85 -12
- package/harness/codex/AGENTS.md +1 -1
- package/harness/copilot/copilot-instructions.md +1 -1
- package/harness/cursor/rules/arkaos.mdc +2 -2
- package/harness/gemini/GEMINI.md +1 -1
- package/harness/opencode/AGENTS.md +1 -1
- package/harness/opencode/agents/arka-architect-gabriel.md +1 -1
- package/harness/opencode/agents/arka-brand-director-valentina.md +1 -1
- package/harness/opencode/agents/arka-cfo-helena.md +1 -1
- package/harness/opencode/agents/arka-chief-of-staff-afonso.md +1 -1
- package/harness/opencode/agents/arka-community-strategist-beatriz.md +1 -1
- package/harness/opencode/agents/arka-content-strategist-rafael.md +1 -1
- package/harness/opencode/agents/arka-conversion-strategist-ines.md +1 -1
- package/harness/opencode/agents/arka-coo-sofia.md +1 -1
- package/harness/opencode/agents/arka-copy-director-eduardo.md +1 -1
- package/harness/opencode/agents/arka-cqo-marta.md +1 -1
- package/harness/opencode/agents/arka-cto-marco.md +1 -1
- package/harness/opencode/agents/arka-design-ops-lead-iris.md +1 -1
- package/harness/opencode/agents/arka-ecom-director-ricardo.md +1 -1
- package/harness/opencode/agents/arka-knowledge-director-clara.md +1 -1
- package/harness/opencode/agents/arka-leadership-director-rodrigo.md +1 -1
- package/harness/opencode/agents/arka-marketing-director-luna.md +1 -1
- package/harness/opencode/agents/arka-ops-lead-daniel.md +1 -1
- package/harness/opencode/agents/arka-pm-director-carolina.md +1 -1
- package/harness/opencode/agents/arka-revops-lead-vicente.md +1 -1
- package/harness/opencode/agents/arka-saas-strategist-tiago.md +1 -1
- package/harness/opencode/agents/arka-sales-director-miguel.md +1 -1
- package/harness/opencode/agents/arka-strategy-director-tomas.md +1 -1
- package/harness/opencode/agents/arka-tech-director-francisca.md +1 -1
- package/harness/opencode/agents/arka-tech-lead-paulo.md +1 -1
- package/harness/opencode/agents/arka-video-producer-simao.md +1 -1
- package/harness/zed/.rules +1 -1
- package/knowledge/skills-manifest.json +1 -1
- package/package.json +1 -1
- package/pyproject.toml +1 -1
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""Ownership manifest schema for ``~/.arkaos/ownership.json`` (PR-C1).
|
|
2
|
+
|
|
3
|
+
The manifest is the C2 manager's record of WHICH harness surfaces
|
|
4
|
+
ArkaOS claims and UNDER WHICH policy. C1 ships only the schema and a
|
|
5
|
+
tolerant loader — nothing here writes the file, and the default
|
|
6
|
+
manifest is DERIVED from ``core.harness.spec`` so the two can never
|
|
7
|
+
disagree about which surfaces exist.
|
|
8
|
+
|
|
9
|
+
Policy vocabulary (decided in the 2026-07-26 campaign plan, C2):
|
|
10
|
+
|
|
11
|
+
- ``own`` — ArkaOS replaces the surface wholesale on assert.
|
|
12
|
+
- ``own-subset`` — ArkaOS guarantees its own entries exist; operator
|
|
13
|
+
entries on the same surface are never removed.
|
|
14
|
+
- ``seed`` — written only when absent; an operator edit is
|
|
15
|
+
ADOPTED (drift reports it, assert never reverts it).
|
|
16
|
+
- ``operator`` — ArkaOS never touches the surface.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
from enum import StrEnum
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
from pydantic import BaseModel, Field, ValidationError
|
|
25
|
+
|
|
26
|
+
from core.harness import json_store
|
|
27
|
+
from core.harness import spec as harness_spec
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class OwnershipPolicy(StrEnum):
|
|
31
|
+
"""How ArkaOS treats one harness surface."""
|
|
32
|
+
|
|
33
|
+
OWN = "own"
|
|
34
|
+
OWN_SUBSET = "own-subset"
|
|
35
|
+
SEED = "seed"
|
|
36
|
+
OPERATOR = "operator"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class SurfaceRecord(BaseModel):
|
|
40
|
+
"""One claimed surface and the policy it is held under."""
|
|
41
|
+
|
|
42
|
+
surface: str = Field(description="Stable surface id, e.g. settings:hooks")
|
|
43
|
+
policy: OwnershipPolicy
|
|
44
|
+
last_asserted: str | None = Field(
|
|
45
|
+
default=None,
|
|
46
|
+
description="ISO-8601 timestamp of the last C2 assert; C1 never sets it",
|
|
47
|
+
)
|
|
48
|
+
content_sha256: str | None = Field(
|
|
49
|
+
default=None,
|
|
50
|
+
description="Digest of the ArkaOS-owned content at last assert",
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class OwnershipManifest(BaseModel):
|
|
55
|
+
"""The full ownership manifest for one runtime."""
|
|
56
|
+
|
|
57
|
+
version: int = 1
|
|
58
|
+
runtime: str = "claude-code"
|
|
59
|
+
surfaces: list[SurfaceRecord] = Field(default_factory=list)
|
|
60
|
+
|
|
61
|
+
@classmethod
|
|
62
|
+
def default(cls, runtime: str = "claude-code") -> OwnershipManifest:
|
|
63
|
+
"""Manifest derived from the runtime spec's surface table."""
|
|
64
|
+
runtime_spec = harness_spec.spec_for(runtime)
|
|
65
|
+
return cls(
|
|
66
|
+
runtime=runtime,
|
|
67
|
+
surfaces=[
|
|
68
|
+
SurfaceRecord(surface=s.surface, policy=OwnershipPolicy(s.policy))
|
|
69
|
+
for s in runtime_spec.surfaces
|
|
70
|
+
],
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def load_manifest(path: Path) -> tuple[OwnershipManifest | None, str | None]:
|
|
75
|
+
"""Tolerant manifest load: ``(manifest, None)`` or ``(None, error)``.
|
|
76
|
+
|
|
77
|
+
Error vocabulary extends ``json_store.LoadResult`` with
|
|
78
|
+
``invalid-schema`` — a file that parses as JSON but does not
|
|
79
|
+
validate is reported, never raised.
|
|
80
|
+
"""
|
|
81
|
+
result = json_store.load_json(path)
|
|
82
|
+
if not result.ok:
|
|
83
|
+
return None, result.error
|
|
84
|
+
try:
|
|
85
|
+
return OwnershipManifest.model_validate(result.data), None
|
|
86
|
+
except ValidationError:
|
|
87
|
+
return None, "invalid-schema"
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""Canonical harness paths, resolved at call time.
|
|
2
|
+
|
|
3
|
+
Every path here takes an explicit override — ``home`` for the
|
|
4
|
+
user-tree paths (defaulting to ``Path.home()``), ``root`` for
|
|
5
|
+
``hooks_dir`` — resolved at CALL time, so tests and the C2 manager can
|
|
6
|
+
operate on an alternate tree without monkeypatching. Nothing here is a
|
|
7
|
+
module-level constant: paths captured at import bind to whatever root
|
|
8
|
+
the importing process started with, which is exactly the split-root
|
|
9
|
+
failure mode PR-A2 closed (``ARKAOS_ROOT`` in the shell pointing one
|
|
10
|
+
way, ``.repo-path`` another).
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
from core.hooks._shared import resolve_arkaos_root
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def claude_home(home: Path | None = None) -> Path:
|
|
21
|
+
"""``~/.claude`` — the Claude Code configuration tree."""
|
|
22
|
+
return (home or Path.home()) / ".claude"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def claude_settings_path(home: Path | None = None) -> Path:
|
|
26
|
+
"""``~/.claude/settings.json`` — the executed harness surface."""
|
|
27
|
+
return claude_home(home) / "settings.json"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def claude_global_md_path(home: Path | None = None) -> Path:
|
|
31
|
+
"""``~/.claude/CLAUDE.md`` — global instructions (C5 managed block)."""
|
|
32
|
+
return claude_home(home) / "CLAUDE.md"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def arkaos_home(home: Path | None = None) -> Path:
|
|
36
|
+
"""``~/.arkaos`` — ArkaOS user-mutable data."""
|
|
37
|
+
return (home or Path.home()) / ".arkaos"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def ownership_manifest_path(home: Path | None = None) -> Path:
|
|
41
|
+
"""``~/.arkaos/ownership.json`` — the C2 ownership manifest."""
|
|
42
|
+
return arkaos_home(home) / "ownership.json"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def audit_dir(home: Path | None = None) -> Path:
|
|
46
|
+
"""``~/.arkaos/audit`` — mutation audit trails."""
|
|
47
|
+
return arkaos_home(home) / "audit"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def harness_audit_log_path(home: Path | None = None) -> Path:
|
|
51
|
+
"""``~/.arkaos/audit/harness-mutations.jsonl`` (written by C2)."""
|
|
52
|
+
return audit_dir(home) / "harness-mutations.jsonl"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def hard_deny_extension_path(home: Path | None = None) -> Path:
|
|
56
|
+
"""``~/.arkaos/hard-deny.json`` — operator hard-deny extensions."""
|
|
57
|
+
return arkaos_home(home) / "hard-deny.json"
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def arkaos_config_path(home: Path | None = None) -> Path:
|
|
61
|
+
"""``~/.arkaos/config.json`` — feature flags (hardEnforcement, ...)."""
|
|
62
|
+
return arkaos_home(home) / "config.json"
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def hooks_dir(root: str | None = None) -> Path:
|
|
66
|
+
"""``<arkaos-root>/config/hooks`` — the deployed hook scripts.
|
|
67
|
+
|
|
68
|
+
The root goes through the PR-A2 validated chain
|
|
69
|
+
(``core.hooks._shared.resolve_arkaos_root``) at call time; passing
|
|
70
|
+
``root`` overrides it for tests.
|
|
71
|
+
"""
|
|
72
|
+
return Path(root or resolve_arkaos_root()) / "config" / "hooks"
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"""Desired-state harness spec, keyed by runtime (PR-C1).
|
|
2
|
+
|
|
3
|
+
Until now the desired state of the operator's harness lived only inside
|
|
4
|
+
installer JavaScript: ``installer/hard-deny.js`` (the curated hard-deny
|
|
5
|
+
rules) and ``installer/adapters/claude-code.js`` (which hook events get
|
|
6
|
+
registered, with which scripts and timeouts). This module is the Python
|
|
7
|
+
port that ``drift`` (C1) and the ``ClaudeConfigManager`` (C2) read.
|
|
8
|
+
|
|
9
|
+
PARITY IS PINNED, NOT PROMISED: ``tests/python/test_harness_spec.py``
|
|
10
|
+
parses both JS sources and asserts this module matches them — rule
|
|
11
|
+
list in full, same content and order, hook registrations by
|
|
12
|
+
event/script/timeout/matcher. Editing either side without the other
|
|
13
|
+
fails the suite. The installer files remain the EXECUTED path until C4
|
|
14
|
+
flips ownership.
|
|
15
|
+
|
|
16
|
+
``config/settings-template.json`` is NOT a source here — the template
|
|
17
|
+
is not the executed path (PR-B5 lesson: it declared agent-provision for
|
|
18
|
+
months while the adapter never registered it).
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
from dataclasses import dataclass
|
|
24
|
+
|
|
25
|
+
# Port of DEFAULT_HARD_DENY_RULES in installer/hard-deny.js — same
|
|
26
|
+
# rules, same order. Parity-pinned by test_harness_spec.py.
|
|
27
|
+
HARD_DENY_RULES: tuple[str, ...] = (
|
|
28
|
+
# Destructive git operations
|
|
29
|
+
"Bash(git push --force*)",
|
|
30
|
+
"Bash(git push -f*)",
|
|
31
|
+
"Bash(git reset --hard*)",
|
|
32
|
+
"Bash(git clean -fd*)",
|
|
33
|
+
"Bash(git branch -D*)",
|
|
34
|
+
# Filesystem destruction
|
|
35
|
+
"Bash(rm -rf /*)",
|
|
36
|
+
"Bash(rm -rf ~/*)",
|
|
37
|
+
"Bash(rm -rf ~)",
|
|
38
|
+
"Bash(rm -rf .*)",
|
|
39
|
+
# Secrets and credential paths
|
|
40
|
+
"Read(~/.ssh/*)",
|
|
41
|
+
"Read(~/.ssh/**)",
|
|
42
|
+
"Read(~/.aws/credentials)",
|
|
43
|
+
"Read(~/.aws/config)",
|
|
44
|
+
"Read(~/.gnupg/*)",
|
|
45
|
+
"Read(~/.gnupg/**)",
|
|
46
|
+
"Read(~/.npmrc)",
|
|
47
|
+
"Read(~/.docker/config.json)",
|
|
48
|
+
"Read(~/.config/gh/*)",
|
|
49
|
+
"Read(/etc/shadow)",
|
|
50
|
+
"Read(/etc/sudoers)",
|
|
51
|
+
# Writes to credential / config dirs
|
|
52
|
+
"Write(~/.ssh/*)",
|
|
53
|
+
"Write(~/.aws/credentials)",
|
|
54
|
+
"Write(~/.gnupg/*)",
|
|
55
|
+
"Write(~/.npmrc)",
|
|
56
|
+
# Process / privilege escalation
|
|
57
|
+
"Bash(sudo *)",
|
|
58
|
+
"Bash(su -*)",
|
|
59
|
+
"Bash(chmod 777*)",
|
|
60
|
+
"Bash(curl * | sh*)", # arka:sec-ok(curl-pipe-shell): deny-rule literal
|
|
61
|
+
"Bash(curl * | bash*)", # arka:sec-ok(curl-pipe-shell): deny-rule literal
|
|
62
|
+
"Bash(wget * | sh*)",
|
|
63
|
+
"Bash(wget * | bash*)",
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@dataclass(frozen=True)
|
|
68
|
+
class HookRegistration:
|
|
69
|
+
"""One hook entry the adapter writes into ``settings.json``.
|
|
70
|
+
|
|
71
|
+
``script`` is the basename without extension — the adapter picks
|
|
72
|
+
``.sh`` / ``.ps1`` / ``.cjs`` per platform and fastpath, so drift
|
|
73
|
+
matching accepts any of the three.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
event: str
|
|
77
|
+
script: str
|
|
78
|
+
timeout: int
|
|
79
|
+
matcher: str | None = None
|
|
80
|
+
posix_only: bool = False
|
|
81
|
+
conditional: bool = False
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
# Port of the settings.hooks registrations in
|
|
85
|
+
# installer/adapters/claude-code.js — parity-pinned.
|
|
86
|
+
_CLAUDE_CODE_HOOKS: tuple[HookRegistration, ...] = (
|
|
87
|
+
HookRegistration("SessionStart", "session-start", 15),
|
|
88
|
+
HookRegistration("UserPromptSubmit", "user-prompt-submit", 20),
|
|
89
|
+
HookRegistration("PostToolUse", "post-tool-use", 5),
|
|
90
|
+
HookRegistration("PostToolUseFailure", "post-tool-use", 5),
|
|
91
|
+
HookRegistration("PreToolUse", "pre-tool-use", 10),
|
|
92
|
+
HookRegistration(
|
|
93
|
+
"PreToolUse",
|
|
94
|
+
"agent-provision",
|
|
95
|
+
10,
|
|
96
|
+
matcher="Task",
|
|
97
|
+
posix_only=True,
|
|
98
|
+
conditional=True,
|
|
99
|
+
),
|
|
100
|
+
HookRegistration("Stop", "stop", 5),
|
|
101
|
+
HookRegistration("SubagentStop", "subagent-stop", 10),
|
|
102
|
+
HookRegistration("SessionEnd", "session-end", 15),
|
|
103
|
+
HookRegistration("PreCompact", "pre-compact", 30),
|
|
104
|
+
HookRegistration("CwdChanged", "cwd-changed", 5),
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@dataclass(frozen=True)
|
|
109
|
+
class SurfaceSpec:
|
|
110
|
+
"""One harness surface ArkaOS claims, with its default policy.
|
|
111
|
+
|
|
112
|
+
``policy`` values are the ``manifest.OwnershipPolicy`` vocabulary;
|
|
113
|
+
kept as strings here so ``spec`` stays import-light (manifest
|
|
114
|
+
imports spec, not the reverse).
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
surface: str
|
|
118
|
+
policy: str
|
|
119
|
+
detail: str
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
_CLAUDE_CODE_SURFACES: tuple[SurfaceSpec, ...] = (
|
|
123
|
+
SurfaceSpec(
|
|
124
|
+
"settings:hooks",
|
|
125
|
+
"own-subset",
|
|
126
|
+
"ArkaOS hook entries per event; user hooks on the same events stay",
|
|
127
|
+
),
|
|
128
|
+
SurfaceSpec(
|
|
129
|
+
"settings:autoMode.hard_deny",
|
|
130
|
+
"own-subset",
|
|
131
|
+
"Curated deny rules merged in; operator rules are never removed",
|
|
132
|
+
),
|
|
133
|
+
SurfaceSpec(
|
|
134
|
+
"settings:statusLine",
|
|
135
|
+
"seed",
|
|
136
|
+
"Branded status bar; an operator replacement is adopted once "
|
|
137
|
+
"C2 owns the surface",
|
|
138
|
+
),
|
|
139
|
+
SurfaceSpec(
|
|
140
|
+
"settings:worktree",
|
|
141
|
+
"seed",
|
|
142
|
+
"worktree.baseRef default; an operator change is adopted once "
|
|
143
|
+
"C2 owns the surface",
|
|
144
|
+
),
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
@dataclass(frozen=True)
|
|
149
|
+
class RuntimeSpec:
|
|
150
|
+
"""The desired harness state for one runtime."""
|
|
151
|
+
|
|
152
|
+
runtime: str
|
|
153
|
+
hook_registrations: tuple[HookRegistration, ...]
|
|
154
|
+
hard_deny_rules: tuple[str, ...]
|
|
155
|
+
surfaces: tuple[SurfaceSpec, ...]
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
_RUNTIME_SPECS: dict[str, RuntimeSpec] = {
|
|
159
|
+
"claude-code": RuntimeSpec(
|
|
160
|
+
runtime="claude-code",
|
|
161
|
+
hook_registrations=_CLAUDE_CODE_HOOKS,
|
|
162
|
+
hard_deny_rules=HARD_DENY_RULES,
|
|
163
|
+
surfaces=_CLAUDE_CODE_SURFACES,
|
|
164
|
+
),
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
SUPPORTED_RUNTIMES: tuple[str, ...] = tuple(sorted(_RUNTIME_SPECS))
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def spec_for(runtime: str) -> RuntimeSpec:
|
|
171
|
+
"""The :class:`RuntimeSpec` for ``runtime``.
|
|
172
|
+
|
|
173
|
+
Raises ``ValueError`` for an unknown runtime — a silent empty spec
|
|
174
|
+
would make drift report a perfectly-owned harness for a runtime
|
|
175
|
+
this package knows nothing about.
|
|
176
|
+
"""
|
|
177
|
+
try:
|
|
178
|
+
return _RUNTIME_SPECS[runtime]
|
|
179
|
+
except KeyError:
|
|
180
|
+
raise ValueError(
|
|
181
|
+
f"unknown runtime {runtime!r}; supported: {', '.join(SUPPORTED_RUNTIMES)}"
|
|
182
|
+
) from None
|
|
@@ -76,6 +76,22 @@ _EVIDENCE_CONTRACT = (
|
|
|
76
76
|
" 10 lines."
|
|
77
77
|
)
|
|
78
78
|
|
|
79
|
+
# PR-A4: routing was fulfilled in PROSE — the model announced the squad
|
|
80
|
+
# and never invoked the Skill tool, so the department's workflows and
|
|
81
|
+
# contracts never entered context (the label without the payload). This
|
|
82
|
+
# contract defines fulfilment; enforcement stays with A5a/A5b,
|
|
83
|
+
# telemetry-gated.
|
|
84
|
+
_SKILL_ROUTING_CONTRACT = (
|
|
85
|
+
"\n\n[ARKA:SKILL-CONTRACT] Routing IS tool invocation. A department"
|
|
86
|
+
" route is fulfilled by calling the department's hub skill — e.g."
|
|
87
|
+
" Skill(arka-dev), Skill(arka-brand), Skill(arka-content) — BEFORE"
|
|
88
|
+
" substantive work; announcing the squad in prose without invoking"
|
|
89
|
+
" the skill is not routing; it is the label without the payload."
|
|
90
|
+
"\nNever derive a hub name by concatenation — [arka:skill-hint] tags"
|
|
91
|
+
" name the exact call to make. The [arka:trivial] bypass is"
|
|
92
|
+
" unchanged."
|
|
93
|
+
)
|
|
94
|
+
|
|
79
95
|
_META_TAG_CONTRACT = (
|
|
80
96
|
"\n\n[ARKA:META-TAG] Every substantive response ends with a single line:"
|
|
81
97
|
"\n [arka:meta] kb=N research=X persona=Y gap=Z critic=W"
|
|
@@ -348,6 +364,7 @@ def build_context(cwd: str) -> str:
|
|
|
348
364
|
"""
|
|
349
365
|
parts = [
|
|
350
366
|
_EVIDENCE_CONTRACT,
|
|
367
|
+
_SKILL_ROUTING_CONTRACT,
|
|
351
368
|
_META_TAG_CONTRACT,
|
|
352
369
|
_authority_brief(cwd),
|
|
353
370
|
_model_fabric(),
|
|
@@ -31,7 +31,7 @@ import os
|
|
|
31
31
|
import re
|
|
32
32
|
import subprocess
|
|
33
33
|
import sys
|
|
34
|
-
from datetime import
|
|
34
|
+
from datetime import UTC, datetime
|
|
35
35
|
from pathlib import Path
|
|
36
36
|
|
|
37
37
|
_TELEMETRY_DIR = Path.home() / ".arkaos" / "telemetry"
|
|
@@ -135,7 +135,10 @@ def _load_commands_registry() -> list[dict]:
|
|
|
135
135
|
|
|
136
136
|
def _routing_lines(prompt: str, session_id: str, cwd: str) -> list[str]:
|
|
137
137
|
"""UserPromptSubmit routing parity (core/hooks/user_prompt_submit.py):
|
|
138
|
-
[ARKA:ROUTE] on every prompt + L1 [dept:X] + L5 [hint
|
|
138
|
+
[ARKA:ROUTE] on every prompt + L1 [dept:X] + L5 [arka:skill-hint]
|
|
139
|
+
Skill(<hub>) -> /cmd (PR-A4 imperative form; hub resolved by
|
|
140
|
+
layers._hub_skill, never by slug concatenation — a /mkt hint
|
|
141
|
+
renders Skill(arka-marketing)) +
|
|
139
142
|
[ARKA:WORKFLOW-REQUIRED] on creation/implementation prompts. The
|
|
140
143
|
directive strings and the verb regex are IMPORTED from the claude
|
|
141
144
|
hook — one source of truth, zero drift."""
|
|
@@ -170,7 +173,7 @@ def _routing_lines(prompt: str, session_id: str, cwd: str) -> list[str]:
|
|
|
170
173
|
hints = CommandHintsLayer(commands=commands).compute(ctx)
|
|
171
174
|
if hints.tag:
|
|
172
175
|
lines.append(hints.tag)
|
|
173
|
-
except Exception:
|
|
176
|
+
except Exception:
|
|
174
177
|
pass
|
|
175
178
|
if prompt[0] not in ("/", "!") and _WF_VERB_RE.search(prompt):
|
|
176
179
|
lines.append(_WORKFLOW_DIRECTIVE.strip())
|
|
@@ -285,7 +288,7 @@ def _action_post_tool(payload: dict) -> dict:
|
|
|
285
288
|
_append_jsonl(
|
|
286
289
|
_TELEMETRY_DIR / "opencode-tools.jsonl",
|
|
287
290
|
{
|
|
288
|
-
"ts": datetime.now(
|
|
291
|
+
"ts": datetime.now(UTC).isoformat(),
|
|
289
292
|
"session": session_id,
|
|
290
293
|
"tool": mapped,
|
|
291
294
|
"ok": bool(payload.get("ok", True)),
|
|
@@ -319,7 +322,7 @@ def _enqueue_turn_capture(session_id: str, text: str, cwd: str) -> None:
|
|
|
319
322
|
proc.stdin.close()
|
|
320
323
|
except (BrokenPipeError, OSError):
|
|
321
324
|
pass
|
|
322
|
-
except Exception:
|
|
325
|
+
except Exception:
|
|
323
326
|
pass
|
|
324
327
|
|
|
325
328
|
|
|
@@ -347,7 +350,7 @@ def _action_idle(payload: dict) -> dict:
|
|
|
347
350
|
_append_jsonl(
|
|
348
351
|
_TELEMETRY_DIR / "opencode-compliance.jsonl",
|
|
349
352
|
{
|
|
350
|
-
"ts": datetime.now(
|
|
353
|
+
"ts": datetime.now(UTC).isoformat(),
|
|
351
354
|
"session": session_id,
|
|
352
355
|
"kb_cite": result.reason,
|
|
353
356
|
"meta_tag": "[arka:meta]" in text,
|
|
@@ -388,7 +391,7 @@ def _handoff_line(session_id: str, project: str) -> str:
|
|
|
388
391
|
try:
|
|
389
392
|
_handoff_marker(session_id).parent.mkdir(parents=True, exist_ok=True)
|
|
390
393
|
_handoff_marker(session_id).write_text(
|
|
391
|
-
json.dumps({"shown_at": datetime.now(
|
|
394
|
+
json.dumps({"shown_at": datetime.now(UTC).isoformat()}),
|
|
392
395
|
encoding="utf-8",
|
|
393
396
|
)
|
|
394
397
|
except OSError:
|
|
@@ -440,7 +443,7 @@ def _action_memory(payload: dict) -> dict:
|
|
|
440
443
|
handoff = _handoff_line(session_id, project)
|
|
441
444
|
if handoff:
|
|
442
445
|
lines.insert(0, handoff)
|
|
443
|
-
except Exception:
|
|
446
|
+
except Exception:
|
|
444
447
|
return {"context": []}
|
|
445
448
|
return {"context": lines[:6]}
|
|
446
449
|
|
|
@@ -460,7 +463,7 @@ def _action_compact(payload: dict) -> dict:
|
|
|
460
463
|
current = state.get_state()
|
|
461
464
|
if current:
|
|
462
465
|
context.append(f"Workflow activo: {json.dumps(current)[:400]}")
|
|
463
|
-
except Exception:
|
|
466
|
+
except Exception:
|
|
464
467
|
pass
|
|
465
468
|
return {"context": context}
|
|
466
469
|
|
|
@@ -488,7 +491,7 @@ def main() -> int:
|
|
|
488
491
|
else:
|
|
489
492
|
try:
|
|
490
493
|
result = handler(payload)
|
|
491
|
-
except Exception as exc:
|
|
494
|
+
except Exception as exc:
|
|
492
495
|
result = {"error": f"{type(exc).__name__}: {exc}"}
|
|
493
496
|
sys.stdout.write(json.dumps(result, ensure_ascii=False) + "\n")
|
|
494
497
|
return 0
|
package/core/synapse/layers.py
CHANGED
|
@@ -386,8 +386,89 @@ class BranchLayer(Layer):
|
|
|
386
386
|
# --- L5: Command Hints ---
|
|
387
387
|
|
|
388
388
|
|
|
389
|
+
# PR-A4: department slug -> deployed hub-skill directory. Both slug
|
|
390
|
+
# vocabularies reach this layer — the registry department field (which
|
|
391
|
+
# says "leadership") and the command-prefix slugs (which say "lead") —
|
|
392
|
+
# and both must resolve. Registry values need no key: identity already
|
|
393
|
+
# yields the right hub (leadership -> arka-leadership). The keys exist
|
|
394
|
+
# for the four prefix slugs whose hub directory differs (mkt, fin,
|
|
395
|
+
# strat, lead — the same alias set
|
|
396
|
+
# tests/python/test_skill_routing_integrity.py pins) plus the two
|
|
397
|
+
# orchestrator entries ("arka" and "do" both belong to the main
|
|
398
|
+
# orchestrator skill; /do is the universal router in arka/SKILL.md).
|
|
399
|
+
# An unknown department falls back to the identity form — a
|
|
400
|
+
# wrong-but-plausible hint beats a crashed layer.
|
|
401
|
+
_DEPT_HUB_EXCEPTIONS = {
|
|
402
|
+
"mkt": "arka-marketing",
|
|
403
|
+
"fin": "arka-finance",
|
|
404
|
+
"strat": "arka-strategy",
|
|
405
|
+
"lead": "arka-leadership",
|
|
406
|
+
"arka": "arka",
|
|
407
|
+
"do": "arka",
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
def _hub_skill(department: str) -> str:
|
|
412
|
+
return _DEPT_HUB_EXCEPTIONS.get(department, f"arka-{department}")
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
def _hint_department(cmd: dict) -> str:
|
|
416
|
+
"""Department for the hint's hub skill.
|
|
417
|
+
|
|
418
|
+
Falls back to the command prefix ("/dev feature" -> "dev",
|
|
419
|
+
"/arka-do" -> "arka") for registry entries that predate the
|
|
420
|
+
department field — an approximate hub beats no hint.
|
|
421
|
+
"""
|
|
422
|
+
dept = str(cmd.get("department", ""))
|
|
423
|
+
if dept:
|
|
424
|
+
return dept
|
|
425
|
+
prefix = str(cmd.get("command", "")).lstrip("/").split(" ")[0]
|
|
426
|
+
return prefix.split("-")[0]
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
def _score_commands(
|
|
430
|
+
commands: list[dict], text: str
|
|
431
|
+
) -> list[tuple[int, str, str]]:
|
|
432
|
+
"""Keyword-score the registry commands against the prompt text,
|
|
433
|
+
best score first; zero-score commands never qualify."""
|
|
434
|
+
scored = []
|
|
435
|
+
for cmd in commands:
|
|
436
|
+
keywords = cmd.get("keywords", [])
|
|
437
|
+
score = sum(1 for kw in keywords if kw.lower() in text)
|
|
438
|
+
if score > 0:
|
|
439
|
+
scored.append((
|
|
440
|
+
score,
|
|
441
|
+
cmd.get("command", ""),
|
|
442
|
+
_hint_department(cmd),
|
|
443
|
+
))
|
|
444
|
+
scored.sort(reverse=True)
|
|
445
|
+
return scored
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
def _hint_tag(top: list[tuple[int, str, str]]) -> str:
|
|
449
|
+
"""Render the imperative hint tag for the top-scored commands.
|
|
450
|
+
|
|
451
|
+
EACH hint pairs the command with ITS OWN department's hub — the
|
|
452
|
+
top-2 can legitimately span two departments ("standup color
|
|
453
|
+
palette" matches /brand colors AND /pm story on the live registry),
|
|
454
|
+
and a hint whose hub belongs to the other command is a dead
|
|
455
|
+
instruction (QG A4, Francisca B1).
|
|
456
|
+
"""
|
|
457
|
+
return " ".join(
|
|
458
|
+
f"[arka:skill-hint] Skill({_hub_skill(dept)}) -> {command}"
|
|
459
|
+
for _, command, dept in top
|
|
460
|
+
)
|
|
461
|
+
|
|
462
|
+
|
|
389
463
|
class CommandHintsLayer(Layer):
|
|
390
|
-
"""L5: Matching commands from the registry for non-explicit requests.
|
|
464
|
+
"""L5: Matching commands from the registry for non-explicit requests.
|
|
465
|
+
|
|
466
|
+
PR-A4: hints are IMPERATIVE — `[arka:skill-hint] Skill(arka-dev) ->
|
|
467
|
+
/dev feature <description>` names the exact tool call that fulfils
|
|
468
|
+
the route. The old `[hint:/cmd]` form was declarative and routinely
|
|
469
|
+
ignored: the model announced the squad in prose and the skill's
|
|
470
|
+
content never entered context.
|
|
471
|
+
"""
|
|
391
472
|
|
|
392
473
|
def __init__(self, commands: list[dict] | None = None) -> None:
|
|
393
474
|
self._commands = commands or []
|
|
@@ -429,18 +510,10 @@ class CommandHintsLayer(Layer):
|
|
|
429
510
|
cached=False,
|
|
430
511
|
)
|
|
431
512
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
for cmd in self._commands:
|
|
435
|
-
keywords = cmd.get("keywords", [])
|
|
436
|
-
score = sum(1 for kw in keywords if kw.lower() in text)
|
|
437
|
-
if score > 0:
|
|
438
|
-
scored.append((score, cmd.get("command", "")))
|
|
439
|
-
|
|
440
|
-
scored.sort(reverse=True)
|
|
441
|
-
hints = [cmd for _, cmd in scored[:2]]
|
|
513
|
+
top = _score_commands(self._commands, text)[:2]
|
|
514
|
+
hints = [command for _, command, _ in top]
|
|
442
515
|
|
|
443
|
-
tags =
|
|
516
|
+
tags = _hint_tag(top)
|
|
444
517
|
ms = int((time.time() - start) * 1000)
|
|
445
518
|
return LayerResult(
|
|
446
519
|
layer_id=self.id,
|
package/harness/codex/AGENTS.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# ArkaOS — The Operating System for AI Agent Teams
|
|
2
2
|
|
|
3
|
-
> v4.
|
|
3
|
+
> v4.46.0 — 89 agents, 17 departments, 332 skills. Generated by `scripts/harness_gen.py`; do not edit.
|
|
4
4
|
|
|
5
5
|
You are operating within ArkaOS. Every request routes through the
|
|
6
6
|
appropriate department squad — never respond as a generic assistant.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# ArkaOS — The Operating System for AI Agent Teams
|
|
2
2
|
|
|
3
|
-
> v4.
|
|
3
|
+
> v4.46.0 — 89 agents, 17 departments, 332 skills. Generated by `scripts/harness_gen.py`; do not edit.
|
|
4
4
|
|
|
5
5
|
You are operating within ArkaOS. Every request routes through the
|
|
6
6
|
appropriate department squad — never respond as a generic assistant.
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: ArkaOS v4.
|
|
2
|
+
description: ArkaOS v4.46.0 agent-team contract
|
|
3
3
|
alwaysApply: true
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# ArkaOS — The Operating System for AI Agent Teams
|
|
7
7
|
|
|
8
|
-
> v4.
|
|
8
|
+
> v4.46.0 — 89 agents, 17 departments, 332 skills. Generated by `scripts/harness_gen.py`; do not edit.
|
|
9
9
|
|
|
10
10
|
You are operating within ArkaOS. Every request routes through the
|
|
11
11
|
appropriate department squad — never respond as a generic assistant.
|
package/harness/gemini/GEMINI.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# ArkaOS — The Operating System for AI Agent Teams
|
|
2
2
|
|
|
3
|
-
> v4.
|
|
3
|
+
> v4.46.0 — 89 agents, 17 departments, 332 skills. Generated by `scripts/harness_gen.py`; do not edit.
|
|
4
4
|
|
|
5
5
|
You are operating within ArkaOS. Every request routes through the
|
|
6
6
|
appropriate department squad — never respond as a generic assistant.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# ArkaOS — The Operating System for AI Agent Teams
|
|
2
2
|
|
|
3
|
-
> v4.
|
|
3
|
+
> v4.46.0 — 89 agents, 17 departments, 332 skills. Generated by `scripts/harness_gen.py`; do not edit.
|
|
4
4
|
|
|
5
5
|
You are operating within ArkaOS. Every request routes through the
|
|
6
6
|
appropriate department squad — never respond as a generic assistant.
|
|
@@ -3,7 +3,7 @@ description: "Software Architect — ArkaOS /dev department"
|
|
|
3
3
|
mode: subagent
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
You are Gabriel, Software Architect of the ArkaOS /dev department (v4.
|
|
6
|
+
You are Gabriel, Software Architect of the ArkaOS /dev department (v4.46.0; generated by scripts/harness_gen.py — do not edit).
|
|
7
7
|
|
|
8
8
|
Expertise: system design, system visualization via dev/diagram (architecture + dataflow diagrams delivered as browser artifacts), domain modeling (event storming, bounded contexts), design patterns (GoF, PoEAA), business / domain analysis, API design.
|
|
9
9
|
|
|
@@ -3,7 +3,7 @@ description: "Creative Director — ArkaOS /brand department"
|
|
|
3
3
|
mode: subagent
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
You are Valentina, Creative Director of the ArkaOS /brand department (v4.
|
|
6
|
+
You are Valentina, Creative Director of the ArkaOS /brand department (v4.46.0; generated by scripts/harness_gen.py — do not edit).
|
|
7
7
|
|
|
8
8
|
Expertise: brand identity creation, reference-video visual analysis via dev/watch (complete frames + transcript — motion and art direction judged on evidence, never on screenshots), visual design direction, UX/UI strategy, design systems, brand voice & tone.
|
|
9
9
|
|
|
@@ -3,7 +3,7 @@ description: "Chief Financial Officer — ArkaOS /fin department"
|
|
|
3
3
|
mode: subagent
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
You are Helena, Chief Financial Officer of the ArkaOS /fin department (v4.
|
|
6
|
+
You are Helena, Chief Financial Officer of the ArkaOS /fin department (v4.46.0; generated by scripts/harness_gen.py — do not edit).
|
|
7
7
|
|
|
8
8
|
Expertise: financial planning & analysis, valuation & investment, unit economics & SaaS metrics, risk management & ERM, fundraising & cap tables, cash flow management.
|
|
9
9
|
|
|
@@ -3,7 +3,7 @@ description: "Chief of Staff & Governance Lead — ArkaOS /org department"
|
|
|
3
3
|
mode: subagent
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
You are Afonso, Chief of Staff & Governance Lead of the ArkaOS /org department (v4.
|
|
6
|
+
You are Afonso, Chief of Staff & Governance Lead of the ArkaOS /org department (v4.46.0; generated by scripts/harness_gen.py — do not edit).
|
|
7
7
|
|
|
8
8
|
Expertise: meeting cadence (daily/weekly/quarterly/annual), OKR & CFR orchestration cross-department, decision records & RACI, premortem / blameless postmortem rituals, governance, board & founder-CEO succession, strategic alignment & single-threaded leadership.
|
|
9
9
|
|