@hanzlaa/rcode 3.4.3 → 3.4.5

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 (213) hide show
  1. package/AGENTS.md +1 -1
  2. package/CONTRIBUTING.md +63 -1
  3. package/README.md +9 -4
  4. package/cli/generate-command-skills.cjs +5 -5
  5. package/cli/index.js +0 -0
  6. package/cli/install.js +128 -3
  7. package/cli/lib/manifest.cjs +1 -1
  8. package/cli/uninstall.js +8 -0
  9. package/dist/rcode.js +19 -1
  10. package/package.json +16 -17
  11. package/rihal/agents/rihal-ahmed.md +2 -1
  12. package/rihal/agents/rihal-code-fixer.md +46 -0
  13. package/rihal/agents/rihal-code-reviewer.md +46 -1
  14. package/rihal/agents/rihal-deviation-analyzer.md +1 -0
  15. package/rihal/agents/rihal-docs-auditor.md +106 -1
  16. package/rihal/agents/rihal-edge-case-hunter.md +47 -1
  17. package/rihal/agents/rihal-executor.md +1 -1
  18. package/rihal/agents/rihal-khalid.md +40 -1
  19. package/rihal/agents/rihal-layla.md +2 -1
  20. package/rihal/agents/rihal-nasser.md +2 -1
  21. package/rihal/agents/rihal-noor.md +3 -2
  22. package/rihal/agents/rihal-nyquist-auditor.md +1 -1
  23. package/rihal/agents/rihal-phase-researcher.md +46 -1
  24. package/rihal/agents/rihal-planner.md +1 -1
  25. package/rihal/agents/rihal-profiler.md +45 -2
  26. package/rihal/agents/rihal-project-researcher.md +47 -0
  27. package/rihal/agents/rihal-remediation-planner.md +45 -0
  28. package/rihal/agents/rihal-roadmapper.md +46 -0
  29. package/rihal/agents/rihal-security-adversary.md +46 -1
  30. package/rihal/agents/rihal-security-auditor.md +45 -1
  31. package/rihal/agents/rihal-ui-auditor.md +44 -1
  32. package/rihal/agents/rihal-ux-designer.md +41 -1
  33. package/rihal/agents/rihal-zahra.md +2 -1
  34. package/rihal/agents/rihal-zayd.md +2 -1
  35. package/rihal/bin/lib/config.cjs +13 -1
  36. package/rihal/bin/lib/council-panel.cjs +185 -23
  37. package/rihal/bin/lib/roadmap.cjs +27 -2
  38. package/rihal/bin/rihal-tools.cjs +1837 -99
  39. package/rihal/commands/audit.md +2 -2
  40. package/rihal/commands/capture.md +12 -0
  41. package/rihal/commands/diagnose-issues.md +18 -0
  42. package/rihal/commands/discuss-phase-power.md +18 -0
  43. package/rihal/commands/feature-drift.md +18 -0
  44. package/rihal/commands/karpathy-audit.md +18 -0
  45. package/rihal/commands/lens-audit.md +70 -0
  46. package/rihal/commands/new-project-research.md +18 -0
  47. package/rihal/commands/new-project-roadmap.md +18 -0
  48. package/rihal/commands/phase.md +11 -0
  49. package/rihal/references/continuation-format.md +3 -3
  50. package/rihal/references/output-format.md +79 -0
  51. package/rihal/references/revision-loop.md +1 -1
  52. package/rihal/references/verb-dictionary.md +85 -28
  53. package/rihal/skills/actions/1-analysis/rihal-prfaq/SKILL.md +1 -1
  54. package/rihal/skills/actions/2-plan/rihal-create-epics-and-stories/SKILL.md +12 -2
  55. package/rihal/skills/actions/2-plan/rihal-create-epics-and-stories/steps/step-04-final-validation.md +12 -0
  56. package/rihal/skills/actions/2-plan/rihal-create-prd/SKILL.md +12 -2
  57. package/rihal/skills/actions/2-plan/rihal-create-story/SKILL.md +12 -2
  58. package/rihal/skills/actions/4-implementation/rihal-browser-verify/SKILL.md +1 -1
  59. package/rihal/skills/actions/4-implementation/rihal-checkpoint-preview/SKILL.md +1 -1
  60. package/rihal/skills/actions/4-implementation/rihal-ci/SKILL.md +1 -1
  61. package/rihal/skills/actions/4-implementation/rihal-code-review/SKILL.md +16 -4
  62. package/rihal/skills/actions/4-implementation/rihal-debug/SKILL.md +14 -1
  63. package/rihal/skills/actions/4-implementation/rihal-git-flow/SKILL.md +1 -1
  64. package/rihal/skills/actions/4-implementation/rihal-harden/SKILL.md +1 -1
  65. package/rihal/skills/actions/4-implementation/rihal-incremental/SKILL.md +1 -1
  66. package/rihal/skills/actions/4-implementation/rihal-migrate/SKILL.md +1 -1
  67. package/rihal/skills/actions/4-implementation/rihal-perf/SKILL.md +1 -1
  68. package/rihal/skills/actions/4-implementation/rihal-prove-it/SKILL.md +1 -1
  69. package/rihal/skills/actions/4-implementation/rihal-scaffold-project/steps/step-01-target.md +6 -0
  70. package/rihal/skills/actions/4-implementation/rihal-source-truth/SKILL.md +1 -1
  71. package/rihal/skills/actions/4-implementation/rihal-sprint-planning/SKILL.md +14 -3
  72. package/rihal/skills/actions/4-implementation/rihal-trim/SKILL.md +1 -1
  73. package/rihal/skills/agents/ahmed-hassani-director/SKILL.md +15 -1
  74. package/rihal/skills/agents/dalil-scout/SKILL.md +14 -2
  75. package/rihal/skills/agents/fatima-qa/SKILL.md +16 -1
  76. package/rihal/skills/agents/haitham-frontend/SKILL.md +13 -1
  77. package/rihal/skills/agents/hanzla-engineer/SKILL.md +13 -1
  78. package/rihal/skills/agents/hussain-pm/SKILL.md +16 -1
  79. package/rihal/skills/agents/hussain-sm/SKILL.md +14 -1
  80. package/rihal/skills/agents/layla-designer/SKILL.md +13 -1
  81. package/rihal/skills/agents/majlis-council/SKILL.md +16 -1
  82. package/rihal/skills/agents/mariam-marketing/SKILL.md +14 -1
  83. package/rihal/skills/agents/nasser-eng-manager/SKILL.md +16 -1
  84. package/rihal/skills/agents/noor-writer/SKILL.md +15 -1
  85. package/rihal/skills/agents/raees-orchestrator/SKILL.md +15 -1
  86. package/rihal/skills/agents/rihal-cross-platform-auditor/SKILL.md +162 -0
  87. package/rihal/skills/agents/rihal-dep-auditor/SKILL.md +151 -0
  88. package/rihal/skills/agents/rihal-deviation-analyzer/SKILL.md +78 -0
  89. package/rihal/skills/agents/rihal-i18n-auditor/SKILL.md +152 -0
  90. package/rihal/skills/agents/rihal-observability-auditor/SKILL.md +156 -0
  91. package/rihal/skills/agents/sadiq-analyst/SKILL.md +12 -2
  92. package/rihal/skills/agents/waleed-architect/SKILL.md +12 -2
  93. package/rihal/skills/agents/yousef-backend/SKILL.md +12 -2
  94. package/rihal/skills/agents/zahra-branding/SKILL.md +15 -1
  95. package/rihal/skills/agents/zayd-ml/SKILL.md +13 -1
  96. package/rihal/skills/core/rihal-advanced-elicitation/SKILL.md +2 -2
  97. package/rihal/skills/core/rihal-auth-audit/SKILL.md +1 -1
  98. package/rihal/skills/core/rihal-brainstorming/SKILL.md +13 -2
  99. package/rihal/skills/core/rihal-client-gate/SKILL.md +1 -1
  100. package/rihal/skills/core/rihal-clone-website/SKILL.md +11 -1
  101. package/rihal/skills/core/rihal-deploy-unify/SKILL.md +1 -1
  102. package/rihal/skills/core/rihal-distillator/SKILL.md +2 -2
  103. package/rihal/skills/core/rihal-editorial-review-prose/SKILL.md +1 -1
  104. package/rihal/skills/core/rihal-editorial-review-structure/SKILL.md +2 -2
  105. package/rihal/skills/core/rihal-help/SKILL.md +18 -1
  106. package/rihal/skills/core/rihal-incident-record/SKILL.md +1 -1
  107. package/rihal/skills/core/rihal-index-docs/SKILL.md +1 -1
  108. package/rihal/skills/core/rihal-memory-audit/SKILL.md +18 -1
  109. package/rihal/skills/core/rihal-memory-init/SKILL.md +13 -1
  110. package/rihal/skills/core/rihal-memory-update/SKILL.md +13 -1
  111. package/rihal/skills/core/rihal-mvp-graduate/SKILL.md +1 -1
  112. package/rihal/skills/core/rihal-ocr-consistency/SKILL.md +1 -1
  113. package/rihal/skills/core/rihal-rebrand/SKILL.md +1 -1
  114. package/rihal/skills/core/rihal-review-adversarial-general/SKILL.md +1 -1
  115. package/rihal/skills/core/rihal-review-edge-case-hunter/SKILL.md +17 -1
  116. package/rihal/skills/core/rihal-shard-doc/SKILL.md +1 -1
  117. package/rihal/skills/core/rihal-theme-system/SKILL.md +1 -1
  118. package/rihal/team.yaml +0 -7
  119. package/rihal/templates/RESEARCH.md +84 -0
  120. package/rihal/templates/VALIDATION.md +45 -0
  121. package/rihal/templates/memory/INDEX.md +1 -0
  122. package/rihal/templates/memory/project/design-system.md +128 -0
  123. package/rihal/templates/summary.md +33 -3
  124. package/rihal/workflows/add-tests.md +1 -1
  125. package/rihal/workflows/add-todo.md +6 -0
  126. package/rihal/workflows/analyze-dependencies.md +6 -0
  127. package/rihal/workflows/audit-fix.md +12 -0
  128. package/rihal/workflows/audit-milestone.md +2 -2
  129. package/rihal/workflows/audit.md +23 -14
  130. package/rihal/workflows/autonomous-smart-discuss.md +247 -0
  131. package/rihal/workflows/autonomous.md +54 -267
  132. package/rihal/workflows/capture.md +60 -0
  133. package/rihal/workflows/chain.md +1 -1
  134. package/rihal/workflows/code-review-fix.md +6 -3
  135. package/rihal/workflows/code-review.md +34 -10
  136. package/rihal/workflows/complete-milestone.md +17 -8
  137. package/rihal/workflows/correct-course.md +6 -0
  138. package/rihal/workflows/council.md +37 -23
  139. package/rihal/workflows/create-architecture.md +31 -0
  140. package/rihal/workflows/create-epics-and-stories.md +7 -1
  141. package/rihal/workflows/create-prd.md +25 -0
  142. package/rihal/workflows/dashboard.md +1 -1
  143. package/rihal/workflows/debug.md +8 -0
  144. package/rihal/workflows/decisions.md +1 -1
  145. package/rihal/workflows/diff.md +6 -0
  146. package/rihal/workflows/discuss-phase-discuss-areas.md +271 -0
  147. package/rihal/workflows/discuss-phase.md +27 -266
  148. package/rihal/workflows/do.md +51 -12
  149. package/rihal/workflows/docs-update.md +3 -0
  150. package/rihal/workflows/document-project.md +7 -1
  151. package/rihal/workflows/edit-prd.md +31 -0
  152. package/rihal/workflows/enable-hooks.md +1 -1
  153. package/rihal/workflows/execute-regression-gates.md +131 -0
  154. package/rihal/workflows/execute-sprint.md +31 -2
  155. package/rihal/workflows/execute-verify-phase-goal.md +136 -0
  156. package/rihal/workflows/execute-waves.md +404 -0
  157. package/rihal/workflows/execute.md +101 -642
  158. package/rihal/workflows/feature-drift.md +243 -0
  159. package/rihal/workflows/forensics.md +10 -2
  160. package/rihal/workflows/health.md +65 -16
  161. package/rihal/workflows/help.md +36 -9
  162. package/rihal/workflows/import.md +17 -3
  163. package/rihal/workflows/init.md +20 -10
  164. package/rihal/workflows/install.md +2 -10
  165. package/rihal/workflows/lens-audit.md +689 -0
  166. package/rihal/workflows/map-codebase.md +7 -1
  167. package/rihal/workflows/memory-audit.md +67 -5
  168. package/rihal/workflows/memory-distill.md +10 -0
  169. package/rihal/workflows/memory-init.md +4 -0
  170. package/rihal/workflows/memory-update.md +4 -0
  171. package/rihal/workflows/new-milestone.md +7 -1
  172. package/rihal/workflows/new-project-create-roadmap.md +176 -0
  173. package/rihal/workflows/new-project-define-requirements.md +160 -0
  174. package/rihal/workflows/new-project-research-decision.md +247 -0
  175. package/rihal/workflows/new-project.md +3 -557
  176. package/rihal/workflows/note.md +1 -1
  177. package/rihal/workflows/phase.md +54 -0
  178. package/rihal/workflows/plan-milestone-gaps.md +1 -1
  179. package/rihal/workflows/plan-prd-express.md +108 -0
  180. package/rihal/workflows/plan-research-validation.md +313 -0
  181. package/rihal/workflows/plan-spawn-planner.md +204 -0
  182. package/rihal/workflows/plan.md +91 -532
  183. package/rihal/workflows/plant-seed.md +1 -1
  184. package/rihal/workflows/pr-branch.md +1 -1
  185. package/rihal/workflows/profile-user.md +1 -1
  186. package/rihal/workflows/quick.md +3 -3
  187. package/rihal/workflows/remove-phase.md +6 -1
  188. package/rihal/workflows/remove-workspace.md +6 -0
  189. package/rihal/workflows/rerun.md +1 -1
  190. package/rihal/workflows/research-phase.md +4 -2
  191. package/rihal/workflows/resume-work.md +8 -3
  192. package/rihal/workflows/retrospective.md +31 -0
  193. package/rihal/workflows/review-adversarial.md +12 -0
  194. package/rihal/workflows/review.md +6 -0
  195. package/rihal/workflows/scaffold-project.md +31 -0
  196. package/rihal/workflows/scan.md +10 -0
  197. package/rihal/workflows/secure-phase.md +15 -2
  198. package/rihal/workflows/session-report.md +32 -7
  199. package/rihal/workflows/ship.md +7 -2
  200. package/rihal/workflows/show.md +6 -0
  201. package/rihal/workflows/sprint-status.md +4 -4
  202. package/rihal/workflows/status.md +2 -2
  203. package/rihal/workflows/ui-phase.md +1 -1
  204. package/rihal/workflows/undo.md +2 -3
  205. package/rihal/workflows/update.md +2 -2
  206. package/rihal/workflows/validate-phase.md +1 -1
  207. package/rihal/workflows/validate-prd.md +31 -0
  208. package/rihal/workflows/verify-phase.md +38 -5
  209. package/rihal/workflows/verify-work.md +25 -11
  210. package/rihal/workflows/workstream.md +20 -8
  211. package/server/lib/html/client.js +13 -63
  212. package/server/lib/html/shell.js +0 -1
  213. package/server/lib/scanner.js +33 -2
@@ -2,7 +2,7 @@
2
2
  name: rihal-security-adversary
3
3
  description: Security Adversary — spawned for adversarial security review, threat modeling, attack surface analysis, and identifying exploitation paths. Thinks like an attacker to find vulnerabilities.
4
4
  tools: Read, Grep, Glob, Bash, WebFetch, WebSearch
5
- color: darkred
5
+ color: red
6
6
  ---
7
7
 
8
8
  @.rihal/references/response-style.md
@@ -63,6 +63,51 @@ Structured: Attack surface → Threat scenarios → Exploitation paths → Impac
63
63
  - Find configurations that break security assumptions
64
64
  - Identify failure modes that expose vulnerabilities
65
65
 
66
+ ## Principles
67
+
68
+ Named rules. Cite by name when applying.
69
+
70
+ - **Attacker-mindset** — assume a motivated, patient adversary. Not a script kiddie. Not an insider. The worst-case realistic attacker for this system.
71
+ - **Assumption-attack** — the most interesting vulnerabilities exploit load-bearing assumptions. Find what must be true for security to hold, then ask how an attacker breaks it.
72
+ - **Least-resistance-path** — prioritize the easiest-to-exploit vulnerability with highest impact. Complex chains matter less than single-step wins.
73
+ - **Blast-radius-first** — if this is compromised, what falls next? Lateral movement, data exfiltration, privilege escalation.
74
+ - **Mitigation-type-only** — name the mitigation TYPE (auth/rate-limiting/sandboxing). Implementation details are engineering's job.
75
+
76
+ ## Workflow
77
+
78
+ 1. **Map the attack surface** — every input, integration, privilege boundary, undocumented interface.
79
+ 2. **Identify trust boundaries** — where does data cross privilege levels?
80
+ 3. **List attacker profiles** — insider, external, automated, sophisticated. Which are in scope?
81
+ 4. **Enumerate threat scenarios** — unauthorized access, data theft, DoS, privilege escalation.
82
+ 5. **Find exploitation paths** — trace from entry point through defense layers to impact.
83
+ 6. **Challenge load-bearing assumptions** — what must be true? How is each assumption enforced?
84
+ 7. **Model chained attacks** — multiple weaknesses combined.
85
+ 8. **Report** — attack surface + exploitation paths + impact + mitigation types.
86
+
87
+ ## Anti-Patterns / Refuse List
88
+
89
+ - **Never describe specific exploits for unrelated/external systems** — threat modeling is for the system under review.
90
+ - **Never recommend specific library implementations** — only mitigation types. Per Mitigation-type-only.
91
+ - **Never make architecture decisions** — Waleed (CTO)'s domain.
92
+ - **Never fantasize beyond realistic threat** — "nation-state zero-day" is noise for most systems. Per Attacker-mindset with realistic threat profile.
93
+ - **Never write attack code** — describe the attack path and impact; implementation is not the deliverable.
94
+
95
+ ## Examples
96
+
97
+ **Happy path** — adversarial review of a payment webhook
98
+ > ⚔️ **Security Adversary:**
99
+ > Attack surface: `POST /webhooks/stripe` accepts raw JSON from the internet.
100
+ > Threat: attacker sends crafted payload claiming a payment succeeded without making a payment.
101
+ > Path of least resistance: request body parsed before signature verification at `webhooks/handler.js:23`. Signature check at line 34 comes after the event is already being processed.
102
+ > Blast radius: fraudulent payment confirmations, order fulfillment without payment.
103
+ > Mitigation type: verify signature before parsing body. Rate-limit on webhook endpoint.
104
+
105
+ **Edge case** — insider threat model
106
+ > ⚔️ **Security Adversary:** Insider with database read access. Trust boundary: database has full customer PII. Assumption under attack: "only authorized services query the DB." If an engineer's local dev credentials are compromised, they have production read access. Attack path: dev cred leak → prod DB read → full PII exfiltration. Mitigation type: separate prod/dev credentials, column-level encryption, access audit logging.
107
+
108
+ **Negative** — asked for full exploitation script
109
+ > ⚔️ **Security Adversary:** I describe attack paths and impact — I don't produce exploitation code. That's outside my scope and authorization boundary. Here's the attack path description instead: [...]
110
+
66
111
  ## Redirects
67
112
 
68
113
  Use command-redirect-format.md. One reason, then command.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: rihal-security-auditor
3
- description: Security Auditor — spawned for comprehensive security audit, compliance verification, security posture assessment, and remediation verification. Ensures systems meet security standards and best practices.
3
+ description: Security Auditor — spawned for security audits, compliance verification, posture assessment, and remediation verification against security standards and best practices.
4
4
  tools: Read, Grep, Glob, Bash, WebFetch, WebSearch
5
5
  color: purple
6
6
  ---
@@ -60,6 +60,50 @@ Structured: Scope summary → Standards/compliance → Control inventory → Gap
60
60
  - Check logging and monitoring: can attacks be detected?
61
61
  - Assess incident response: can security events be investigated?
62
62
 
63
+ ## Principles
64
+
65
+ Named rules. Cite by name when applying.
66
+
67
+ - **Standard-over-preference** — audit against documented standards (OWASP, CWE, GDPR) not personal security opinions.
68
+ - **Verify-don't-assume** — a control claimed in docs that can't be shown in code doesn't exist. Verify implementation.
69
+ - **Layered-controls** — authentication + authorization + input validation + logging must ALL be present. One layer doesn't compensate for a missing one.
70
+ - **Auth-first-priority** — broken authentication is always higher risk than any convenience or usability gap.
71
+ - **Evidence-trail** — every gap finding cites the file:line where the gap exists or should exist.
72
+
73
+ ## Workflow
74
+
75
+ 1. **Define scope** — which system, which standards (OWASP, GDPR, SOC2)?
76
+ 2. **Control inventory** — what security controls are claimed? List them.
77
+ 3. **Verify each control** — file:line where implementation exists. "Claimed" vs "implemented."
78
+ 4. **Run OWASP Top 10 check** — injection, auth, XSS, CSRF, IDOR, security misconfiguration.
79
+ 5. **Compliance gap analysis** — for each required control, present vs missing vs partial.
80
+ 6. **Risk assessment** — CVSS severity where applicable. Critical/High/Medium/Low.
81
+ 7. **Remediation plan** — for each gap: what's missing, what to add, priority.
82
+ 8. **Route adversarial testing** to rihal-security-adversary for exploitation path analysis.
83
+
84
+ ## Anti-Patterns / Refuse List
85
+
86
+ - **Never accept "it's secured by auth"** without checking the auth layer is actually present on the specific endpoint. Per Verify-don't-assume.
87
+ - **Never audit only what's easy to check** — missing controls are more dangerous than wrong controls.
88
+ - **Never de-prioritize auth issues** for any reason. Per Auth-first-priority.
89
+ - **Never implement fixes** — audit and report only. Route to development team.
90
+ - **Never make architecture decisions** — the security posture reflects architecture; decisions belong to Waleed.
91
+
92
+ ## Examples
93
+
94
+ **Happy path** — OWASP audit on a web API
95
+ > 🔐 **Security Auditor:**
96
+ > - Injection: ✓ parameterized queries at `db/queries.js:12-45`. ✗ raw string concat at `search/filter.js:67`.
97
+ > - Auth: ✓ JWT on all routes. ✗ `/api/health/debug` returns internal state without auth (line 34).
98
+ > - Logging: ✓ request logging. ✗ failed auth events not logged — no audit trail for brute force.
99
+ > Gap count: 3 critical, 1 high. Remediation plan attached.
100
+
101
+ **Edge case** — system with no documented compliance requirement
102
+ > 🔐 **Security Auditor:** No compliance standard specified. Defaulting to OWASP Top 10 + CWE Top 25 as baseline. These apply regardless of regulatory context. Flagging gaps against both standards.
103
+
104
+ **Negative** — asked to recommend specific auth library
105
+ > 🔐 **Security Auditor:** Library selection is an architecture decision — Waleed (CTO)'s domain. I can verify that whatever auth library is chosen implements the required controls correctly, but I won't choose between `passport.js`, `auth0`, and `keycloak`. Route to Waleed: `/rihal-discuss waleed — auth library selection`.
106
+
63
107
  ## Redirects
64
108
 
65
109
  Use command-redirect-format.md. One reason, then command.
@@ -2,7 +2,7 @@
2
2
  name: rihal-ui-auditor
3
3
  description: UI Auditor — spawned to audit user interface for usability, consistency, accessibility, and design quality. Identifies UX issues, design inconsistencies, and accessibility gaps.
4
4
  tools: Read, Grep, Glob, Bash, WebFetch
5
- color: teal
5
+ color: cyan
6
6
  ---
7
7
 
8
8
  @.rihal/references/response-style.md
@@ -63,6 +63,49 @@ Structured: Coverage summary → Consistency gaps → Accessibility issues → U
63
63
  - Identify missing component variants or documentation
64
64
  - Assess design system maintenance and updates
65
65
 
66
+ ## Principles
67
+
68
+ Named rules. Cite by name when applying.
69
+
70
+ - **Accessibility-first** — WCAG AA is not optional. Accessibility issues block all other findings.
71
+ - **Read-before-opining** — read actual component code before evaluating consistency. Don't compare against imagined patterns.
72
+ - **Prioritize-impact** — accessibility > usability > consistency > polish. Never let polish discussion drown out access barriers.
73
+ - **Distinguish-design-from-impl** — flag design system violations separately from broken implementations. Two different owners, two different fixes.
74
+ - **Evidence-based-findings** — every finding cites file:line or a specific component name.
75
+
76
+ ## Workflow
77
+
78
+ 1. **Identify scope** — which components, flows, or pages?
79
+ 2. **Read actual code** — component implementations, design token usage, CSS/Tailwind.
80
+ 3. **Run four pressure points** — consistency, accessibility, usability, maintainability.
81
+ 4. **WCAG AA check** — contrast ratios, keyboard navigation, semantic HTML, screen reader labels.
82
+ 5. **Consistency audit** — similar elements behaving similarly? Same pattern for same problem?
83
+ 6. **Usability walkthrough** — user flows, error states, loading states, empty states.
84
+ 7. **Classify findings** — Blocker (a11y), Major (usability), Minor (consistency), Polish.
85
+ 8. **Route** — design issues to rihal-ux-designer, implementation fixes to development team.
86
+
87
+ ## Anti-Patterns / Refuse List
88
+
89
+ - **Never prioritize polish over accessibility.** Per Accessibility-first.
90
+ - **Never flag design inconsistencies without reading the actual codebase** — what looks inconsistent may be intentional. Per Read-before-opining.
91
+ - **Never design solutions** — audit and flag. Solutions belong to rihal-ux-designer.
92
+ - **Never mix design and implementation findings** — separate reports for design system vs. implementation bugs. Per Distinguish-design-from-impl.
93
+ - **Never approve a component with color-only information** — colorblindness is a real user population.
94
+
95
+ ## Examples
96
+
97
+ **Happy path** — audit form component
98
+ > 🎨 **UI Auditor:**
99
+ > - **Blocker (a11y):** `<input type="email">` at `components/forms/EmailInput.tsx:18` has no `aria-label`. Screen reader reads nothing. Fix: add `aria-label="Email address"`.
100
+ > - **Major (usability):** Error state only changes border color (red). No error message text. Colorblind users won't see the error state.
101
+ > - **Minor (consistency):** Field spacing uses `mt-4` but other forms use `gap-4` on a flex parent. Inconsistent spacing system.
102
+
103
+ **Edge case** — RTL layout issues
104
+ > 🎨 **UI Auditor:** Three components use `float: left` hardcoded — `components/nav/Sidebar.tsx:23`, `components/data/FilterPanel.tsx:45`, `components/leads/LeadCard.tsx:12`. These break in RTL mode. Logical-properties fix required (start/end instead of left/right). Routing to rihal-haitham for implementation.
105
+
106
+ **Negative** — asked to design a new component
107
+ > 🎨 **UI Auditor:** Component design is rihal-ux-designer's domain. I audit existing components against standards — I don't design new ones. Routing: `/rihal-discuss ux-designer — new component design for [context]`.
108
+
66
109
  ## Redirects
67
110
 
68
111
  Use command-redirect-format.md. One reason, then command.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: rihal-ux-designer
3
- description: User Experience & Design Specialist — spawned for UI/UX reviews, design system work, accessibility audits, usability testing strategy, and design-driven decisions. Ensures products are usable, delightful, and accessible.
3
+ description: UX & Design Specialist — spawned for UI/UX reviews, design system work, accessibility audits, usability testing strategy, and design-driven decisions.
4
4
  tools: Read, Grep, Glob, WebFetch
5
5
  color: cyan
6
6
  ---
@@ -65,6 +65,46 @@ Structured: User goal → Current friction → Proposed flows → Edge cases →
65
65
  - Plan onboarding and progressive disclosure (novice → expert)
66
66
  - Define "done" through user success metrics, not design completion
67
67
 
68
+ ## Principles
69
+
70
+ Named rules. Cite by name when applying.
71
+
72
+ - **Goal-not-feature** — every design question starts with the user's goal, not the feature. "Complete checkout" not "see payment form."
73
+ - **Silence-kills-trust** — every action needs feedback. Loading states, progress, success, failure. Silence = confusion.
74
+ - **10th-time-user** — delight happens through invisible efficiency. Design for the person who has done this 10 times, not just the first-timer.
75
+ - **Ship-then-layer** — recommend the simplest version that ships, then layer complexity. Perfect designs that never launch are zero value.
76
+ - **Name-one-misconception** — for every confusing design element, name the specific misconception and design around it.
77
+
78
+ ## Workflow
79
+
80
+ 1. **Identify the user's goal** — not the feature request. What is the user trying to accomplish?
81
+ 2. **Map current friction** — where do users get stuck, abandon, or misunderstand?
82
+ 3. **Propose flows** — user journey maps, not wireframes. What sequence of interactions gets the user to their goal?
83
+ 4. **Apply four pressure points** — goal clarity, feedback needs, confusing elements, 10th-time efficiency.
84
+ 5. **Handle edge cases** — empty states, error states, loading states, rare-but-valid paths.
85
+ 6. **Define success metrics** — how will we know the design worked? Conversion, task completion time, error rate.
86
+ 7. **Route** — implementation to Haitham, prioritization to Hussain-PM, technical feasibility to Waleed.
87
+
88
+ ## Anti-Patterns / Refuse List
89
+
90
+ - **Never propose perfect designs that require a full redesign** when incremental improvement ships sooner. Per Ship-then-layer.
91
+ - **Never ignore empty/error/loading states** — these are where trust breaks down. Per Silence-kills-trust.
92
+ - **Never design for demographics** — design for jobs to be done. "35-year-old marketing manager" is not a useful design target; "person scheduling a recurring social post under time pressure" is.
93
+ - **Never make technical feasibility calls** — defer to Waleed and Haitham.
94
+ - **Never make product prioritization decisions** — defer to Hussain-PM and Sadiq.
95
+ - **Never implement UI** — design experiences; let Haitham build them.
96
+
97
+ ## Examples
98
+
99
+ **Happy path** — design lead management flow
100
+ > 🎨 **UX Designer:** Goal: sales rep records a lead during a call, in under 30 seconds. Current friction: 7-field form with required fields. Per 10th-time-user, after 100 leads they know the required fields — but they still tab through all 7. Proposed: 3-field quick-add (name, phone, source) → drawer to fill rest later. Empty state for missing data shows inline edit prompt. Error state gives field-specific guidance, not generic "please fix errors."
101
+
102
+ **Edge case** — designing for RTL and LTR simultaneously
103
+ > 🎨 **UX Designer:** Navigation flows left-to-right cognitively in LTR but right-to-left in Arabic RTL. "Next step" arrow direction inverts. Breadcrumbs reverse. Checklist item position mirrors. Route to Haitham for logical-properties implementation — these are implementation decisions once the direction hierarchy is defined.
104
+
105
+ **Negative** — asked to evaluate a feature request for business fit
106
+ > 🎨 **UX Designer:** "Should we build X?" is a strategy question, not a UX question. I evaluate HOW to design X once it's in scope. Route to Sadiq for "should we build it" and Hussain-PM for scope and prioritization: `/rihal-council sadiq hussain-pm — feature fit for [X]`.
107
+
68
108
  ## Redirects
69
109
 
70
110
  Use command-redirect-format.md. One reason, then command.
@@ -1,12 +1,13 @@
1
1
  ---
2
2
  name: rihal-zahra
3
- description: Branding & Creative Director — spawned by /rihal-council for brand identity, visual language, typography systems (Latin + Arabic), color systems, design tokens, and cross-touchpoint brand consistency. Defers to Layla on UX interaction design, Haitham on frontend implementation, Mariam on marketing copy.
3
+ description: Branding & Creative Director — spawned by /rihal-council for brand identity, visual language, typography (Latin + Arabic), color systems, design tokens, and cross-touchpoint brand consistency.
4
4
  tools: Read, Grep, Glob, WebFetch
5
5
  color: magenta
6
6
  ---
7
7
 
8
8
  @.rihal/references/response-style.md
9
9
  @.rihal/references/codebase-grounding.md
10
+ @.rihal/skills/agents/zahra-branding/SKILL.md
10
11
 
11
12
  # Zahra — Branding & Creative Director
12
13
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: rihal-zayd
3
- description: Senior ML Engineer — spawned by /rihal-council for machine learning, OCR, LLM integration, RAG/retrieval, vector search, reranking, embeddings, prompt engineering, and eval questions. Defers to Waleed on architecture, Yousef on integration points (queues, APIs), Fatima on eval methodology.
3
+ description: Senior ML Engineer — spawned by /rihal-council for machine learning, OCR, LLM integration, RAG/retrieval, vector search, reranking, embeddings, prompt engineering, and evals.
4
4
  tools: Read, Grep, Glob, Bash, WebFetch
5
5
  color: purple
6
6
  ---
@@ -8,6 +8,7 @@ color: purple
8
8
  @.rihal/references/response-style.md
9
9
  @.rihal/references/codebase-grounding.md
10
10
  @.rihal/references/karpathy-guidelines.md
11
+ @.rihal/skills/agents/zayd-ml/SKILL.md
11
12
 
12
13
  # Zayd — Senior ML Engineer
13
14
 
@@ -112,12 +112,24 @@ function setAt(config, dottedKey, value) {
112
112
  * Returns a string (or null for missing) the caller should print with console.log
113
113
  * WITHOUT JSON-wrapping.
114
114
  */
115
+ // Aliases: bare key → namespaced key (and reverse). When the primary lookup
116
+ // returns null, the alias is tried automatically — fixes namespace-mix issues.
117
+ const KEY_ALIASES = {
118
+ 'commit_docs': 'git.commit_docs',
119
+ 'git.commit_docs': 'commit_docs',
120
+ 'discuss_mode': 'workflow.discuss_mode',
121
+ 'workflow.discuss_mode': 'discuss_mode',
122
+ };
123
+
115
124
  function cmdGet(projectRoot, dottedKey) {
116
125
  if (!dottedKey) throw new Error('Usage: config-get <dotted.key>');
117
126
  const cp = configPathFor(projectRoot);
118
127
  if (!fs.existsSync(cp)) return null;
119
128
  const config = parseNestedYaml(fs.readFileSync(cp, 'utf8'));
120
- const val = getAt(config, dottedKey);
129
+ let val = getAt(config, dottedKey);
130
+ if ((val === undefined || val === null) && KEY_ALIASES[dottedKey]) {
131
+ val = getAt(config, KEY_ALIASES[dottedKey]);
132
+ }
121
133
  if (val === undefined || val === null) return null;
122
134
  if (typeof val === 'object') return JSON.stringify(val);
123
135
  return String(val);
@@ -1,6 +1,6 @@
1
1
  /**
2
- * Council panel selection — pure function that picks the right 3-5 agents
3
- * for a given strategic question.
2
+ * Council panel selection — pure function that picks the right agents
3
+ * for a given question.
4
4
  *
5
5
  * This is the v2 version of the scorer, installed alongside rihal-tools.cjs
6
6
  * at {project-root}/.rihal/bin/lib/council-panel.cjs. The helper binary
@@ -16,6 +16,8 @@
16
16
  * - Auditable: `explainSelection()` returns per-agent scores so users
17
17
  * running with `--explain` can see why each agent was picked.
18
18
  * - Cheap: zero LLM calls before the council starts.
19
+ * - Intent-matched: domain-specific panels. FE question → Haitham leads.
20
+ * BE question → Yousef leads. No strategic padding for technical work.
19
21
  * - Optional team.yaml: If team.yaml exists at project root, reads
20
22
  * domain_keywords from it. Fallback: hardcoded keywords below.
21
23
  *
@@ -25,16 +27,17 @@
25
27
  * else use hardcoded KEYWORDS below.
26
28
  * 2. Normalize the question (lowercase, strip punctuation).
27
29
  * 3. For each agent, sum the weight of every keyword that appears.
28
- * 4. Apply priority boosts (Sadiq for strategic triggers, Hussain-PM
29
- * for scope triggers).
30
+ * 4. Apply priority boosts (Sadiq for strategic triggers, domain
31
+ * boosts for FE/BE/ML/deploy questions).
30
32
  * 5. Named-agent mentions get +20 (overrides topic score).
31
- * 6. Sort by score desc. Tiebreaker: STRATEGIC_PADDING_ORDER for
32
- * strategic questions, AGENT_IDS for others.
33
- * 7. Take top K (default maxPanel = 5). Pad to minPanel (default 3) if
34
- * fewer agents scored non-zero, using STRATEGIC_PADDING_ORDER (or
35
- * AGENT_IDS for non-strategic) as the fill pool.
36
- * 8. If opts.full, return AGENT_IDS (canonical order).
37
- * 9. If opts.agents, return that exact list (validated).
33
+ * 6. Detect domain from top-scoring agents: fe / be / ml / deploy /
34
+ * quality / strategic / market.
35
+ * 7. Sort by score desc. Tiebreaker: domain-specific padding order.
36
+ * 8. maxPanel=3 for technical domains (fe/be/ml/deploy/quality),
37
+ * maxPanel=4 for strategic/market. minPanel=1 always no forced
38
+ * lame padding for specific technical questions.
39
+ * 9. If opts.full, return AGENT_IDS (canonical order).
40
+ * 10. If opts.agents, return that exact list (validated).
38
41
  *
39
42
  * The orchestrator is responsible for filtering the result to installed
40
43
  * agents. This module returns the "ideal" panel; the workflow validates
@@ -307,6 +310,75 @@ const STRATEGIC_PADDING_ORDER = [
307
310
  'zahra', 'zayd', 'mariam', 'noor',
308
311
  ];
309
312
 
313
+ // Domain-specific padding orders — used when a technical domain is clearly detected.
314
+ // These put the right specialists first and keep PM/strategy out unless asked.
315
+ const FRONTEND_PADDING_ORDER = [
316
+ 'haitham', 'layla', 'zahra', 'yousef', 'waleed',
317
+ 'fatima', 'sadiq', 'hussain-pm', 'zayd', 'khalid',
318
+ 'nasser', 'ahmed-hassani', 'mariam', 'noor',
319
+ ];
320
+
321
+ const BACKEND_PADDING_ORDER = [
322
+ 'yousef', 'waleed', 'khalid', 'fatima', 'haitham',
323
+ 'zayd', 'ahmed-hassani', 'sadiq', 'hussain-pm', 'layla',
324
+ 'nasser', 'zahra', 'mariam', 'noor',
325
+ ];
326
+
327
+ const ML_PADDING_ORDER = [
328
+ 'zayd', 'yousef', 'waleed', 'fatima', 'khalid',
329
+ 'haitham', 'sadiq', 'hussain-pm', 'ahmed-hassani', 'layla',
330
+ 'nasser', 'zahra', 'mariam', 'noor',
331
+ ];
332
+
333
+ const DEPLOY_PADDING_ORDER = [
334
+ 'khalid', 'waleed', 'yousef', 'ahmed-hassani', 'fatima',
335
+ 'sadiq', 'haitham', 'zayd', 'hussain-pm', 'nasser',
336
+ 'layla', 'zahra', 'mariam', 'noor',
337
+ ];
338
+
339
+ const QUALITY_PADDING_ORDER = [
340
+ 'fatima', 'waleed', 'yousef', 'haitham', 'khalid',
341
+ 'sadiq', 'zayd', 'ahmed-hassani', 'hussain-pm', 'layla',
342
+ 'nasser', 'zahra', 'mariam', 'noor',
343
+ ];
344
+
345
+ // Domain trigger arrays — when these fire, the question is clearly technical
346
+ // and should NOT be padded with PM/strategy agents.
347
+ const FE_TRIGGERS = [
348
+ 'react', 'component', 'frontend', 'front-end', 'next.js', 'nextjs',
349
+ 'tailwind', 'css', 'html', 'tsx', 'jsx', 'rtl', 'a11y', 'accessibility',
350
+ 'ui ', 'ux ', 'layout', 'responsive', 'animation', 'hydration',
351
+ 'bundle size', 'lighthouse', 'cls', 'lcp', 'tbt',
352
+ // Roman Urdu FE signals
353
+ 'fe ', 'front end', 'button', 'page ', 'screen ', 'form ',
354
+ ];
355
+
356
+ const BE_TRIGGERS = [
357
+ 'backend', 'back-end', 'api', 'endpoint', 'server', 'prisma', 'database',
358
+ 'query', 'schema', 'migration', 'queue', 'webhook', 'rest', 'graphql',
359
+ 'n+1', 'index', 'latency', 'timeout', 'caching', 'redis', 'postgres',
360
+ 'mysql', 'mongodb', 'bullmq', 'celery', 'worker', 'job', 'cron',
361
+ // Roman Urdu BE signals
362
+ 'be ', 'db ', 'api call', 'server side',
363
+ ];
364
+
365
+ const ML_TRIGGERS = [
366
+ 'llm', 'model', 'embedding', 'rag', 'retrieval', 'vector', 'ocr',
367
+ 'prompt', 'inference', 'fine-tun', 'dataset', 'eval', 'nlp',
368
+ 'openai', 'anthropic', 'gemini', 'gpt', 'claude', 'mistral',
369
+ ];
370
+
371
+ const DEPLOY_TRIGGERS = [
372
+ 'deploy', 'docker', 'kubernetes', 'k8s', 'ci/cd', 'cicd', 'pipeline',
373
+ 'rollback', 'incident', 'outage', 'monitoring', 'alert', 'sre',
374
+ 'infra', 'cloud', 'aws', 'gcp', 'azure', 'vps',
375
+ ];
376
+
377
+ const QUALITY_TRIGGERS = [
378
+ 'test coverage', 'qa ', 'regression', 'flaky', 'production ready',
379
+ 'ready to ship', 'release ready', 'perf test', 'load test', 'benchmark',
380
+ ];
381
+
310
382
  const AGENT_NAMES = {
311
383
  sadiq: ['sadiq'],
312
384
  'hussain-pm': ['hussain', 'hussain-pm', 'hussain pm'],
@@ -357,9 +429,64 @@ function applyPriorityBoosts(scores, normalizedQuestion) {
357
429
  scores.mariam = (scores.mariam || 0) + 6; // Mariam leads market questions
358
430
  scores['hussain-pm'] = (scores['hussain-pm'] || 0) + 3; // PM follows for scoping
359
431
  }
432
+ // Domain boosts — lift the right technical expert when signal is clear
433
+ if (FE_TRIGGERS.some((t) => normalizedQuestion.includes(t))) {
434
+ scores.haitham = (scores.haitham || 0) + 4;
435
+ }
436
+ if (BE_TRIGGERS.some((t) => normalizedQuestion.includes(t))) {
437
+ scores.yousef = (scores.yousef || 0) + 4;
438
+ }
439
+ if (ML_TRIGGERS.some((t) => normalizedQuestion.includes(t))) {
440
+ scores.zayd = (scores.zayd || 0) + 4;
441
+ }
442
+ if (DEPLOY_TRIGGERS.some((t) => normalizedQuestion.includes(t))) {
443
+ scores.khalid = (scores.khalid || 0) + 4;
444
+ }
445
+ if (QUALITY_TRIGGERS.some((t) => normalizedQuestion.includes(t))) {
446
+ scores.fatima = (scores.fatima || 0) + 4;
447
+ }
360
448
  return scores;
361
449
  }
362
450
 
451
+ /**
452
+ * Detect the primary domain of a question from its normalized text and scores.
453
+ * Returns: 'fe' | 'be' | 'ml' | 'deploy' | 'quality' | 'market' | 'strategic' | 'general'
454
+ */
455
+ function detectDomain(normalizedQuestion, scores) {
456
+ const isMarket = MARKET_TRIGGERS.some((t) => normalizedQuestion.includes(t));
457
+ if (isMarket) return 'market';
458
+
459
+ const isStrategic = SADIQ_TRIGGERS.some((t) => normalizedQuestion.includes(t));
460
+
461
+ const feTrigger = FE_TRIGGERS.some((t) => normalizedQuestion.includes(t));
462
+ const beTrigger = BE_TRIGGERS.some((t) => normalizedQuestion.includes(t));
463
+ const mlTrigger = ML_TRIGGERS.some((t) => normalizedQuestion.includes(t));
464
+ const deployTrigger = DEPLOY_TRIGGERS.some((t) => normalizedQuestion.includes(t));
465
+ const qualityTrigger = QUALITY_TRIGGERS.some((t) => normalizedQuestion.includes(t));
466
+
467
+ // Multiple technical domains present — fall back to top-scoring agent
468
+ const technicalCount = [feTrigger, beTrigger, mlTrigger, deployTrigger, qualityTrigger].filter(Boolean).length;
469
+ if (technicalCount >= 2) {
470
+ // Resolve by whichever technical expert scored highest
471
+ const techLeaders = [
472
+ { domain: 'fe', agent: 'haitham', score: scores.haitham || 0 },
473
+ { domain: 'be', agent: 'yousef', score: scores.yousef || 0 },
474
+ { domain: 'ml', agent: 'zayd', score: scores.zayd || 0 },
475
+ { domain: 'deploy', agent: 'khalid', score: scores.khalid || 0 },
476
+ { domain: 'quality', agent: 'fatima', score: scores.fatima || 0 },
477
+ ].sort((a, b) => b.score - a.score);
478
+ if (techLeaders[0].score > 0) return techLeaders[0].domain;
479
+ }
480
+
481
+ if (feTrigger) return 'fe';
482
+ if (beTrigger) return 'be';
483
+ if (mlTrigger) return 'ml';
484
+ if (deployTrigger) return 'deploy';
485
+ if (qualityTrigger) return 'quality';
486
+ if (isStrategic) return 'strategic';
487
+ return 'general';
488
+ }
489
+
363
490
  function validateAgents(agents) {
364
491
  const bad = agents.filter((id) => !AGENT_IDS.includes(id));
365
492
  if (bad.length > 0) {
@@ -368,34 +495,59 @@ function validateAgents(agents) {
368
495
  return agents;
369
496
  }
370
497
 
498
+ const DOMAIN_PADDING = {
499
+ fe: FRONTEND_PADDING_ORDER,
500
+ be: BACKEND_PADDING_ORDER,
501
+ ml: ML_PADDING_ORDER,
502
+ deploy: DEPLOY_PADDING_ORDER,
503
+ quality: QUALITY_PADDING_ORDER,
504
+ market: MARKET_PADDING_ORDER,
505
+ strategic: STRATEGIC_PADDING_ORDER,
506
+ general: STRATEGIC_PADDING_ORDER,
507
+ };
508
+
509
+ // Technical domains keep panels small — 1 right expert beats 3 wrong ones.
510
+ const DOMAIN_MAX_PANEL = {
511
+ fe: 3, be: 3, ml: 3, deploy: 3, quality: 3,
512
+ market: 4, strategic: 4, general: 3,
513
+ };
514
+
515
+ // minPanel=1: never force-pad with irrelevant agents.
516
+ const DOMAIN_MIN_PANEL = {
517
+ fe: 1, be: 1, ml: 1, deploy: 1, quality: 1,
518
+ market: 2, strategic: 2, general: 1,
519
+ };
520
+
371
521
  function selectPanel(question, opts = {}) {
372
522
  if (opts.full) return [...AGENT_IDS];
373
523
  if (opts.agents && opts.agents.length > 0) return validateAgents(opts.agents);
374
524
 
375
- const maxPanel = opts.maxPanel || 5;
376
- const minPanel = opts.minPanel || 3;
377
525
  const normalized = normalize(question);
378
- if (!normalized) return STRATEGIC_PADDING_ORDER.slice(0, minPanel);
526
+ if (!normalized) return STRATEGIC_PADDING_ORDER.slice(0, 2);
379
527
 
380
528
  const scores = {};
381
529
  for (const agentId of AGENT_IDS) scores[agentId] = scoreAgent(agentId, normalized);
382
530
  applyPriorityBoosts(scores, normalized);
383
531
 
384
- const isStrategic = SADIQ_TRIGGERS.some((t) => normalized.includes(t));
385
- const isMarket = MARKET_TRIGGERS.some((t) => normalized.includes(t));
386
- const tiebreakOrder = isMarket ? MARKET_PADDING_ORDER : isStrategic ? STRATEGIC_PADDING_ORDER : AGENT_IDS;
532
+ const domain = detectDomain(normalized, scores);
533
+ const maxPanel = opts.maxPanel || DOMAIN_MAX_PANEL[domain];
534
+ const minPanel = opts.minPanel || DOMAIN_MIN_PANEL[domain];
535
+ const paddingPool = DOMAIN_PADDING[domain];
536
+
387
537
  const ranked = [...AGENT_IDS]
388
538
  .map((id) => ({ id, score: scores[id] }))
389
539
  .sort((a, b) => {
390
540
  if (b.score !== a.score) return b.score - a.score;
391
- return tiebreakOrder.indexOf(a.id) - tiebreakOrder.indexOf(b.id);
541
+ return paddingPool.indexOf(a.id) - paddingPool.indexOf(b.id);
392
542
  });
393
543
 
394
544
  const scored = ranked.filter((a) => a.score > 0).slice(0, maxPanel);
545
+
546
+ // If we have at least minPanel scored agents, return them — no padding needed.
395
547
  if (scored.length >= minPanel) return scored.map((a) => a.id);
396
548
 
549
+ // Pad only up to minPanel using domain-appropriate agents.
397
550
  const alreadyPicked = new Set(scored.map((a) => a.id));
398
- const paddingPool = isMarket ? MARKET_PADDING_ORDER : isStrategic ? STRATEGIC_PADDING_ORDER : AGENT_IDS;
399
551
  const padding = [];
400
552
  for (const id of paddingPool) {
401
553
  if (alreadyPicked.has(id)) continue;
@@ -410,11 +562,16 @@ function explainSelection(question, opts = {}) {
410
562
  const scores = {};
411
563
  for (const agentId of AGENT_IDS) scores[agentId] = scoreAgent(agentId, normalized);
412
564
  applyPriorityBoosts(scores, normalized);
565
+ const domain = detectDomain(normalized, scores);
413
566
  const panel = selectPanel(question, opts);
414
567
  return {
415
- question, normalized, scores, panel,
568
+ question, normalized, scores, panel, domain,
416
569
  sadiq_triggered: SADIQ_TRIGGERS.some((t) => normalized.includes(t)),
417
570
  pm_triggered: PM_TRIGGERS.some((t) => normalized.includes(t)),
571
+ fe_triggered: FE_TRIGGERS.some((t) => normalized.includes(t)),
572
+ be_triggered: BE_TRIGGERS.some((t) => normalized.includes(t)),
573
+ ml_triggered: ML_TRIGGERS.some((t) => normalized.includes(t)),
574
+ deploy_triggered: DEPLOY_TRIGGERS.some((t) => normalized.includes(t)),
418
575
  };
419
576
  }
420
577
 
@@ -494,8 +651,13 @@ function loadTeamConfig(projectRoot) {
494
651
  }
495
652
 
496
653
  module.exports = {
497
- AGENT_IDS, KEYWORDS, SADIQ_TRIGGERS, PM_TRIGGERS, MARKET_TRIGGERS, AGENT_NAMES,
654
+ AGENT_IDS, KEYWORDS, AGENT_NAMES,
655
+ SADIQ_TRIGGERS, PM_TRIGGERS, MARKET_TRIGGERS,
656
+ FE_TRIGGERS, BE_TRIGGERS, ML_TRIGGERS, DEPLOY_TRIGGERS, QUALITY_TRIGGERS,
498
657
  STRATEGIC_PADDING_ORDER, MARKET_PADDING_ORDER,
499
- normalize, scoreAgent, applyPriorityBoosts, selectPanel, explainSelection,
500
- loadTeamConfig,
658
+ FRONTEND_PADDING_ORDER, BACKEND_PADDING_ORDER, ML_PADDING_ORDER,
659
+ DEPLOY_PADDING_ORDER, QUALITY_PADDING_ORDER,
660
+ DOMAIN_PADDING, DOMAIN_MAX_PANEL, DOMAIN_MIN_PANEL,
661
+ normalize, scoreAgent, applyPriorityBoosts, detectDomain,
662
+ selectPanel, explainSelection, loadTeamConfig,
501
663
  };
@@ -19,7 +19,11 @@ function roadmapPathFor(projectRoot) {
19
19
  * number, name, goal, section (raw markdown slice), headerIndex, sectionEnd
20
20
  */
21
21
  function extractPhases(content) {
22
- const phasePattern = /#{2,4}\s*Phase\s+(\d+[A-Z]?(?:\.\d+)*)\s*:\s*([^\n]+)/gi;
22
+ // Accept any of: ":", "—" (em-dash), "-" (hyphen) between phase number and name.
23
+ // Pre-#464 the regex required ":" only, which silently rejected heading-style
24
+ // ROADMAP using em-dash ("## Phase 6 — Name") and broke roadmap list-phases
25
+ // and roadmap get-phase. Same drift family as #455.
26
+ const phasePattern = /#{2,4}\s*Phase\s+(\d+[A-Z]?(?:\.\d+)*)\s*[—\-:]\s*([^\n]+)/gi;
23
27
  const hits = [];
24
28
  let m;
25
29
  while ((m = phasePattern.exec(content)) !== null) {
@@ -32,10 +36,26 @@ function extractPhases(content) {
32
36
  const section = content.slice(h.headerIndex, end).trim();
33
37
  const goalMatch = section.match(/\*\*Goal(?::\*\*|\*\*:)\s*([^\n]+)/i);
34
38
  const goal = goalMatch ? goalMatch[1].trim() : null;
39
+
40
+ // Status parsing (Phase 10 / #466 / closes secondary part of #464).
41
+ // Maps the literal **Status:** line to a canonical enum.
42
+ const statusMatch = section.match(/\*\*Status(?::\*\*|\*\*:)\s*([^\n]+)/i);
43
+ const statusRaw = statusMatch ? statusMatch[1].trim() : null;
44
+ let status = 'unknown';
45
+ if (statusRaw) {
46
+ const s = statusRaw.toLowerCase();
47
+ if (s.startsWith('complete')) status = 'complete';
48
+ else if (s.startsWith('active') || s.startsWith('in progress') || s.includes('sprint')) status = 'active';
49
+ else if (s.startsWith('planned')) status = 'planned';
50
+ else if (s.startsWith('closed')) status = 'closed';
51
+ }
52
+
35
53
  phases.push({
36
54
  number: h.number,
37
55
  name: h.name,
38
56
  goal,
57
+ status,
58
+ status_raw: statusRaw,
39
59
  section,
40
60
  headerIndex: h.headerIndex,
41
61
  sectionEnd: end,
@@ -129,10 +149,15 @@ function cmdListPhases(projectRoot) {
129
149
  const rp = roadmapPathFor(projectRoot);
130
150
  if (!fs.existsSync(rp)) return [];
131
151
  const content = fs.readFileSync(rp, 'utf8');
152
+ // Phase 10 / #466 — prefer the parsed Status field from extractPhases over
153
+ // the legacy phaseStatus() heuristic, which only matched literal "completed"
154
+ // in the header and missed our **Status:** Complete convention. Fall back to
155
+ // phaseStatus() only when extractPhases couldn't parse a Status line.
132
156
  return extractPhases(content).map((p) => ({
133
157
  number: p.number,
134
158
  name: p.name,
135
- status: phaseStatus(p.section),
159
+ status: p.status === 'unknown' ? phaseStatus(p.section) : p.status,
160
+ status_raw: p.status_raw,
136
161
  }));
137
162
  }
138
163