arkaos 4.12.0 → 4.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +2 -2
  2. package/VERSION +1 -1
  3. package/arka/SKILL.md +1 -1
  4. package/arka/skills/flow/SKILL.md +11 -4
  5. package/config/claude-agents/content-researcher.md +51 -0
  6. package/config/claude-agents/frontend-dev.md +1 -1
  7. package/config/claude-agents/info-compiler.md +51 -0
  8. package/config/claude-agents/scriptwriter.md +9 -7
  9. package/config/claude-agents/trends-analyst.md +51 -0
  10. package/config/claude-agents/ux-designer.md +1 -1
  11. package/config/claude-agents/video-producer.md +51 -0
  12. package/config/claude-agents/visual-designer.md +1 -1
  13. package/config/constitution.yaml +1 -0
  14. package/config/mcp-policy.yaml +8 -0
  15. package/core/governance/__pycache__/evidence_checks.cpython-313.pyc +0 -0
  16. package/core/governance/__pycache__/phantom_action_check.cpython-313.pyc +0 -0
  17. package/core/governance/__pycache__/phantom_action_check.cpython-314.pyc +0 -0
  18. package/core/governance/evidence_checks.py +70 -0
  19. package/core/hooks/__pycache__/pre_tool_use.cpython-313.pyc +0 -0
  20. package/core/hooks/__pycache__/pre_tool_use.cpython-314.pyc +0 -0
  21. package/core/hooks/__pycache__/stop.cpython-313.pyc +0 -0
  22. package/core/hooks/__pycache__/stop.cpython-314.pyc +0 -0
  23. package/core/hooks/pre_tool_use.py +8 -2
  24. package/core/runtime/__pycache__/model_routing_context.cpython-313.pyc +0 -0
  25. package/core/runtime/model_routing_context.py +4 -0
  26. package/core/workflow/__pycache__/frontend_gate.cpython-313.pyc +0 -0
  27. package/core/workflow/__pycache__/frontend_gate_telemetry.cpython-313.pyc +0 -0
  28. package/core/workflow/__pycache__/frontend_gate_telemetry_cli.cpython-313.pyc +0 -0
  29. package/core/workflow/__pycache__/plan_approval.cpython-314.pyc +0 -0
  30. package/core/workflow/frontend_gate.py +118 -25
  31. package/core/workflow/frontend_gate_telemetry.py +129 -0
  32. package/core/workflow/frontend_gate_telemetry_cli.py +53 -0
  33. package/departments/brand/agents/ux-designer.yaml +1 -1
  34. package/departments/brand/agents/visual-designer.yaml +1 -1
  35. package/departments/brand/references/uiux-knowledge-and-tools.md +73 -0
  36. package/departments/brand/skills/colors/SKILL.md +76 -3
  37. package/departments/brand/skills/design-review/SKILL.md +81 -31
  38. package/departments/brand/skills/design-system/SKILL.md +32 -0
  39. package/departments/brand/skills/identity-system/SKILL.md +65 -4
  40. package/departments/brand/skills/logo-brief/SKILL.md +81 -3
  41. package/departments/brand/skills/mockup-generate/SKILL.md +82 -3
  42. package/departments/brand/skills/ux-audit/SKILL.md +74 -5
  43. package/departments/brand/skills/wireframe/SKILL.md +75 -3
  44. package/departments/content/SKILL.md +16 -2
  45. package/departments/content/agents/production/content-researcher.yaml +68 -0
  46. package/departments/content/agents/production/info-compiler.yaml +68 -0
  47. package/departments/content/agents/production/trends-analyst.yaml +69 -0
  48. package/departments/content/agents/production/video-producer.yaml +77 -0
  49. package/departments/content/agents/scriptwriter.yaml +29 -10
  50. package/departments/content/skills/research-compile/SKILL.md +75 -0
  51. package/departments/content/skills/short-form/SKILL.md +3 -1
  52. package/departments/content/skills/trend-hunt/SKILL.md +74 -0
  53. package/departments/content/skills/video-produce/SKILL.md +98 -0
  54. package/departments/content/skills/video-setup/SKILL.md +91 -0
  55. package/departments/content/squad.yaml +6 -0
  56. package/departments/content/workflows/shorts-social.yaml +83 -0
  57. package/departments/content/workflows/video-production.yaml +142 -0
  58. package/departments/content/workflows/youtube.yaml +3 -0
  59. package/departments/dev/agents/frontend-dev.yaml +1 -1
  60. package/installer/core-snapshot.js +44 -20
  61. package/installer/doctor.js +56 -1
  62. package/knowledge/agents-registry-v2.json +209 -14
  63. package/knowledge/commands-keywords.json +1388 -234
  64. package/knowledge/commands-registry.json +132 -3
  65. package/package.json +1 -1
  66. package/pyproject.toml +1 -1
@@ -3,17 +3,28 @@
3
3
  Constitution: ``excellence-mandate`` (NON-NEGOTIABLE, Excellence Reform
4
4
  2026-07-05). UI work must load the frontend design skills and judge
5
5
  against a named benchmark BEFORE code is written. This gate makes that
6
- duty mechanical: a Write/Edit/MultiEdit touching a UI file requires an
7
- ``[arka:design] <skills/benchmark>`` marker in the recent assistant
8
- messages — the same ceremony contract as ``[arka:routing]``
9
- (flow_enforcer) and ``[arka:dispatch]`` (specialist_enforcer).
6
+ duty mechanical: a Write/Edit/MultiEdit touching a UI file requires the
7
+ STRUCTURED design marker in the recent assistant messages:
8
+
9
+ [arka:design] benchmark=<Company> skills=<comma,list> tokens=<path|none>
10
+
11
+ (the same ceremony contract as ``[arka:routing]`` / ``[arka:dispatch]``,
12
+ upgraded in PR-D2 from the bare ``[arka:design] <anything>`` form — the
13
+ bare form is now a LEGACY marker: tolerated in warn mode with a nudge,
14
+ counted separately in telemetry, treated as missing in hard mode).
10
15
 
11
16
  Modes via ``hooks.frontendGate`` in ``~/.arkaos/config.json``:
12
17
 
13
18
  absent / "warn" → nudge on stderr, allow (rollout default)
14
- true / "hard" → deny UI edits without the marker
19
+ true / "hard" → deny UI edits without the structured marker
15
20
  false / "off" → gate disabled
16
21
 
22
+ Scope: component/stylesheet suffixes (now including ``.html``), plus a
23
+ WARN-only content heuristic for ``.ts/.js`` files that visibly carry UI
24
+ (className=/styled/@apply/tailwind/cva) — heuristic hits never deny,
25
+ even in hard mode, and carry their own ``ui_scope`` so the telemetry can
26
+ exclude them from the hard-flip decision if they prove FP-heavy.
27
+
17
28
  Bypasses: ``[arka:trivial] <reason>`` marker (same contract as the
18
29
  evidence flow — single-file edits under 10 lines) and
19
30
  ``ARKA_BYPASS_DESIGN=1`` env (logged for accountability).
@@ -40,18 +51,35 @@ TELEMETRY_PATH = Path.home() / ".arkaos" / "telemetry" / "frontend-gate.jsonl"
40
51
 
41
52
  ASSISTANT_WINDOW = 20
42
53
 
43
- DESIGN_RE = re.compile(r"\[arka:design\]\s*\S+", re.IGNORECASE)
54
+ # Any [arka:design] marker line; classification into structured vs legacy
55
+ # happens on the tail (order-insensitive key scan, see _classify_marker).
56
+ DESIGN_LINE_RE = re.compile(r"\[arka:design\]([^\n]*)", re.IGNORECASE)
57
+ _BENCHMARK_KEY_RE = re.compile(r"\bbenchmark=\S+", re.IGNORECASE)
58
+ _SKILLS_KEY_RE = re.compile(r"\bskills=\S+", re.IGNORECASE)
59
+
60
+ MARKER_TEMPLATE = (
61
+ "[arka:design] benchmark=<Company> skills=<comma,list> tokens=<path|none>"
62
+ )
44
63
 
45
64
  _GATED_TOOLS = frozenset({"Write", "Edit", "MultiEdit"})
46
65
 
47
- # Component + stylesheet extensions. Plain .ts/.js are excluded: they are
48
- # ambiguous (stores, utils, configs) and would flood the gate with false
49
- # positives the component file is where the visual decision lands.
66
+ # Component + stylesheet extensions. Plain .ts/.js stay out of the suffix
67
+ # set (stores, utils, configs would flood the gate) they are covered by
68
+ # the WARN-only content heuristic below when they visibly carry UI.
50
69
  UI_SUFFIXES = frozenset({
51
70
  ".vue", ".tsx", ".jsx", ".svelte", ".astro",
52
71
  ".css", ".scss", ".sass", ".less",
72
+ ".html", ".htm",
53
73
  })
54
74
 
75
+ _HEURISTIC_SUFFIXES = frozenset({".ts", ".js", ".mjs", ".cjs"})
76
+ _UI_CONTENT_RE = re.compile(
77
+ r"className=|styled\.|styled\(|@apply\b|\btailwind\b|cva\("
78
+ )
79
+ _UI_FILENAME_RE = re.compile(
80
+ r"(?:^|/)tailwind\.config\.[cm]?[tj]s$|\.styles?\.[tj]s$|(?:^|/)theme\.[tj]s$"
81
+ )
82
+
55
83
 
56
84
  @dataclass
57
85
  class Decision:
@@ -62,9 +90,19 @@ class Decision:
62
90
  mode: str = "warn"
63
91
  target_file: str = ""
64
92
  marker_found: str | None = None
93
+ marker_kind: str = "none" # structured | legacy | trivial | none
94
+ ui_scope: str = "suffix" # suffix | heuristic
65
95
 
66
96
  def to_stderr_message(self) -> str:
67
- if self.reason not in ("no-design-marker",):
97
+ if self.reason == "legacy-marker":
98
+ head = "[arka:suggest]" if self.allow else "[ARKA:DESIGN]"
99
+ return (
100
+ f"{head} Legacy design marker found ({self.marker_found}). "
101
+ f"The structured form is now required for hard-mode passage: "
102
+ f"`{MARKER_TEMPLATE}` — name the benchmark company and the "
103
+ f"skills actually loaded (or skills=degraded:<missing>)."
104
+ )
105
+ if self.reason != "no-design-marker":
68
106
  return ""
69
107
  head = "[arka:suggest]" if self.allow else "[ARKA:DESIGN]"
70
108
  verb = "should" if self.allow else "MUST"
@@ -74,7 +112,7 @@ class Decision:
74
112
  f"(frontend-design, ui-ux-pro-max, project design system) at "
75
113
  f"maximum effort and judge against a named benchmark FIRST "
76
114
  f"(constitution `excellence-mandate`). Emit "
77
- f"`[arka:design] <skills> benchmark=<name>` before UI edits, "
115
+ f"`{MARKER_TEMPLATE}` before UI edits, "
78
116
  f"or `[arka:trivial] <reason>` for sub-10-line fixes."
79
117
  )
80
118
 
@@ -100,13 +138,52 @@ def is_ui_file(file_path: str) -> bool:
100
138
  return Path(file_path).suffix.lower() in UI_SUFFIXES
101
139
 
102
140
 
103
- def _scan(messages: list[str]) -> str | None:
104
- """Return the matched evidence marker, or None."""
141
+ def _edit_payloads(tool_name: str, tool_input: dict) -> list[str]:
142
+ """The text this tool call is about to write, per tool shape."""
143
+ if tool_name == "Write":
144
+ return [str(tool_input.get("content", ""))]
145
+ if tool_name == "Edit":
146
+ return [str(tool_input.get("new_string", ""))]
147
+ if tool_name == "MultiEdit":
148
+ edits = tool_input.get("edits") or []
149
+ return [str(e.get("new_string", "")) for e in edits if isinstance(e, dict)]
150
+ return []
151
+
152
+
153
+ def is_heuristic_ui_file(
154
+ file_path: str, tool_name: str, tool_input: dict
155
+ ) -> bool:
156
+ """WARN-only UI detection for .ts/.js: filename or visible UI content."""
157
+ path = Path(file_path)
158
+ if path.suffix.lower() not in _HEURISTIC_SUFFIXES:
159
+ return False
160
+ if _UI_FILENAME_RE.search(file_path.replace("\\", "/")):
161
+ return True
162
+ return any(
163
+ _UI_CONTENT_RE.search(payload)
164
+ for payload in _edit_payloads(tool_name, tool_input)
165
+ )
166
+
167
+
168
+ def _classify_marker(messages: list[str]) -> tuple[str | None, str]:
169
+ """Return (matched_marker, kind) — kind: structured|legacy|trivial|none.
170
+
171
+ A structured marker anywhere in the window wins over legacy ones.
172
+ """
173
+ legacy: str | None = None
105
174
  for message in messages:
106
- match = DESIGN_RE.search(message) or TRIVIAL_RE.search(message)
107
- if match:
108
- return match.group(0)
109
- return None
175
+ for match in DESIGN_LINE_RE.finditer(message):
176
+ tail = match.group(1)
177
+ if _BENCHMARK_KEY_RE.search(tail) and _SKILLS_KEY_RE.search(tail):
178
+ return match.group(0).strip(), "structured"
179
+ if tail.strip() and legacy is None:
180
+ legacy = match.group(0).strip()
181
+ trivial = TRIVIAL_RE.search(message)
182
+ if trivial:
183
+ return trivial.group(0), "trivial"
184
+ if legacy is not None:
185
+ return legacy, "legacy"
186
+ return None, "none"
110
187
 
111
188
 
112
189
  def evaluate(
@@ -119,25 +196,41 @@ def evaluate(
119
196
  ) -> Decision:
120
197
  """Evaluate one tool call against the frontend excellence gate."""
121
198
  file_path = str(tool_input.get("file_path", ""))
122
- if tool_name not in _GATED_TOOLS or not is_ui_file(file_path):
199
+ if tool_name not in _GATED_TOOLS:
200
+ return Decision(allow=True, reason="not-ui-scope", target_file=file_path)
201
+ if is_ui_file(file_path):
202
+ ui_scope = "suffix"
203
+ elif is_heuristic_ui_file(file_path, tool_name, tool_input):
204
+ ui_scope = "heuristic"
205
+ else:
123
206
  return Decision(allow=True, reason="not-ui-scope", target_file=file_path)
124
207
  mode = _mode()
125
208
  if mode == "off":
126
209
  return Decision(allow=True, reason="flag-off", mode=mode,
127
- target_file=file_path)
210
+ target_file=file_path, ui_scope=ui_scope)
128
211
  if os.environ.get("ARKA_BYPASS_DESIGN") == "1":
129
212
  return Decision(allow=True, reason="env-bypass", mode=mode,
130
- target_file=file_path)
213
+ target_file=file_path, ui_scope=ui_scope)
131
214
  if messages is None:
132
215
  messages = _load_last_assistant_messages(
133
216
  transcript_path, ASSISTANT_WINDOW
134
217
  )
135
- marker = _scan(messages)
136
- if marker is not None:
218
+ marker, kind = _classify_marker(messages)
219
+ if kind in ("structured", "trivial"):
137
220
  return Decision(allow=True, reason="design-evidence", mode=mode,
138
- target_file=file_path, marker_found=marker)
139
- return Decision(allow=(mode != "hard"), reason="no-design-marker",
140
- mode=mode, target_file=file_path)
221
+ target_file=file_path, marker_found=marker,
222
+ marker_kind=kind, ui_scope=ui_scope)
223
+ # Heuristic scope is WARN-only by design: it never denies, even in
224
+ # hard mode — its telemetry (ui_scope=heuristic) informs whether it
225
+ # ever graduates to denying scope.
226
+ deniable = mode == "hard" and ui_scope == "suffix"
227
+ if kind == "legacy":
228
+ return Decision(allow=not deniable, reason="legacy-marker", mode=mode,
229
+ target_file=file_path, marker_found=marker,
230
+ marker_kind=kind, ui_scope=ui_scope)
231
+ return Decision(allow=not deniable, reason="no-design-marker",
232
+ mode=mode, target_file=file_path, marker_kind=kind,
233
+ ui_scope=ui_scope)
141
234
 
142
235
 
143
236
  def record_telemetry(session_id: str, tool: str, decision: Decision) -> None:
@@ -0,0 +1,129 @@
1
+ """Frontend-gate telemetry summarizer (Excellence Reform PR-D2).
2
+
3
+ Reads ``~/.arkaos/telemetry/frontend-gate.jsonl`` (the JSONL stream the
4
+ PreToolUse frontend gate appends on every gated decision) and produces
5
+ the evidence artifact for the WARN→hard flip decision: counts by
6
+ reason / marker_kind / mode / ui_scope, plus the would-have-denied rate
7
+ (suffix-scope events that hard mode would deny today).
8
+
9
+ Mirrors ``core.governance.specialist_telemetry`` so periods,
10
+ malformed-line tolerance, and zero-division safety behave the same way
11
+ across telemetry surfaces. Read-only.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import json
17
+ from collections import Counter
18
+ from dataclasses import dataclass, field
19
+ from datetime import datetime, timedelta, timezone
20
+ from pathlib import Path
21
+ from typing import Any
22
+
23
+ DEFAULT_PATH: Path = (
24
+ Path.home() / ".arkaos" / "telemetry" / "frontend-gate.jsonl"
25
+ )
26
+ _VALID_PERIODS: frozenset[str] = frozenset({"today", "week", "month", "all"})
27
+
28
+ # Reasons hard mode denies on suffix scope — the flip's FP surface.
29
+ _WOULD_DENY_REASONS = frozenset({"no-design-marker", "legacy-marker"})
30
+
31
+
32
+ @dataclass(frozen=True)
33
+ class FrontendGateSummary:
34
+ """Aggregated frontend-gate telemetry over a time slice."""
35
+
36
+ period: str
37
+ total_events: int
38
+ by_reason: list[tuple[str, int]] = field(default_factory=list)
39
+ by_marker_kind: list[tuple[str, int]] = field(default_factory=list)
40
+ by_mode: list[tuple[str, int]] = field(default_factory=list)
41
+ by_ui_scope: list[tuple[str, int]] = field(default_factory=list)
42
+ would_deny_events: int = 0
43
+ would_deny_rate: float = 0.0
44
+ corrupt_line_count: int = 0
45
+
46
+
47
+ def summarise(period: str, *, path: Path | None = None) -> FrontendGateSummary:
48
+ if period not in _VALID_PERIODS:
49
+ raise ValueError(f"invalid period: {period!r}")
50
+ src = path or DEFAULT_PATH
51
+ cutoff = _period_cutoff(period)
52
+ entries, corrupt = _read_jsonl(src, cutoff)
53
+ return _build_summary(period, entries, corrupt)
54
+
55
+
56
+ def _period_cutoff(period: str, now: datetime | None = None) -> datetime | None:
57
+ ref = now or datetime.now(timezone.utc)
58
+ if period == "today":
59
+ return ref.replace(hour=0, minute=0, second=0, microsecond=0)
60
+ if period == "week":
61
+ return ref - timedelta(days=7)
62
+ if period == "month":
63
+ return ref - timedelta(days=30)
64
+ return None
65
+
66
+
67
+ def _read_jsonl(
68
+ src: Path, cutoff: datetime | None
69
+ ) -> tuple[list[dict[str, Any]], int]:
70
+ if not src.exists():
71
+ return [], 0
72
+ entries: list[dict[str, Any]] = []
73
+ corrupt = 0
74
+ try:
75
+ with src.open("r", encoding="utf-8", errors="replace") as fh:
76
+ for line in fh:
77
+ if not line.strip():
78
+ continue
79
+ try:
80
+ entry = json.loads(line)
81
+ except json.JSONDecodeError:
82
+ corrupt += 1
83
+ continue
84
+ if cutoff is not None:
85
+ ts_raw = entry.get("ts", "")
86
+ try:
87
+ ts = datetime.fromisoformat(ts_raw)
88
+ except (TypeError, ValueError):
89
+ corrupt += 1
90
+ continue
91
+ if ts < cutoff:
92
+ continue
93
+ entries.append(entry)
94
+ except OSError:
95
+ return entries, corrupt
96
+ return entries, corrupt
97
+
98
+
99
+ def _build_summary(
100
+ period: str, entries: list[dict[str, Any]], corrupt: int
101
+ ) -> FrontendGateSummary:
102
+ # Only gated UI events matter for the flip; not-ui-scope is noise.
103
+ gated = [e for e in entries if e.get("reason") != "not-ui-scope"]
104
+ total = len(gated)
105
+ reason_counter: Counter[str] = Counter()
106
+ kind_counter: Counter[str] = Counter()
107
+ mode_counter: Counter[str] = Counter()
108
+ scope_counter: Counter[str] = Counter()
109
+ would_deny = 0
110
+ for e in gated:
111
+ reason = str(e.get("reason", "unknown"))
112
+ reason_counter[reason] += 1
113
+ kind_counter[str(e.get("marker_kind", "unknown"))] += 1
114
+ mode_counter[str(e.get("mode", "unknown"))] += 1
115
+ scope = str(e.get("ui_scope", "suffix"))
116
+ scope_counter[scope] += 1
117
+ if reason in _WOULD_DENY_REASONS and scope == "suffix":
118
+ would_deny += 1
119
+ return FrontendGateSummary(
120
+ period=period,
121
+ total_events=total,
122
+ by_reason=reason_counter.most_common(),
123
+ by_marker_kind=kind_counter.most_common(),
124
+ by_mode=mode_counter.most_common(),
125
+ by_ui_scope=scope_counter.most_common(),
126
+ would_deny_events=would_deny,
127
+ would_deny_rate=(would_deny / total) if total else 0.0,
128
+ corrupt_line_count=corrupt,
129
+ )
@@ -0,0 +1,53 @@
1
+ """CLI front-end for frontend-gate telemetry (WARN→hard flip evidence).
2
+
3
+ Usage:
4
+ python -m core.workflow.frontend_gate_telemetry_cli today
5
+ python -m core.workflow.frontend_gate_telemetry_cli week
6
+ python -m core.workflow.frontend_gate_telemetry_cli month
7
+ python -m core.workflow.frontend_gate_telemetry_cli all
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import sys
13
+
14
+ from core.workflow.frontend_gate_telemetry import summarise
15
+
16
+
17
+ def _format_human(summary) -> str:
18
+ pct = f"{summary.would_deny_rate * 100:.1f}%"
19
+ lines = [
20
+ f"Frontend Gate Telemetry — {summary.period}",
21
+ f" Gated UI events: {summary.total_events}",
22
+ f" Would-have-denied: {summary.would_deny_events} ({pct}) "
23
+ f"[suffix scope, hard-flip evidence]",
24
+ ]
25
+ for label, pairs in (
26
+ ("By reason", summary.by_reason),
27
+ ("By marker kind", summary.by_marker_kind),
28
+ ("By mode", summary.by_mode),
29
+ ("By UI scope", summary.by_ui_scope),
30
+ ):
31
+ if pairs:
32
+ lines.append(f" {label}:")
33
+ for key, count in pairs:
34
+ lines.append(f" - {key}: {count}")
35
+ if summary.corrupt_line_count:
36
+ lines.append(f" Corrupt lines: {summary.corrupt_line_count}")
37
+ return "\n".join(lines)
38
+
39
+
40
+ def main(argv: list[str] | None = None) -> int:
41
+ args = argv if argv is not None else sys.argv[1:]
42
+ period = args[0] if args else "today"
43
+ try:
44
+ summary = summarise(period)
45
+ except ValueError as exc:
46
+ print(f"error: {exc}", file=sys.stderr)
47
+ return 2
48
+ print(_format_human(summary))
49
+ return 0
50
+
51
+
52
+ if __name__ == "__main__": # pragma: no cover
53
+ sys.exit(main())
@@ -3,7 +3,7 @@ name: Sofia D.
3
3
  role: UX/UI Designer
4
4
  department: brand
5
5
  tier: 2
6
- model: sonnet
6
+ model: opus
7
7
 
8
8
  behavioral_dna:
9
9
  disc:
@@ -3,7 +3,7 @@ name: Isabel
3
3
  role: Visual Designer
4
4
  department: brand
5
5
  tier: 2
6
- model: sonnet
6
+ model: opus
7
7
 
8
8
  behavioral_dna:
9
9
  disc:
@@ -134,3 +134,76 @@ write material findings back to `[[Area 02 - Design]]`:
134
134
  - Microinteractions (trigger→rules→feedback→loops/modes)
135
135
  - Color theory & typography science (OKLCH, harmony, modular scale)
136
136
  - Gestalt & visual hierarchy (F/Z scanning, proximity, contrast)
137
+
138
+ ---
139
+
140
+ ## 8. Anti-Default Doctrine (NON-NEGOTIABLE — excellence-mandate)
141
+
142
+ AI-generated design clusters around three default looks. Producing any of
143
+ them WITHOUT a brief that explicitly asks for it is a constitution
144
+ violation (`excellence-mandate`: "no default-looking output"):
145
+
146
+ 1. **Cream + serif + terracotta** — warm cream background (≈ `#F4F1EA`),
147
+ high-contrast serif display, terracotta accent.
148
+ 2. **Dark + acid accent** — near-black background with a single bright
149
+ acid-green or vermilion accent.
150
+ 3. **Broadsheet** — hairline rules, zero border-radius, dense
151
+ newspaper-like columns.
152
+
153
+ These are defaults, not choices. Where the brief pins a direction, the
154
+ brief wins — including when it asks for one of these. Where an axis is
155
+ free, never spend it on a default.
156
+
157
+ **Token-plan-then-critique loop** (before writing any code or asset):
158
+
159
+ - **Color**: palette as 4–6 NAMED hex values (name = role, not "blue").
160
+ - **Type**: faces for 2+ roles — characterful display used with restraint,
161
+ complementary body, utility for captions/data when needed.
162
+ - **Layout**: one-sentence concept + ASCII wireframe to compare options.
163
+ - **Signature**: the ONE element this design will be remembered by.
164
+ Spend your boldness there; keep everything else quiet and disciplined.
165
+
166
+ Then critique the plan BEFORE building: *"would this exact design appear
167
+ for any other brief?"* If yes, revise and state what changed and why.
168
+ Quality floor, never announced: responsive to mobile, visible keyboard
169
+ focus, `prefers-reduced-motion` respected, WCAG AA contrast.
170
+
171
+ ## 9. Design Marker Contract (`[arka:design]`)
172
+
173
+ Every UI/design-producing turn MUST emit the structured marker on its own
174
+ line BEFORE the first file edit:
175
+
176
+ ```
177
+ [arka:design] benchmark=<Company> skills=<comma,list> tokens=<path|none>
178
+ ```
179
+
180
+ - `benchmark=` — named reference company (Linear, Stripe, Vercel, Notion,
181
+ Airbnb…). Pick it FIRST and state what its design lead would reject in
182
+ your plan.
183
+ - `skills=` — the design skills ACTUALLY loaded this session via the
184
+ Skill tool (e.g. `frontend-design,ui-ux-pro-max,gsap-core`). If a
185
+ plugin is not installed, be honest: `skills=degraded:<missing-name>`
186
+ and fall back to §3 + §8 of this reference. Never claim a load that
187
+ did not happen; never silently proceed as if it had.
188
+ - `tokens=` — path of the project design-system source
189
+ (`design-system.yaml`, tokens file), or `none` + one-line justification.
190
+
191
+ A bare `[arka:design] <token>` is a LEGACY marker: tolerated in WARN mode,
192
+ counted separately in telemetry, and treated as missing once the frontend
193
+ gate goes hard.
194
+
195
+ ## 10. Aesthetic Directions Catalog (optional, curated)
196
+
197
+ When a project has NO design system yet and the brief leaves the visual
198
+ axis free, seed a direction from the curated TypeUI catalog
199
+ (`npx typeui.sh pull <slug>` — MIT, bergside/awesome-design-skills):
200
+
201
+ `editorial` · `brutalism` · `neobrutalism` · `bento` · `expressive` ·
202
+ `dramatic` — anti-default directions; `shadcn` · `mono` — clean baselines.
203
+
204
+ Catalog tokens are a SEED for the project's own token system, never the
205
+ final design system. Motion is absent from the catalog — that is GSAP's
206
+ job: load `gsap-core` + `gsap-timeline` for any animation work,
207
+ `gsap-scrolltrigger`/`gsap-plugins` as the work demands, `gsap-react` on
208
+ React, and treat `gsap-performance` as the review bar
209
+ (`npx skills add https://github.com/greensock/gsap-skills` when absent).
@@ -20,11 +20,84 @@ allowed-tools: [Read, Write, Edit, Bash, Grep, Glob, Agent, WebFetch, WebSearch]
20
20
  # Colors — `/brand colors <mood>`
21
21
 
22
22
  > **Agent:** Isabel (Visual Designer) | **Framework:** Color Theory + Accessibility
23
+ > **Squad reference:** `departments/brand/references/uiux-knowledge-and-tools.md` (§3 tokens, §8 anti-default, §9 marker)
23
24
 
24
- ## What It Does
25
+ ## Load design intelligence (MANDATORY — excellence-mandate)
25
26
 
26
- Color palette design: primary, secondary, accent, neutrals with contrast ratios.
27
+ Do this BEFORE proposing a single hex value, in this order, and record
28
+ what actually loaded:
29
+
30
+ 1. **`Skill(frontend-design)`** — Anthropic's anti-default doctrine: the
31
+ three AI-default looks, token-plan-then-critique, signature discipline.
32
+ 2. **`Skill(ui-ux-pro-max)`** — curated design data: 161 palettes,
33
+ 57 font pairings, 99 UX guidelines; use its palette data as
34
+ comparative evidence, never as a menu to copy from.
35
+ 3. **Read the project design system** — `design-system.yaml`, token
36
+ files, or the brandbook; §3 of the squad reference is the in-repo
37
+ fallback when the Obsidian note is unreachable.
38
+
39
+ ### Graceful degradation (honest, never silent)
40
+
41
+ If a plugin skill is NOT installed: say so explicitly, fall back to §3 +
42
+ §8 of the squad reference, and emit the marker with
43
+ `skills=degraded:<missing-name>`. Never claim a load that did not
44
+ happen; never proceed as if it had.
45
+
46
+ ## Benchmark first
47
+
48
+ NAME the reference company (Linear, Stripe, Vercel, Notion, Airbnb…)
49
+ whose color craft this brand should be judged against, and write one
50
+ line on what that company's design lead would reject in your current
51
+ direction. No named benchmark, no palette.
52
+
53
+ ## Anti-default check
54
+
55
+ Before finalizing: which of the three AI-default looks (§8 — cream +
56
+ serif + terracotta; dark + acid accent; broadsheet) is this palette
57
+ drifting toward? Self-test: *"would this exact palette appear for any
58
+ other brief?"* If yes, revise and state what changed and why.
59
+
60
+ ## Design marker (before any file edit)
61
+
62
+ ```
63
+ [arka:design] benchmark=<Company> skills=<comma,list> tokens=<path|none>
64
+ ```
65
+
66
+ Emit on its own line BEFORE the first Write/Edit. Full contract: §9 of
67
+ the squad reference.
68
+
69
+ ## Workflow
70
+
71
+ 1. **KB + brief** — search the vault for existing brand/palette notes;
72
+ extract the mood words from the brief and the subject's own world
73
+ (its materials, environments, vernacular — that is where
74
+ non-default color comes from).
75
+ 2. **Anchor** — pick the anchor hue from the subject, not from fashion.
76
+ State the reasoning in one sentence.
77
+ 3. **Build the palette as 4–6 NAMED roles** (name = function, never
78
+ "blue"): `surface` (canvas), `ink` (text), `primary` (identity),
79
+ `accent` (signal, ≤ 10–15% usage), plus optional `positive`/`danger`
80
+ semantics. Derive tints/shades in OKLCH so lightness steps are
81
+ perceptually even; give each role a hex.
82
+ 4. **WCAG pairs table** — for every text-bearing combination, compute
83
+ the contrast ratio and mark AA/AAA for body and large text. Any pair
84
+ below AA is redesigned, not footnoted.
85
+ 5. **Dark-mode variants** — restate every role for dark surfaces (do
86
+ not naive-invert; re-pick lightness in OKLCH).
87
+ 6. **Usage guidelines** — the 60/25/15 composition ratio (§3), do/don't
88
+ examples, and which role carries the signature element.
89
+ 7. **Critique pass** — rerun the anti-default self-test against the
90
+ named benchmark before delivering.
27
91
 
28
92
  ## Output
29
93
 
30
- Color palette with hex codes, usage guidelines, and WCAG contrast checks
94
+ A palette block ready for token systems (DTCG-shaped: role hex +
95
+ OKLCH), the WCAG pairs table, dark-mode variants, and usage guidelines
96
+ — saved to Obsidian under the project's Brand path and referenced by
97
+ `tokens=` in the design marker of any implementation that follows.
98
+
99
+ | Deliverable | Format |
100
+ |---|---|
101
+ | Palette (4–6 named roles) | hex + OKLCH per role, light + dark |
102
+ | Contrast evidence | WCAG pairs table (AA/AAA per combination) |
103
+ | Usage guide | 60/25/15 ratio, do/don't, signature carrier |