arkaos 4.8.0 → 4.10.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/VERSION +1 -1
- package/arka/skills/research/SKILL.md +6 -6
- package/bin/arka +5 -1
- package/bin/arkaos +2 -2
- package/config/disc-team-validator.sh +7 -7
- package/config/evals/dev.yaml +28 -0
- package/config/evals/finance.yaml +28 -0
- package/config/evals/kb.yaml +26 -0
- package/config/evals/marketing.yaml +28 -0
- package/config/evals/strategy.yaml +26 -0
- package/core/agents/__pycache__/registry_gen.cpython-313.pyc +0 -0
- package/core/agents/registry_gen.py +6 -4
- package/core/evals/__init__.py +19 -0
- package/core/evals/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/evals/__pycache__/schema.cpython-313.pyc +0 -0
- package/core/evals/__pycache__/verdict_labels.cpython-313.pyc +0 -0
- package/core/evals/schema.py +54 -0
- package/core/evals/verdict_labels.py +72 -0
- package/core/registry/__pycache__/__init__.cpython-312.pyc +0 -0
- package/core/registry/__pycache__/generator.cpython-312.pyc +0 -0
- package/core/registry/__pycache__/generator.cpython-313.pyc +0 -0
- package/core/registry/generator.py +138 -110
- package/core/runtime/__pycache__/llm_cost_telemetry.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/llm_cost_telemetry.cpython-314.pyc +0 -0
- package/core/runtime/llm_cost_telemetry.py +13 -1
- package/core/synapse/__pycache__/engine.cpython-313.pyc +0 -0
- package/core/synapse/__pycache__/engine.cpython-314.pyc +0 -0
- package/core/synapse/__pycache__/layers.cpython-313.pyc +0 -0
- package/core/synapse/__pycache__/layers.cpython-314.pyc +0 -0
- package/core/synapse/engine.py +6 -0
- package/core/synapse/layers.py +17 -0
- package/departments/dev/skills/research/SKILL.md +3 -2
- package/departments/dev/skills/scaffold/SKILL.md +3 -3
- package/knowledge/commands-registry.json +4194 -2627
- package/knowledge/commands-registry.json.bak +4194 -2627
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/scripts/__pycache__/dashboard-api.cpython-313.pyc +0 -0
- package/scripts/__pycache__/synapse-bridge.cpython-313.pyc +0 -0
- package/scripts/__pycache__/synapse-bridge.cpython-314.pyc +0 -0
- package/scripts/dashboard-api.py +2 -2
- package/scripts/synapse-bridge.py +1 -1
- package/scripts/tools/__pycache__/prompt_surface_benchmark.cpython-313.pyc +0 -0
- package/scripts/tools/prompt_surface_benchmark.py +144 -0
- package/knowledge/agents-registry.json +0 -254
- package/knowledge/commands-registry-v2.json +0 -3827
|
@@ -1,28 +1,47 @@
|
|
|
1
|
-
"""Commands registry generator.
|
|
2
|
-
|
|
3
|
-
Scans
|
|
4
|
-
|
|
1
|
+
"""Commands registry generator — single canonical source (M2 consolidation).
|
|
2
|
+
|
|
3
|
+
Scans every SKILL.md command table in the repo (arka orchestrator,
|
|
4
|
+
department SKILL.md files, and department sub-skills) and generates
|
|
5
|
+
``knowledge/commands-registry.json`` — the one registry consumed by
|
|
6
|
+
runtime routing (system-prompt.sh), Synapse L5 hints (synapse-bridge),
|
|
7
|
+
``bin/arka commands``, the dashboard API, and ``bin/arkaos`` status.
|
|
8
|
+
|
|
9
|
+
Replaces the retired bash ``bin/arka-registry-gen`` and the parallel
|
|
10
|
+
``commands-registry-v2.json`` (2026-07-09): one deterministic generator,
|
|
11
|
+
repo sources only — machine-local ``arka-ext-*``/``arka-pro-*`` skills
|
|
12
|
+
are intentionally NOT scanned so the committed file is reproducible;
|
|
13
|
+
``tests/python/test_commands_registry.py`` locks committed-vs-regen
|
|
14
|
+
drift the same way the agents registry is locked.
|
|
15
|
+
|
|
16
|
+
Department naming follows the command prefixes (``mkt``/``fin``/
|
|
17
|
+
``strat``), matching the runtime tables in CLAUDE.md.
|
|
5
18
|
"""
|
|
6
19
|
|
|
7
20
|
import json
|
|
8
21
|
import re
|
|
9
|
-
from datetime import datetime
|
|
22
|
+
from datetime import datetime, timezone
|
|
10
23
|
from pathlib import Path
|
|
11
|
-
from typing import Optional
|
|
12
24
|
|
|
25
|
+
# Directory name → command-prefix department slug.
|
|
26
|
+
DEPT_PREFIX = {
|
|
27
|
+
"marketing": "mkt",
|
|
28
|
+
"finance": "fin",
|
|
29
|
+
"strategy": "strat",
|
|
30
|
+
}
|
|
13
31
|
|
|
14
|
-
#
|
|
32
|
+
# Fallback keywords per department slug when a command has no entry in
|
|
33
|
+
# knowledge/commands-keywords.json — feeds Synapse L5 hint matching.
|
|
15
34
|
DEPARTMENT_KEYWORDS: dict[str, list[str]] = {
|
|
16
35
|
"dev": ["build", "code", "feature", "deploy", "test", "review", "scaffold", "debug",
|
|
17
36
|
"refactor", "api", "migration", "implement", "fix", "bug", "database", "architecture"],
|
|
18
|
-
"
|
|
19
|
-
|
|
37
|
+
"mkt": ["social", "content", "campaign", "post", "instagram", "linkedin", "twitter",
|
|
38
|
+
"tiktok", "seo", "marketing", "ads", "email", "growth", "analytics"],
|
|
20
39
|
"brand": ["brand", "logo", "colors", "palette", "mockup", "identity", "naming",
|
|
21
40
|
"positioning", "voice", "guidelines", "ux", "wireframe", "design", "ui"],
|
|
22
|
-
"
|
|
23
|
-
|
|
24
|
-
"
|
|
25
|
-
|
|
41
|
+
"fin": ["budget", "invoice", "revenue", "forecast", "profit", "financial", "invest",
|
|
42
|
+
"valuation", "cashflow", "expense", "pitch", "model", "scenario"],
|
|
43
|
+
"strat": ["strategy", "brainstorm", "market", "swot", "competitor", "roadmap",
|
|
44
|
+
"position", "blue ocean", "five forces", "tam", "moat", "growth"],
|
|
26
45
|
"ecom": ["store", "product", "shop", "shopify", "ecommerce", "catalog", "cart",
|
|
27
46
|
"checkout", "pricing", "marketplace", "rfm", "conversion", "cro"],
|
|
28
47
|
"kb": ["learn", "persona", "knowledge", "youtube", "transcribe", "research",
|
|
@@ -41,150 +60,159 @@ DEPARTMENT_KEYWORDS: dict[str, list[str]] = {
|
|
|
41
60
|
"circle", "gamification", "engagement", "moderate", "event"],
|
|
42
61
|
"sales": ["pipeline", "proposal", "discovery", "objection", "negotiate", "deal",
|
|
43
62
|
"close", "prospect", "spin", "challenger", "forecast"],
|
|
44
|
-
"
|
|
45
|
-
|
|
63
|
+
"leadership": ["leadership", "delegation", "1on1", "feedback", "culture", "hiring",
|
|
64
|
+
"performance review", "team build", "conflict", "okr"],
|
|
46
65
|
"org": ["org design", "hiring plan", "onboarding", "remote", "meeting",
|
|
47
66
|
"compensation", "decision", "team assess", "sop"],
|
|
48
67
|
}
|
|
49
68
|
|
|
69
|
+
# Metadata rules ported verbatim from the retired bin/arka-registry-gen.
|
|
70
|
+
_DEV_BRANCH_WORDS = {"feature", "api", "debug", "refactor", "db"}
|
|
71
|
+
_DEV_MODIFY_WORDS = {"scaffold", "deploy", "test"}
|
|
72
|
+
|
|
73
|
+
_TABLE_ROW_RE = re.compile(r"\|\s*`([^`]+)`\s*\|\s*([^|]+)\|")
|
|
74
|
+
_FRONTMATTER_NAME_RE = re.compile(r"^name:\s*(\S+)", re.MULTILINE)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def derive_command_id(command_text: str) -> str:
|
|
78
|
+
"""``/arka costs [period]`` → ``arka-costs``.
|
|
79
|
+
|
|
80
|
+
Drops everything from the first argument token (``<arg>``,
|
|
81
|
+
``[optional]``, ``--flag``) onward, so ids never embed argument
|
|
82
|
+
syntax (the old ``dev-onboard---ecosystem`` class of bug).
|
|
83
|
+
"""
|
|
84
|
+
tokens: list[str] = []
|
|
85
|
+
for token in command_text.lstrip("/").split():
|
|
86
|
+
if token.startswith(("<", "[", "--")):
|
|
87
|
+
break
|
|
88
|
+
tokens.append(token)
|
|
89
|
+
return "-".join(tokens).lower()
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def extract_lead_agent(text: str) -> str:
|
|
93
|
+
match = _FRONTMATTER_NAME_RE.search(text)
|
|
94
|
+
return match.group(1) if match else ""
|
|
95
|
+
|
|
50
96
|
|
|
51
97
|
def extract_commands_from_skill(skill_path: Path) -> list[dict]:
|
|
52
98
|
"""Extract commands from a SKILL.md file's command table.
|
|
53
99
|
|
|
54
|
-
Parses markdown tables with |
|
|
100
|
+
Parses markdown tables with | `/command` | Description | rows.
|
|
55
101
|
"""
|
|
56
102
|
if not skill_path.exists():
|
|
57
103
|
return []
|
|
58
104
|
|
|
59
|
-
text = skill_path.read_text()
|
|
105
|
+
text = skill_path.read_text(encoding="utf-8")
|
|
106
|
+
lead_agent = extract_lead_agent(text)
|
|
60
107
|
commands = []
|
|
61
|
-
|
|
62
|
-
# Find command tables (lines with | `/ ... ` | description | ...)
|
|
63
108
|
for line in text.split("\n"):
|
|
64
|
-
match =
|
|
65
|
-
if match:
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
109
|
+
match = _TABLE_ROW_RE.match(line)
|
|
110
|
+
if not match:
|
|
111
|
+
continue
|
|
112
|
+
command = match.group(1).strip()
|
|
113
|
+
description = match.group(2).strip()
|
|
114
|
+
if not command.startswith("/") or "Description" in description or description.startswith("-"):
|
|
115
|
+
continue
|
|
116
|
+
commands.append({
|
|
117
|
+
"command": command,
|
|
118
|
+
"description": description,
|
|
119
|
+
"lead_agent": lead_agent,
|
|
120
|
+
})
|
|
75
121
|
return commands
|
|
76
122
|
|
|
77
123
|
|
|
78
|
-
def
|
|
79
|
-
"""
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
124
|
+
def command_metadata(command_text: str) -> dict:
|
|
125
|
+
"""tier / requires_branch / modifies_code — bash-generator rules."""
|
|
126
|
+
if command_text.startswith("/dev "):
|
|
127
|
+
first_arg = command_text.split()[1] if len(command_text.split()) > 1 else ""
|
|
128
|
+
if first_arg in _DEV_BRANCH_WORDS:
|
|
129
|
+
return {"tier": 1, "requires_branch": True, "modifies_code": True}
|
|
130
|
+
if first_arg in _DEV_MODIFY_WORDS:
|
|
131
|
+
return {"tier": 2, "requires_branch": False, "modifies_code": True}
|
|
132
|
+
if first_arg == "security-audit":
|
|
133
|
+
return {"tier": 1, "requires_branch": False, "modifies_code": False}
|
|
134
|
+
return {"tier": 2, "requires_branch": False, "modifies_code": False}
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _load_keywords_seed(base_dir: Path) -> dict:
|
|
138
|
+
seed_path = base_dir / "knowledge" / "commands-keywords.json"
|
|
139
|
+
if not seed_path.exists():
|
|
140
|
+
return {}
|
|
141
|
+
return json.loads(seed_path.read_text(encoding="utf-8"))
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _department_slug(skill_path: Path) -> str:
|
|
145
|
+
# departments/<dir>/SKILL.md and departments/<dir>/skills/<s>/SKILL.md
|
|
146
|
+
parts = skill_path.parts
|
|
147
|
+
dir_name = parts[parts.index("departments") + 1]
|
|
148
|
+
return DEPT_PREFIX.get(dir_name, dir_name)
|
|
84
149
|
|
|
85
150
|
|
|
86
|
-
def
|
|
87
|
-
"""
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
151
|
+
def _skill_sources(base_dir: Path) -> list[tuple[Path, str]]:
|
|
152
|
+
"""(skill_path, department) in deterministic scan order.
|
|
153
|
+
|
|
154
|
+
Order matters: dedup keeps the FIRST occurrence of an id, so the
|
|
155
|
+
orchestrator wins over departments, departments over sub-skills.
|
|
156
|
+
"""
|
|
157
|
+
sources: list[tuple[Path, str]] = [(base_dir / "arka" / "SKILL.md", "arka")]
|
|
158
|
+
for path in sorted(base_dir.glob("departments/*/SKILL.md")):
|
|
159
|
+
sources.append((path, _department_slug(path)))
|
|
160
|
+
for path in sorted(base_dir.glob("departments/*/skills/*/SKILL.md")):
|
|
161
|
+
sources.append((path, _department_slug(path)))
|
|
162
|
+
return sources
|
|
94
163
|
|
|
95
164
|
|
|
96
165
|
def generate_commands_registry(
|
|
97
166
|
base_dir: str | Path,
|
|
98
167
|
output_path: str | Path,
|
|
99
168
|
) -> dict:
|
|
100
|
-
"""Generate commands-registry
|
|
101
|
-
|
|
102
|
-
Args:
|
|
103
|
-
base_dir: Project root directory.
|
|
104
|
-
output_path: Where to write the JSON registry.
|
|
105
|
-
|
|
106
|
-
Returns:
|
|
107
|
-
The registry dict.
|
|
108
|
-
"""
|
|
169
|
+
"""Generate the canonical commands-registry.json from SKILL.md files."""
|
|
109
170
|
base_dir = Path(base_dir)
|
|
110
171
|
output_path = Path(output_path)
|
|
172
|
+
keywords_seed = _load_keywords_seed(base_dir)
|
|
111
173
|
|
|
112
|
-
all_commands = []
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
"dev", "marketing", "brand", "finance", "strategy", "ecom", "kb",
|
|
117
|
-
"ops", "pm", "saas", "landing", "content", "community", "sales",
|
|
118
|
-
"leadership", "org", "quality",
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
# Scan department SKILL.md files
|
|
122
|
-
for skill_file in sorted(base_dir.glob("departments/*/SKILL.md")):
|
|
123
|
-
if skill_file.parent.name not in v2_departments:
|
|
124
|
-
continue
|
|
125
|
-
dept = determine_department(skill_file)
|
|
126
|
-
raw_commands = extract_commands_from_skill(skill_file)
|
|
127
|
-
keywords = DEPARTMENT_KEYWORDS.get(dept, [])
|
|
128
|
-
|
|
129
|
-
for cmd in raw_commands:
|
|
174
|
+
all_commands: list[dict] = []
|
|
175
|
+
seen_ids: set[str] = set()
|
|
176
|
+
for skill_path, dept in _skill_sources(base_dir):
|
|
177
|
+
for cmd in extract_commands_from_skill(skill_path):
|
|
130
178
|
command_text = cmd["command"]
|
|
131
|
-
|
|
132
|
-
cmd_id
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
)
|
|
138
|
-
|
|
179
|
+
cmd_id = derive_command_id(command_text)
|
|
180
|
+
if not cmd_id or cmd_id in seen_ids:
|
|
181
|
+
continue
|
|
182
|
+
seen_ids.add(cmd_id)
|
|
183
|
+
seed = keywords_seed.get(cmd_id, {})
|
|
184
|
+
keywords = seed.get("keywords") or DEPARTMENT_KEYWORDS.get(dept, [])[:8]
|
|
139
185
|
all_commands.append({
|
|
140
186
|
"id": cmd_id,
|
|
141
187
|
"command": command_text,
|
|
142
188
|
"department": dept,
|
|
143
189
|
"description": cmd["description"],
|
|
144
|
-
"
|
|
145
|
-
"
|
|
146
|
-
"
|
|
147
|
-
"
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
# Scan orchestrator SKILL.md
|
|
151
|
-
arka_skill = base_dir / "arka" / "SKILL.md"
|
|
152
|
-
if arka_skill.exists():
|
|
153
|
-
raw_commands = extract_commands_from_skill(arka_skill)
|
|
154
|
-
for cmd in raw_commands:
|
|
155
|
-
command_text = cmd["command"]
|
|
156
|
-
cmd_id = command_text.lstrip("/").replace(" ", "-").split("<")[0].rstrip("-")
|
|
157
|
-
all_commands.append({
|
|
158
|
-
"id": cmd_id,
|
|
159
|
-
"command": command_text,
|
|
160
|
-
"department": "arka",
|
|
161
|
-
"description": cmd["description"],
|
|
162
|
-
"keywords": [],
|
|
163
|
-
"tier": 3,
|
|
164
|
-
"modifies_code": False,
|
|
165
|
-
"requires_branch": False,
|
|
190
|
+
"lead_agent": cmd["lead_agent"],
|
|
191
|
+
"keywords": keywords,
|
|
192
|
+
"examples": seed.get("examples", []),
|
|
193
|
+
"source": "builtin",
|
|
194
|
+
**command_metadata(command_text),
|
|
166
195
|
})
|
|
167
196
|
|
|
168
|
-
# Build department summary
|
|
169
197
|
dept_counts: dict[str, int] = {}
|
|
170
198
|
for cmd in all_commands:
|
|
171
|
-
|
|
172
|
-
dept_counts[d] = dept_counts.get(d, 0) + 1
|
|
199
|
+
dept_counts[cmd["department"]] = dept_counts.get(cmd["department"], 0) + 1
|
|
173
200
|
|
|
174
201
|
registry = {
|
|
175
202
|
"_meta": {
|
|
176
|
-
"version": "
|
|
177
|
-
"generated": datetime.now().
|
|
203
|
+
"version": "3.0.0",
|
|
204
|
+
"generated": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
|
178
205
|
"total_commands": len(all_commands),
|
|
179
206
|
"generator": "core/registry/generator.py",
|
|
180
|
-
"departments": dept_counts,
|
|
207
|
+
"departments": dict(sorted(dept_counts.items())),
|
|
181
208
|
},
|
|
182
209
|
"commands": all_commands,
|
|
183
210
|
}
|
|
184
211
|
|
|
185
212
|
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
186
|
-
with open(output_path, "w") as f:
|
|
213
|
+
with open(output_path, "w", encoding="utf-8") as f:
|
|
187
214
|
json.dump(registry, f, indent=2, ensure_ascii=False)
|
|
215
|
+
f.write("\n")
|
|
188
216
|
|
|
189
217
|
return registry
|
|
190
218
|
|
|
@@ -193,7 +221,7 @@ if __name__ == "__main__":
|
|
|
193
221
|
base = Path(__file__).parent.parent.parent
|
|
194
222
|
reg = generate_commands_registry(
|
|
195
223
|
base,
|
|
196
|
-
base / "knowledge" / "commands-registry
|
|
224
|
+
base / "knowledge" / "commands-registry.json",
|
|
197
225
|
)
|
|
198
|
-
print(f"
|
|
226
|
+
print(f"Registry generated: {reg['_meta']['total_commands']} commands")
|
|
199
227
|
print(f"Departments: {reg['_meta']['departments']}")
|
|
Binary file
|
|
Binary file
|
|
@@ -232,6 +232,16 @@ def _load_slice(
|
|
|
232
232
|
return kept, corrupt
|
|
233
233
|
|
|
234
234
|
|
|
235
|
+
def _is_fallback_diagnostic(entry: dict[str, Any]) -> bool:
|
|
236
|
+
"""Provider-fallback rows carry the fallback REASON in the model field
|
|
237
|
+
(_log_fallback in llm_provider.py: model='unavailable'/'selected').
|
|
238
|
+
They belong in the by-provider view (degraded chains) but leak bogus
|
|
239
|
+
zero-token rows into any model-keyed breakdown. Totals and by_provider
|
|
240
|
+
keep counting them, so sum(by_model[*].call_count) < call_count when
|
|
241
|
+
fallbacks occurred — intentional, not a reconciliation bug."""
|
|
242
|
+
return str(entry.get("provider") or "").startswith("fallback:")
|
|
243
|
+
|
|
244
|
+
|
|
235
245
|
def _group(entries: list[dict[str, Any]], key: str) -> dict[str, dict[str, Any]]:
|
|
236
246
|
out: dict[str, dict[str, Any]] = {}
|
|
237
247
|
for entry in entries:
|
|
@@ -298,7 +308,9 @@ def summarise(
|
|
|
298
308
|
cache_hit_rate=finalised["cache_hit_rate"],
|
|
299
309
|
call_count=finalised["call_count"],
|
|
300
310
|
by_provider=_group(entries, "provider"),
|
|
301
|
-
by_model=_group(
|
|
311
|
+
by_model=_group(
|
|
312
|
+
[e for e in entries if not _is_fallback_diagnostic(e)], "model"
|
|
313
|
+
),
|
|
302
314
|
by_category=_group(entries, "category"),
|
|
303
315
|
by_session=sessions,
|
|
304
316
|
advisories=_build_advisories(sessions, advisory_threshold_usd),
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/core/synapse/engine.py
CHANGED
|
@@ -118,6 +118,12 @@ class SynapseEngine:
|
|
|
118
118
|
(ctx.user_input or "").encode("utf-8", "replace")
|
|
119
119
|
).hexdigest()[:12]
|
|
120
120
|
cache_key += f":{digest}"
|
|
121
|
+
if getattr(layer, "session_sensitive", False):
|
|
122
|
+
# Session-sensitive layers (KB retrieval) must recompute per
|
|
123
|
+
# session: a cross-session cache hit would skip their
|
|
124
|
+
# per-session side effects (KBSessionCache.store).
|
|
125
|
+
session_id = (ctx.extra or {}).get("session_id", "default")
|
|
126
|
+
cache_key += f":{session_id}"
|
|
121
127
|
|
|
122
128
|
# Check cache
|
|
123
129
|
if layer.cache_ttl > 0:
|
package/core/synapse/layers.py
CHANGED
|
@@ -86,6 +86,19 @@ class Layer(ABC):
|
|
|
86
86
|
"""
|
|
87
87
|
return False
|
|
88
88
|
|
|
89
|
+
@property
|
|
90
|
+
def session_sensitive(self) -> bool:
|
|
91
|
+
"""True when compute() has per-session side effects or output.
|
|
92
|
+
|
|
93
|
+
Session-sensitive layers get ctx.extra['session_id'] added to
|
|
94
|
+
their cache key — without it, a cache hit from one session
|
|
95
|
+
suppresses compute() for a concurrent session in the same cwd,
|
|
96
|
+
skipping that session's side effects (found 2026-07-09: L3.5's
|
|
97
|
+
KBSessionCache.store() never ran for the second session, so its
|
|
98
|
+
KB-injected state and overlap markers belonged to the first).
|
|
99
|
+
"""
|
|
100
|
+
return False
|
|
101
|
+
|
|
89
102
|
@property
|
|
90
103
|
def priority(self) -> int:
|
|
91
104
|
"""Layer priority (lower = computed first)."""
|
|
@@ -546,6 +559,10 @@ class KnowledgeRetrievalLayer(Layer):
|
|
|
546
559
|
def input_sensitive(self) -> bool:
|
|
547
560
|
return True
|
|
548
561
|
|
|
562
|
+
@property
|
|
563
|
+
def session_sensitive(self) -> bool:
|
|
564
|
+
return True
|
|
565
|
+
|
|
549
566
|
@property
|
|
550
567
|
def cache_ttl(self) -> int:
|
|
551
568
|
return 30
|
|
@@ -12,8 +12,9 @@ description: >
|
|
|
12
12
|
adding a new dependency or committing to an architecture-relevant
|
|
13
13
|
library.
|
|
14
14
|
SKIP: general, market, or knowledge-base research whose deliverable
|
|
15
|
-
is an Obsidian KB note
|
|
16
|
-
|
|
15
|
+
is an Obsidian KB note, including "best practices for" a non-code
|
|
16
|
+
topic — arka-research (/arka research, 5-source fan-out) wins;
|
|
17
|
+
requirements definition — arka-dev-spec wins.
|
|
17
18
|
allowed-tools: [Read, Write, Edit, Bash, Grep, Glob, Agent, WebFetch, WebSearch]
|
|
18
19
|
---
|
|
19
20
|
|
|
@@ -18,9 +18,9 @@ Create new projects from real git repositories with full automation: dependencie
|
|
|
18
18
|
## Commands
|
|
19
19
|
|
|
20
20
|
<!-- Column convention: command | DESCRIPTION | repo. The registry
|
|
21
|
-
generator (
|
|
22
|
-
description — a repo URL there ships garbled command
|
|
23
|
-
blocker, 2026-07-09). -->
|
|
21
|
+
generator (core/registry/generator.py) reads column 2 as the
|
|
22
|
+
user-facing description — a repo URL there ships garbled command
|
|
23
|
+
help (QG blocker, 2026-07-09). -->
|
|
24
24
|
| Command | Description | Git Repository |
|
|
25
25
|
|---------|-------------|----------------|
|
|
26
26
|
| `/dev scaffold laravel <name>` | Scaffold a Laravel app into ~/Herd from the starter repo | `https://${GIT_HOST}/laravel/laravel.git` (override with `ARKAOS_LARAVEL_STARTER_REPO` env) |
|