arkaos 4.30.0 → 4.31.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/core/governance/evidence_checks.py +185 -1
- package/departments/brand/skills/design-review/SKILL.md +6 -0
- package/departments/brand/skills/ux-audit/SKILL.md +4 -1
- 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/zed/.rules +1 -1
- package/installer/frontend-tooling.js +36 -0
- package/knowledge/skills-manifest.json +1 -1
- package/package.json +1 -1
- package/pyproject.toml +1 -1
package/THE-ARKAOS-GUIDE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# The ArkaOS Guide
|
|
2
2
|
|
|
3
|
-
> v4.
|
|
3
|
+
> v4.31.0 — 89 agents, 17 departments, 329 skills, 297 commands, 16 ADRs.
|
|
4
4
|
> One file, everything you need to start. Generated by `scripts/guide_gen.py` — never hand-edited.
|
|
5
5
|
|
|
6
6
|
## What it is
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
4.
|
|
1
|
+
4.31.0
|
|
@@ -42,7 +42,7 @@ TIMEOUT_SECONDS = 300
|
|
|
42
42
|
COVERAGE_THRESHOLD = 80.0
|
|
43
43
|
ALL_CHECKS: tuple[str, ...] = (
|
|
44
44
|
"lint", "typecheck", "tests", "coverage", "security-grep", "spellcheck",
|
|
45
|
-
"ui-screenshot",
|
|
45
|
+
"ui-screenshot", "design-slop",
|
|
46
46
|
)
|
|
47
47
|
|
|
48
48
|
# ui-screenshot artifact contract (Excellence Reform PR-D3): captures land
|
|
@@ -52,6 +52,26 @@ UI_EVIDENCE_DIR = Path(".arka") / "evidence" / "ui"
|
|
|
52
52
|
UI_SCREENSHOT_WINDOW_HOURS = 24
|
|
53
53
|
UI_SCREENSHOT_MIN_BYTES = 10 * 1024
|
|
54
54
|
|
|
55
|
+
# design-slop: the deterministic static half of the visual-review loop.
|
|
56
|
+
# Shells the external `impeccable` npm CLI (46 anti-pattern rules, no
|
|
57
|
+
# LLM) over the CHANGED UI files only — never a whole-tree scan. The
|
|
58
|
+
# gate never installs anything (supply-chain: `npx --no-install`).
|
|
59
|
+
# Modes via ``governance.designSlop`` in ``~/.arkaos/config.json``:
|
|
60
|
+
# off → skip · warn (default) → advisory summary, never fails ·
|
|
61
|
+
# hard → `warning` findings fail; `advisory` findings never fail.
|
|
62
|
+
DESIGN_SLOP_TIMEOUT = 120
|
|
63
|
+
_DESIGN_SLOP_MIN_NODE = (22, 12)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _design_slop_config_path() -> Path:
|
|
67
|
+
"""Resolved at call time so tests can redirect HOME (never baked)."""
|
|
68
|
+
return Path.home() / ".arkaos" / "config.json"
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _design_slop_telemetry_path() -> Path:
|
|
72
|
+
"""Resolved at call time so tests can redirect HOME (never baked)."""
|
|
73
|
+
return Path.home() / ".arkaos" / "telemetry" / "design-slop.jsonl"
|
|
74
|
+
|
|
55
75
|
_MAX_SUMMARY_CHARS = 800
|
|
56
76
|
_MAX_GREP_HITS = 20
|
|
57
77
|
|
|
@@ -661,6 +681,169 @@ def _check_ui_screenshot(
|
|
|
661
681
|
)
|
|
662
682
|
|
|
663
683
|
|
|
684
|
+
def _design_slop_mode() -> str:
|
|
685
|
+
"""Resolve ``governance.designSlop`` to 'off' | 'warn' | 'hard'."""
|
|
686
|
+
config = _design_slop_config_path()
|
|
687
|
+
if not config.exists():
|
|
688
|
+
return "warn"
|
|
689
|
+
try:
|
|
690
|
+
data = json.loads(config.read_text(encoding="utf-8"))
|
|
691
|
+
except (json.JSONDecodeError, OSError):
|
|
692
|
+
return "warn"
|
|
693
|
+
raw = data.get("governance", {}).get("designSlop", "warn")
|
|
694
|
+
if raw in (False, "off", "false"):
|
|
695
|
+
return "off"
|
|
696
|
+
if raw in (True, "hard", "true"):
|
|
697
|
+
return "hard"
|
|
698
|
+
return "warn"
|
|
699
|
+
|
|
700
|
+
|
|
701
|
+
def _resolve_detector(project_dir: Path) -> list[str] | None:
|
|
702
|
+
"""Locate the impeccable CLI without ever installing it."""
|
|
703
|
+
direct = shutil.which("impeccable")
|
|
704
|
+
if direct:
|
|
705
|
+
return [direct]
|
|
706
|
+
local = project_dir / "node_modules" / ".bin" / "impeccable"
|
|
707
|
+
if local.is_file():
|
|
708
|
+
return [str(local)]
|
|
709
|
+
if shutil.which("npx"):
|
|
710
|
+
return ["npx", "--no-install", "impeccable"]
|
|
711
|
+
return None
|
|
712
|
+
|
|
713
|
+
|
|
714
|
+
def _node_supports_detector() -> bool:
|
|
715
|
+
"""True when `node --version` meets the detector's floor (22.12)."""
|
|
716
|
+
node = shutil.which("node")
|
|
717
|
+
if not node:
|
|
718
|
+
return False
|
|
719
|
+
try:
|
|
720
|
+
proc = subprocess.run(
|
|
721
|
+
[node, "--version"], capture_output=True, text=True, timeout=10,
|
|
722
|
+
)
|
|
723
|
+
major, minor = proc.stdout.strip().lstrip("v").split(".")[:2]
|
|
724
|
+
return (int(major), int(minor)) >= _DESIGN_SLOP_MIN_NODE
|
|
725
|
+
except (OSError, ValueError, subprocess.TimeoutExpired):
|
|
726
|
+
return False
|
|
727
|
+
|
|
728
|
+
|
|
729
|
+
def _record_design_slop(payload: dict) -> None:
|
|
730
|
+
"""Best-effort telemetry append; never raises into the gate."""
|
|
731
|
+
try:
|
|
732
|
+
target = _design_slop_telemetry_path()
|
|
733
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
734
|
+
with target.open("a", encoding="utf-8") as fh:
|
|
735
|
+
fh.write(json.dumps({"ts": time.time(), **payload}) + "\n")
|
|
736
|
+
except OSError:
|
|
737
|
+
pass
|
|
738
|
+
|
|
739
|
+
|
|
740
|
+
def _design_slop_verdict(
|
|
741
|
+
findings: list[dict], mode: str, n_files: int, command_str: str,
|
|
742
|
+
) -> CheckResult:
|
|
743
|
+
"""Turn parsed detector findings into the check verdict."""
|
|
744
|
+
warnings = [f for f in findings if f.get("severity") != "advisory"]
|
|
745
|
+
advisories = [f for f in findings if f.get("severity") == "advisory"]
|
|
746
|
+
top = "; ".join(
|
|
747
|
+
f"{f.get('file', '?')}:{f.get('line', '?')} {f.get('antipattern', '?')}"
|
|
748
|
+
for f in (warnings + advisories)[:10]
|
|
749
|
+
)
|
|
750
|
+
counts = (
|
|
751
|
+
f"{len(warnings)} warning(s), {len(advisories)} advisory finding(s)"
|
|
752
|
+
)
|
|
753
|
+
if warnings and mode == "hard":
|
|
754
|
+
summary = f"{counts} across {n_files} changed UI file(s): {top}"
|
|
755
|
+
passed = False
|
|
756
|
+
elif findings:
|
|
757
|
+
# Only warning-severity findings ever fail (and only in hard
|
|
758
|
+
# mode) — never promise a hard failure for advisory-only runs.
|
|
759
|
+
escalation = (
|
|
760
|
+
" — fails when governance.designSlop=hard" if warnings else ""
|
|
761
|
+
)
|
|
762
|
+
summary = (
|
|
763
|
+
f"ADVISORY: {counts} across {n_files} changed UI "
|
|
764
|
+
f"file(s){escalation}: {top}"
|
|
765
|
+
)
|
|
766
|
+
passed = True
|
|
767
|
+
else:
|
|
768
|
+
summary = f"0 findings across {n_files} changed UI file(s)"
|
|
769
|
+
passed = True
|
|
770
|
+
return CheckResult(
|
|
771
|
+
check="design-slop", ran=True, passed=passed,
|
|
772
|
+
command=command_str, exit_code=2 if findings else 0,
|
|
773
|
+
summary=summary[:_MAX_SUMMARY_CHARS],
|
|
774
|
+
)
|
|
775
|
+
|
|
776
|
+
|
|
777
|
+
def _check_design_slop(
|
|
778
|
+
project_dir: Path, changed: list[str] | None,
|
|
779
|
+
test_command: str | None, timeout: int,
|
|
780
|
+
) -> CheckResult:
|
|
781
|
+
"""Deterministic AI-design-slop detection over changed UI files.
|
|
782
|
+
|
|
783
|
+
Composition: ``frontend_gate`` is the pre-edit nudge (PreToolUse),
|
|
784
|
+
this check is the static deterministic half, ``ui-screenshot`` is
|
|
785
|
+
the pixel-artifact half, and Francisca supplies judgment. Fail-open
|
|
786
|
+
by design: a missing detector, an old node, a timeout or unreadable
|
|
787
|
+
output SKIP with a note — the gate never blocks on tooling absence
|
|
788
|
+
and never installs anything.
|
|
789
|
+
"""
|
|
790
|
+
mode = _design_slop_mode()
|
|
791
|
+
if mode == "off":
|
|
792
|
+
return _skip("design-slop", "disabled by governance.designSlop flag")
|
|
793
|
+
if not changed:
|
|
794
|
+
return _skip("design-slop", "no changed files provided")
|
|
795
|
+
try:
|
|
796
|
+
from core.workflow.frontend_gate import is_ui_file
|
|
797
|
+
except Exception:
|
|
798
|
+
return _skip("design-slop", "frontend_gate unavailable")
|
|
799
|
+
ui_changed = [
|
|
800
|
+
f for f in changed
|
|
801
|
+
if is_ui_file(f) and (project_dir / f).is_file()
|
|
802
|
+
]
|
|
803
|
+
if not ui_changed:
|
|
804
|
+
return _skip("design-slop", "no UI files changed")
|
|
805
|
+
detector = _resolve_detector(project_dir)
|
|
806
|
+
if detector is None:
|
|
807
|
+
return _skip(
|
|
808
|
+
"design-slop",
|
|
809
|
+
"impeccable CLI not installed (npm i -D impeccable, or re-run "
|
|
810
|
+
"the ArkaOS installer) — skipped",
|
|
811
|
+
)
|
|
812
|
+
if not _node_supports_detector():
|
|
813
|
+
return _skip("design-slop", "node >= 22.12 required by the detector")
|
|
814
|
+
argv = [*detector, "detect", *ui_changed, "--json"]
|
|
815
|
+
command_str = shlex.join(argv)
|
|
816
|
+
try:
|
|
817
|
+
proc = subprocess.run(
|
|
818
|
+
argv, cwd=project_dir, capture_output=True, text=True,
|
|
819
|
+
timeout=min(timeout, DESIGN_SLOP_TIMEOUT),
|
|
820
|
+
)
|
|
821
|
+
except subprocess.TimeoutExpired:
|
|
822
|
+
return CheckResult(
|
|
823
|
+
check="design-slop", ran=True, passed=None,
|
|
824
|
+
command=command_str, exit_code=None, summary="timeout",
|
|
825
|
+
)
|
|
826
|
+
except (OSError, FileNotFoundError):
|
|
827
|
+
return _skip("design-slop", "detector could not be executed")
|
|
828
|
+
if proc.returncode not in (0, 2):
|
|
829
|
+
tail = (proc.stderr or proc.stdout or "").strip()[-200:]
|
|
830
|
+
return _skip("design-slop", f"detector error (exit {proc.returncode}): {tail}")
|
|
831
|
+
try:
|
|
832
|
+
findings = json.loads(proc.stdout or "[]")
|
|
833
|
+
assert isinstance(findings, list)
|
|
834
|
+
except (json.JSONDecodeError, AssertionError):
|
|
835
|
+
return _skip("design-slop", "unparseable detector output — skipped")
|
|
836
|
+
result = _design_slop_verdict(findings, mode, len(ui_changed), command_str)
|
|
837
|
+
_record_design_slop({
|
|
838
|
+
"project_dir": str(project_dir), "mode": mode,
|
|
839
|
+
"ui_files": len(ui_changed),
|
|
840
|
+
"warnings": sum(1 for f in findings if f.get("severity") != "advisory"),
|
|
841
|
+
"advisories": sum(1 for f in findings if f.get("severity") == "advisory"),
|
|
842
|
+
"outcome": "fail" if result.passed is False else "pass",
|
|
843
|
+
})
|
|
844
|
+
return result
|
|
845
|
+
|
|
846
|
+
|
|
664
847
|
_CHECK_DISPATCH = {
|
|
665
848
|
"lint": _check_lint,
|
|
666
849
|
"typecheck": _check_typecheck,
|
|
@@ -669,6 +852,7 @@ _CHECK_DISPATCH = {
|
|
|
669
852
|
"security-grep": _check_security_grep,
|
|
670
853
|
"spellcheck": _check_spellcheck,
|
|
671
854
|
"ui-screenshot": _check_ui_screenshot,
|
|
855
|
+
"design-slop": _check_design_slop,
|
|
672
856
|
}
|
|
673
857
|
|
|
674
858
|
|
|
@@ -112,6 +112,12 @@ For every captured surface, annotate deviations with exact values
|
|
|
112
112
|
- **Benchmark contrast** — for each major finding, one line on what the
|
|
113
113
|
named benchmark does instead.
|
|
114
114
|
|
|
115
|
+
> The deterministic half of this review also runs mechanically as the
|
|
116
|
+
> QG evidence check `design-slop` (`core.governance.evidence_checks` —
|
|
117
|
+
> the external `impeccable` detector over changed UI files, fail-open);
|
|
118
|
+
> cite its finding `antipattern` ids in verdicts when the report
|
|
119
|
+
> carries them.
|
|
120
|
+
|
|
115
121
|
## Slop gates (pre-verdict — MANDATORY)
|
|
116
122
|
|
|
117
123
|
Before issuing any verdict, run the reviewed surfaces through
|
|
@@ -103,7 +103,10 @@ benchmark contrast, concrete fix.
|
|
|
103
103
|
— no memory-based auditing.
|
|
104
104
|
4. **Slop pass** — run the 58 gates (`slop-test.md`) and name every hit
|
|
105
105
|
from `anti-patterns.md`; check reflex-reject violations against the
|
|
106
|
-
squad reference §11 and design-law breaches against §12.
|
|
106
|
+
squad reference §11 and design-law breaches against §12. The
|
|
107
|
+
deterministic half runs mechanically as the QG check `design-slop`
|
|
108
|
+
(external `impeccable` detector, fail-open) — cite its `antipattern`
|
|
109
|
+
ids when its report is on record.
|
|
107
110
|
5. **Accessibility pass** — keyboard-only run, focus visibility,
|
|
108
111
|
contrast (AA), `prefers-reduced-motion`, semantic landmarks.
|
|
109
112
|
6. **Responsive pass** — 390 / 768 / 1440; layout integrity, target
|
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.31.0 — 89 agents, 17 departments, 329 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.31.0 — 89 agents, 17 departments, 329 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.31.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.31.0 — 89 agents, 17 departments, 329 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.31.0 — 89 agents, 17 departments, 329 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.31.0 — 89 agents, 17 departments, 329 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.
|
package/harness/zed/.rules
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# ArkaOS — The Operating System for AI Agent Teams
|
|
2
2
|
|
|
3
|
-
> v4.
|
|
3
|
+
> v4.31.0 — 89 agents, 17 departments, 329 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.
|
|
@@ -131,6 +131,37 @@ export function installMotionKit({ runtime = "claude-code", home = homedir() } =
|
|
|
131
131
|
return { action: "installed" };
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
+
function impeccableMarkerPath(home) {
|
|
135
|
+
return join(home, ".arkaos", ".impeccable-installed");
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function isImpeccableAvailable() {
|
|
139
|
+
const out = spawnSync("impeccable", ["--version"], {
|
|
140
|
+
timeout: 10_000, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8",
|
|
141
|
+
});
|
|
142
|
+
return !out.error && out.status === 0;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Install the impeccable design detector (the deterministic half of the
|
|
146
|
+
// design-slop Quality Gate check, core/governance/evidence_checks.py).
|
|
147
|
+
// Pinned major (^3.2) — the gate itself never installs (`npx
|
|
148
|
+
// --no-install`), so this installer step is the single supply-chain
|
|
149
|
+
// entry point. Runtime-agnostic (plain npm CLI), idempotent via marker
|
|
150
|
+
// + PATH probe, never-throws.
|
|
151
|
+
export function installImpeccableDetector({ home = homedir() } = {}) {
|
|
152
|
+
if (isImpeccableAvailable()) return { action: "already-present" };
|
|
153
|
+
if (existsSync(impeccableMarkerPath(home))) return { action: "already-present" };
|
|
154
|
+
const out = spawnSync("npm", ["install", "-g", "impeccable@^3.2"], {
|
|
155
|
+
timeout: 180_000, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8",
|
|
156
|
+
});
|
|
157
|
+
if (out.error || out.status !== 0) {
|
|
158
|
+
const reason = (out.stderr || out.error?.message || "unknown").trim().slice(0, 200);
|
|
159
|
+
return { action: "failed", reason };
|
|
160
|
+
}
|
|
161
|
+
try { writeFileSync(impeccableMarkerPath(home), new Date().toISOString()); } catch {}
|
|
162
|
+
return { action: "installed" };
|
|
163
|
+
}
|
|
164
|
+
|
|
134
165
|
// Orchestrate the full frontend tooling setup. Single entry point wired
|
|
135
166
|
// into both installer/index.js and installer/update.js.
|
|
136
167
|
export async function setupFrontendTooling({ runtime = "claude-code", home = homedir() } = {}) {
|
|
@@ -146,5 +177,10 @@ export async function setupFrontendTooling({ runtime = "claude-code", home = hom
|
|
|
146
177
|
} catch (err) {
|
|
147
178
|
results.motionKit = { action: "failed", reason: err.message };
|
|
148
179
|
}
|
|
180
|
+
try {
|
|
181
|
+
results.impeccableDetector = installImpeccableDetector({ home });
|
|
182
|
+
} catch (err) {
|
|
183
|
+
results.impeccableDetector = { action: "failed", reason: err.message };
|
|
184
|
+
}
|
|
149
185
|
return results;
|
|
150
186
|
}
|
package/package.json
CHANGED