` |
-
-## P2: Touch & Interaction (CRITICAL)
-
-| Rule | Standard | Avoid |
-|---|---|---|
-| `hover-states` | All interactive = visible hover feedback | Silent no-ops |
-| `cursor-pointer` | `cursor: pointer` on all clickable | Clickable divs without cursor |
-| `loading-feedback` | Spinner/skeleton ALL async > 300ms | Frozen UI |
-| `hover-vs-tap` | `@media (hover: hover)` for hover; click/tap for primary | Features only via hover |
-| `disabled-states` | `opacity: 0.5` + `pointer-events: none` + `aria-disabled` | Disabled still clickable |
-| `empty-states` | Headline + copy + CTA, every empty container | Blank screen |
-| `error-feedback` | Inline message + summary at top (identical wording) | "Something went wrong" |
-| `tap-feedback` | Visual feedback < 100ms of tap | No visual response |
-| `gesture-alternative` | Every gesture = visible button equivalent | Gesture-only actions |
-| `safe-area` | Primary targets away from notch, gesture bar, edges | Content under OS chrome |
-
-## P3: Layout & Responsive (HIGH)
-
-| Rule | Standard | Avoid |
-|---|---|---|
-| `mobile-first` | Mobile styles first; override with `min-width` | Desktop-first `max-width` |
-| `breakpoints` | 375 / 768 / 1024 / 1280 / 1440px | Arbitrary device breakpoints |
-| `content-max-width` | `max-width: 1280px` page; `65ch` prose | Full-width paragraphs on ultrawide |
-| `4px-grid` | ALL spacing = multiples of 4px | 11px, 17px, 23px |
-| `spacing-hierarchy` | Space within groups < space between groups | Same padding everywhere |
-| `z-index-scale` | dropdown(1000) sticky(1020) modal(1050) tooltip(1070) toast(1080) | `z-index: 9999` |
-| `aspect-ratio` | `aspect-ratio` or `width`+`height` on all images | Images without dimensions (CLS) |
-| `sticky-offset` | Fixed/sticky nav → `padding-top: nav-height` on body | Nav covering first section |
-| `viewport-units` | `min-h-dvh` not `100vh` mobile | `100vh` (broken iOS) |
-| `12-col-grid` | 12 col, 24px gutters desktop, 16px mobile, 48-64px margins | No grid system |
-| `no-horizontal-scroll` | Content fits viewport width | Horizontal scroll mobile |
-
-## P4: Typography (HIGH)
-
-| Rule | Standard | Avoid |
-|---|---|---|
-| `type-scale` | Ratio: 1.25 compact / 1.333 standard / 1.414 dramatic | Random sizes (17px, 23px) |
-| `body-fixed` | Body 16px fixed; NEVER fluid `clamp()` | `clamp()` on body (causes reflow) |
-| `heading-fluid` | Headings use `clamp()` | Fixed heading sizes |
-| `line-height-invert` | Display 1.05-1.15; Heading 1.2-1.3; Body 1.5 app / 1.6-1.75 prose | Same lh everywhere |
-| `tracking` | Headings -0.01 to -0.03em; Body 0; Overlines +0.06 to +0.10em | Negative tracking on body |
-| `weight-contrast` | Heading 600-800, body 400 | `font-weight: 500` everywhere |
-| `max-2-families` | Sans + mono for apps; serif + sans for editorial | 3+ families |
-| `prose-width` | `max-width: 65ch` on body text | Full-width text |
-| `fallback-stack` | `ui-sans-serif, system-ui, sans-serif` always | Missing fallbacks |
-| `readable-mobile` | 16px min body mobile (avoids iOS auto-zoom) | 14px body mobile |
-
-## P5: Color System (HIGH)
-
-| Rule | Standard | Avoid |
-|---|---|---|
-| `oklch` | OKLCH for all design tokens | Hex/HSL for design systems |
-| `no-pure-bw` | Near-black `oklch(0.12-0.15 0.005 H)` + near-white `oklch(0.97-0.99 0.005 H)` | Pure #000 on #FFF |
-| `warm-cool-commit` | Commit to warm OR cool neutrals; never mix | Warm bg + cool borders |
-| `semantic-tokens` | 3-layer: primitive ramps → semantic tokens → Tailwind bridge | Raw hex in components |
-| `dark-mode` | Redesign, don't invert. Warm charcoal bg. Lighter surfaces per elevation. | Inverting light mode |
-| `status-colors` | Success/error/warning/info each with solid + soft variant; L ~0.55 for AA | Same color for both variants |
-| `no-ai-purple` | Custom brand hue, not #6366F1 | AI purple as unconscious default |
-| `chart-tokens` | Dedicated `--chart-*` tokens separate from `--primary` | Reusing brand for charts |
-| `shadow-tint` | Warm-tinted oklch shadows, never `rgba(0,0,0,...)` | Cold black shadows |
-| `dark-elevation` | Progressively lighter bg per level | Box shadows in dark mode |
-| `color-dark-mode-test` | Test contrast separately in dark mode | One-mode testing |
-
-## P6: Motion & Animation (MEDIUM)
-
-| Rule | Standard | Avoid |
-|---|---|---|
-| `duration-scale` | 100-150ms hover; 200ms default; 300ms panel; 400-500ms complex | > 500ms UI transitions |
-| `exit-faster` | Exit 50-100ms shorter than enter | Same duration enter/exit |
-| `easing` | ease-out (enter), ease-in (exit), ease-in-out (reposition) | Linear easing |
-| `gpu-only` | Animate only `transform` and `opacity` | width/height/top/left |
-| `no-decorative` | Animation must serve information | `animate-bounce` on static icons |
-| `max-2-animated` | Max 2 animated elements per viewport | 6 elements animating on load |
-| `spring-context` | Spring/bounce only for playful/consumer | Bounce in enterprise |
-| `interruptible` | User gesture cancels in-progress animation | Blocked input during animation |
-| `stagger-sequence` | Stagger list entrance 30-50ms per item | All items appear simultaneously |
-
-## P7: Components (MEDIUM)
-
-| Rule | Standard | Avoid |
-|---|---|---|
-| `all-states` | Every component: default, hover, active, focus, disabled, loading | Missing states |
-| `button-loading` | Spinner replaces text, same width (no CLS) | Width changing |
-| `input-labels` | Visible persistent label above input | Placeholder-as-label |
-| `input-validation` | Validate on blur; real-time only when correcting | Validating on focus |
-| `password-toggle` | Show/hide with text label; never disable paste | Paste disabled |
-| `empty-state` | Headline + copy + single CTA | "No data" |
-| `icon-system` | SVG icons (Lucide/Heroicons/Phosphor), never emojis | Emojis as icons |
-| `confirmation` | Title = action question; buttons = specific verbs | "Are you sure?" / "OK" |
-| `error-messages` | What happened + why + how to fix; never blame user | "Error 403" |
-| `nav-active` | Current page visually indicated | No active indicator |
-| `consistent-radius` | Same radius per component type | Mixing 4px and 24px |
-| `elevation-consistent` | Consistent shadow/elevation scale | Random shadow values |
-
-## P8: UX Writing (MEDIUM)
-
-| Rule | Standard | Avoid |
-|---|---|---|
-| `button-labels` | Verb + object: "Download report", "Add to cart" | "Submit", "Click here", "OK" |
-| `first-person-cta` | "Start my trial" > "Start your trial" (+90% ContentVerve) | Second-person CTAs |
-| `single-cta` | One primary CTA per view (+266% conversions) | Multiple competing CTAs |
-| `plain-language` | Grade 4-6, max 25 words/sentence | "Purchase", "Commence" |
-| `sentence-case` | Sentence case headings | Title Case Everywhere |
-| `specific-headlines` | "3 items in your cart" | "Almost there!" |
-| `loading-copy` | < 2s spinner; 2-10s "Saving..."; 10s+ progressive | "Loading..." for everything |
-| `no-blame` | "That password doesn't match" | "You entered wrong password" |
-| `no-clear-on-error` | Preserve all user input on error | Clearing form on error |
-| `inclusive-language` | "select" not "click"; "they/their"; "primary/subordinate" | "click", "master/slave" |
-
-## P9: Landing Page Conversion (MEDIUM)
-
-| Rule | Evidence | Standard |
-|---|---|---|
-| `hero-3sec` | NNG: 57% viewing time above fold | Value prop in 3 seconds |
-| `false-floor` | NNG: 102% more views above fold | Bleed 40-80px of next section |
-| `section-order` | Unbounce 41K pages | Hero→Proof→Problem→Features→Testimonials→Pricing→FAQ→CTA→Footer |
-| `social-proof` | TrustRadius: 30-70% lift | Named metrics > logos > badges |
-| `pricing-3tier` | ProfitWell 12K SaaS | 3 tiers, highlight middle, expensive first |
-| `trust-signals` | 61% didn't buy without seals | Trust signal near every CTA |
-| `page-speed` | Google: 1s→3s = +32% bounce | Sub-2s; AVIF > WebP > JPEG |
-| `cta-above-fold` | HubSpot 40K pages: +30% | CTA above fold, repeated after sections |
-| `grade-5-7` | Unbounce: +56% vs grade 8-9 | Copy at grade 5-7 |
-| `form-fields` | Baymard: -4-6% per field beyond 8 | Max 7-8 fields |
-| `mobile-cta` | 82.9% mobile traffic | Sticky CTA bar, thumb-zone placement |
-
-## P10: Charts & Data (LOW)
-
-| Rule | Standard | Avoid |
-|---|---|---|
-| `right-align-numbers` | Right-align quantitative data in monospace | Left-aligned numbers |
-| `table-row-hover` | Subtle bg highlight on row hover | No row tracking |
-| `table-pagination` | Pagination > infinite scroll | Infinite scroll on tables |
-| `fixed-headers` | Freeze row/col headers during scroll | Scrolling headers |
-| `no-rainbow` | Viridis/Okabe-Ito; max 8 categories | Rainbow colormap |
-| `data-ink` | Erase elements without info loss (Tufte) | Gridlines dominating data |
-| `chart-a11y` | Color + pattern/shape; provide table alternative | Color-only encoding |
+Cross-reference your resolved specs against the hard constraints. Use `view_file` on `references/anti-patterns-checklist.md` and `references/website-experience-cheatsheet.md` if MCP fails.
+- **P1 Accessibility:** 4.5:1 contrast, 44px targets, `prefers-reduced-motion`.
+- **P2 Interaction:** Hover states on all interactive elements.
+- **P3 Responsive:** Mobile-first, 4px grid spacing, 65ch prose width limit.
+- **P4 Typography:** Fixed 16px body, fluid headings (`clamp`).
+- **P5 Color:** Use OKLCH. No pure pure #000/#FFF. Warm/cool neutral commitment.
---
-# PERSONALITY ATLAS
-
-> Source: 58 real company design systems. Design = communication before aesthetics.
-
-```
-Layer 1: Personality → what does product say about itself?
-Layer 2: Language → what visual vocabulary carries that?
-Layer 3: Rules → what constraints enforce it consistently?
-```
-
-## 1. Premium Precision
-**Communicates:** "Engineered by people who care about every pixel."
-**Exemplars:** Linear (tight type -0.03em, 3-variable color, opacity hierarchy), Vercel (black+white, Geist -0.04em, 96-128px sections), Apple (SF Pro 44pt targets, Clarity/Deference/Depth), Stripe (weight 300/500, CIELAB 5-step = 4.5:1)
-
-| Property | Value | Why |
-|---|---|---|
-| Colors | Mono + single accent | Multiple colors compete. One directs. |
-| Typography | -0.02 to -0.03em headings, weight 300-600 | Tight = deliberate. Low weight = restraint. |
-| Radius | 0-8px | Sharp = engineered. |
-| Shadows | None or ultra-subtle | Precision demands lightness. |
-| Motion | 200-250ms, ease-out, no bounce | Motion serves info, not personality. |
-| Density | Normal | Balanced. |
-| Default | Light | Content visibility. |
-
-**Use:** SaaS, dev tools (light), docs, B2B. **Never:** Children's, gaming, playful.
-
-## 2. Technical Developer
-**Communicates:** "Built by developers, for developers."
-**Exemplars:** Supabase (dark emerald, code-first), Warp (IDE-like, terminal IS product), Cursor (sleek dark, keyboard-first), Raycast (command palette is entire UX, every action < 100ms)
-
-| Property | Value | Why |
-|---|---|---|
-| Colors | Dark bg (oklch 0.08-0.14), green/cyan accents | Dark reduces strain. Green/cyan = terminal convention. |
-| Typography | Monospace for data, sans for UI chrome | Monospace = data. Sans = chrome. That IS hierarchy. |
-| Radius | 4-8px | Functional middle. |
-| Shadows | Glow on accent (0 0 20px oklch(accent / 0.15)) | Box shadows wrong on dark. Glow feels native. |
-| Motion | 100-200ms, snappy | Devs notice 50ms lag. |
-| Density | Compact (14px body, 48px sections, 20px cards) | Info density = feature. |
-| Default | Dark | 8+ hours in dark IDEs. |
-
-**Critical:** Command palette (Cmd+K) NOT optional for 50+ feature apps. Keyboard shortcuts on every action. Code blocks: syntax highlighting + copy button.
-
-**Use:** Dev tools, CLIs, APIs, editors. **Never:** Consumer, marketing, healthcare, government.
-
-## 3. Warm Editorial
-**Communicates:** "Reading this should feel like opening a well-made book."
-**Exemplars:** Notion (serif headings, cream bg, editor disappears), Airbnb (warm coral #FF5A5F, photography-driven, rounded-xl), Medium (serif body, 1.75lh)
-
-| Property | Value | Why |
-|---|---|---|
-| Colors | Warm-tinted: oklch(0.98 0.012 78) not oklch(0.98 0 0) | 0.012 chroma at hue 78 = "premium notebook" not "cold SaaS". Highest-leverage single decision. |
-| Typography | Serif or humanist sans, 18px body, 1.6-1.75 lh | Serif = trust. Larger body + generous leading = comfort. |
-| Radius | 12-20px | Rounded = friendly (Gestalt). Cap 20px - beyond = childish. |
-| Shadows | Warm-tinted (oklch(0.22 0.006 56 / 0.06)) | Cold rgba shadows disconnected on warm surfaces. |
-| Motion | 200-300ms, ease-in-out, gentle | Spring/bounce = tonally wrong. |
-| Density | Comfortable (96px sections, 40px cards, 18px body) | Reading requires breathing room. |
-| Default | Light | Paper metaphor. |
-
-**Use:** Content, editorial, consumer, hospitality. **Never:** Dev tools, data-dense, fintech.
-
-## 4. Bold Energetic
-**Communicates:** "We're confident and not afraid to stand out."
-**Exemplars:** Figma (multi-color, each feature = own color), Framer (bold black + blue, motion-first), PostHog (playful dark, hedgehog branding)
-
-| Property | Value | Why |
-|---|---|---|
-| Colors | 4-6 vivid, complementary/triadic, C 0.15+ | Energy requires contrast. Mono kills it. Cap 6 = chaos limit. |
-| Typography | Display 700-900, 32px+, -0.02 to -0.03em | Large heavy type demands attention. That's the point. |
-| Radius | 12-16px | Confident. Not sharp (cold) or super-round (childish). |
-| Shadows | Colored (0 8px 24px oklch(brand / 0.10)) | Colored shadows reference brand. More expressive. |
-| Motion | 200-400ms, spring (cubic-bezier(0.34, 1.56, 0.64, 1)) | Bold = bold motion. Under 400ms for UI. |
-| Default | Light | Bold colors need light contrast. Dark mutes vibrancy. |
+# PHASE 4: GENERATE DESIGN.md (Conditional)
-**Use:** Creative tools, startups, social, youth. **Never:** Banking, healthcare, government, B2B.
+Only run this phase when route classification says `requires_design_contract = true`.
-## 5. Cinematic Dark
-**Communicates:** "This is an experience, not a tool."
-**Exemplars:** ElevenLabs (audio waveform aesthetics define visual language), RunwayML (AI content IS hero, UI = invisible framing), SpaceX (full-bleed mission photography, futuristic through restraint)
-
-| Property | Value | Why |
-|---|---|---|
-| Colors | Near-black (oklch 0.05-0.10), single neon accent | True black = 60% OLED power savings at 100% brightness. Single accent = Von Restorff focal. |
-| Typography | Light weight 300-400 at large display, tight tracking | Light on dark = ethereal. Heavy on dark = oppressive. |
-| Radius | 0-6px | Sharp = futuristic. Rounded adds warmth that conflicts. |
-| Shadows | Glow (0 0 40px oklch(accent / 0.20)), no box-shadow | Shadows invisible on dark. Glow = native. |
-| Motion | 400-600ms cinematic, parallax, ease-in-out | Speed destroys cinematic feel. ONLY personality where 500ms+ OK for reveals. |
-| Density | Comfortable (120px sections) | Dense breaks immersive spell. Each section = "scene." |
-| Default | Dark | Personality IS darkness. Light mode may not be appropriate. |
-
-**Use:** Media, entertainment, gaming, music, AI creative. **Never:** Productivity, forms-heavy, government, healthcare.
-
-## 6. Enterprise Trust
-**Communicates:** "Your data is safe with us."
-**Exemplars:** IBM Carbon (every component ships WCAG 2.1 AA, 3:1 focus ring), Coinbase (institutional blue, says "bank" not "startup"), Salesforce (Lightning system, complexity through structure)
-
-| Property | Value | Why |
-|---|---|---|
-| Colors | Blue/navy (oklch 0.45-0.55 0.15-0.18 240-260), 1 brand + 1 accent | Blue = most cross-culturally consistent trust signal (Frontiers in Psychology, Goldstein 1942). |
-| Typography | Formal sans (IBM Plex, Inter), 400 body, 600 heading | No serif (editorial), no display weight (startup). Moderate = structured. |
-| Radius | 4-8px | Professional. 0px = brutalist. 12px+ = consumer. |
-| Motion | 150-200ms, ease-out | 8+ hour workday. Decorative motion = distraction. |
-| Default | Light | Institutional convention. |
-
-**Trust signals required:** SOC2/ISO badges, uptime guarantees, SSO/SAML, audit logs, RBAC visible.
-
-**Use:** B2B, fintech, healthcare management, gov tech. **Never:** Consumer, creative, gaming.
-
-## Choosing Personality
-
-Industry × user type × emotional target. Signals conflict → **industry wins** (harder constraint).
-
-| Emotional Target | Maps To |
-|---|---|
-| Trustworthy | enterprise-trust |
-| Playful | bold-energetic |
-| Premium | premium-precision |
-| Energetic | bold-energetic |
-| Calm | warm-editorial |
-| Technical | technical-developer |
-| Bold | bold-energetic |
-| Editorial | warm-editorial |
-
----
-
-# INDUSTRY RULES
-
-> Source: ui-ux-pro-max (161 rules), awesome-design-md (58 companies).
-
-| Industry | Style | Color | Must | Never | Notes |
-|---|---|---|---|---|---|
-| SaaS | Soft UI | Trust blue + accent | Hover transitions, empty states, onboarding checklist (3-5 items) | Excessive animation, AI purple, > 2 accents | |
-| Analytics/Dashboard | Minimalism | Neutral + Okabe-Ito/viridis | Data export, filtering, sortable tables, keyboard nav, monospace numbers, right-align quantities | Ornate design, rainbow colormaps | |
-| Healthcare | Soft UI | Calm blue + green, C < 0.12 | **WCAG AAA**, 48px+ targets, crisis resources | Neon, motion-heavy, < 14px text, gamification w/o clinical validation | HIPAA: no PHI in URLs, session timeouts |
-| Fintech | Minimalism | Navy + blue + gold | Security signals, trust badges near monetary actions, locale-aware number formatting, audit trail | Playful design, AI purple, unclear fees (FTC risk), animations on transactions | |
-| Creative Agency | Vibrant Block | Bold, high chroma | Case studies, full-bleed imagery, scroll animations, portfolio grid | Corporate minimalism, stock photos, template layouts | |
-| Developer Tool | Dark OLED | Dark + emerald/cyan | Code examples, keyboard shortcuts, Cmd+K, syntax highlighting, copy buttons | Heavy chrome, poor keyboard support, forced light default | Docs quality IS product quality |
-| AI/Chatbot | Minimalism | Neutral + one accent (NOT #6366F1) | Streaming text < 100ms first token, typing indicators, code blocks + copy | Heavy chrome, AI purple default, anthropomorphized persona | |
-| E-commerce (Luxury) | Minimalism | Black + gold | Product photography as hero, trust signals, size guides, guest checkout (26% abandon if forced account) | Block-based colorful, cheap discount badges | "complimentary shipping" not "FREE SHIPPING!!!" |
-| Government | Minimalism | High contrast, institutional | **WCAG AAA**, skip links, grade 4-6 language, breadcrumbs, multi-language, print-friendly | Ornate, motion, decorative images, jargon | |
-| Mental Health | Soft UI | Muted pastels, warm | Calm aesthetics, privacy-first, breathing room, crisis resources every screen | Neon, gamification, social pressure, dark default, aggressive notifications | |
-| Education | Soft UI / Clay | Friendly pastels | Progress indicators, achievement feedback, clear nav, full a11y | Complex layouts, dense tables, dark default, small text | |
-| Wellness | Soft UI | Earth tones, sage, coral | Calm, breathing space, warm photography, gentle CTAs | Dark default, aggressive motion, neon, dense layouts | |
-
-For full detail: `designer_get_industry_rules(industry)`
-
----
-
-# COGNITIVE LAWS (11)
-
-> Source: Laws of UX, NNG, Smashing Magazine. Not opinions - empirically documented.
-
-## Fitts' Law
-`T = a + b * log2(2D/W)` - Halving distance > doubling size.
-- Touch targets ≥ 44px (WCAG) / 48px (Material). Screen edges = infinite mouse targets, hardest for touch.
-- Submit CTA at bottom of form (pointer already near last field). Destructive actions ≥ 8px from safe.
-- **Violations:** Icon-only buttons (16px visual ≠ 44px target), Delete beside Save < 8px gap.
-
-## Hick's Law
-`RT = a + b * log2(n + 1)` - 2→4 choices costs more than 20→22. First added choices most expensive.
-- Minimize choices at irreversible points. Wizard pattern for multi-step. Surface recommended option.
-- Applies to decisions, not recognition. 15-item nav with clear categories = fine. 15-option modal = not.
-- **Violations:** All features on first login, pricing with no highlighted plan, 50+ unsorted dropdowns.
-
-## Miller's Law
-7 ±2 chunks (Cowan 2001: 4 ±1 without rehearsal). Unit = chunk, not item.
-- "(919) 555-2743" = 3 chunks vs "9195552743" = 10 items. 73% lower cognitive load.
-- Auto-chunk numbers, max 7 nav items, group forms ≤ 5 fields/section.
-- **Violations:** Flat 12+ item nav, unbroken reference codes, 20+ ungrouped settings.
-
-## Gestalt Principles
-**Proximity > Similarity > Closure > Continuity > Common Fate**
-- **Proximity:** Label-input gap 4-8px < input-to-next-label 16-24px. Equal spacing = invisible structure.
-- **Similarity:** All links share treatment. Primary ≠ secondary button weight.
-- **Closure:** Partial card at viewport bottom = scroll hook. Skeleton screens exploit this.
-- **Continuity:** Vertical form alignment. Zigzag layouts break scanning (NNG confirmed).
-- **Common Fate:** Skeleton placeholders pulse at identical timing.
-
-## Von Restorff Effect
-ONE highlighted element per view. Multiple highlights cancel.
-- One CTA. One recommended pricing plan. Red dot badge = only red in monochromatic UI.
-- **Violations:** Multiple "highlighted" CTAs, 6 elements animating on load.
-
-## Serial Position Effect
-First (primacy) and last (recency) remembered most. Middle forgotten.
-- Nav: important at edges. Pricing: recommended first or last. Onboarding: aha moment early, friction last.
-- Form fields: easy first (name, email), hard middle (tax ID).
-
-## F-Pattern (NNG: 232 users, 1.5M fixations)
-Dense text → horizontal top sweep → shorter lower sweep → vertical left scan. Right side = near-zero. 80% viewing time on left half.
-- Front-load sentences. "Billing settings" not "Settings for billing". H2/H3 every 200-300 words.
-
-## Z-Pattern
-Sparse visual pages (< 50 words). Logo top-left → trust top-right → diagonal → **CTA bottom-right** (terminal point).
-
-## Jakob's Law
-Users prefer sites to work like other sites. Radio = single, checkbox = multi, toggle = binary. Blue underline = link (-10-15% clicks without it). Cart top-right. Redesign: preview + opt-in + revert.
-
-## Doherty Threshold (IBM 1982)
-< 100ms instantaneous. 100-400ms immediate. **> 400ms flow breaks.** > 1s abandonment. > 10s gone.
-- Optimistic UI. Skeleton > spinner. Autocomplete < 300ms. Keystroke-to-display < 50ms.
-
-## Peak-End Rule (Kahneman 1993)
-Remembered = avg(peak intensity + final moment). Duration neglect.
-- Order confirmation = peak AND end (Mailchimp "High Five"). Onboarding completion = peak. Error recovery = peak management (Stripe specific messages). No-friction cancellation > dark-pattern 5-step.
-
-**Cross-law interactions:**
-- Hick + Serial Position on nav: fewer items + important at edges
-- Fitts + Von Restorff on CTAs: large + visually isolated = compound positive
-- Doherty + Peak-End: design loading reveal as positive peak
-- Hick vs disclosure: < 20% usage = hide; 80%+ = visible
-
----
-
-# VISUAL COMPOSITION
-
-> Source: NNG (1.5M fixations, 57,453 fold fixations), A List Apart, Smashing Magazine
-
-## Visual Hierarchy - 6 Levers (priority order)
-
-1. **Size** - Bigger = more important. Max 3 size variations. Never equal size for different priorities.
-2. **Contrast** - Squint test: blur 5-10px, primary action must be first visible.
-3. **Color** - Warm/saturated advances, cool/muted recedes. Red = destructive only.
-4. **Typography** - Weight is primary signal. Everything emphasized = nothing emphasized.
-5. **Spacing** - More surrounding space = more attention. Use before borders/fills.
-6. **Position** - Top-left = most attention. Optical center above mathematical center.
-
-## CRAP Principles
-- **Contrast:** If different, make VERY different. Same-same = no hierarchy.
-- **Repetition:** All links same treatment. Consistency = implicit rules.
-- **Alignment:** Left-align = simplest valid system. Never center multi-line paragraphs.
-- **Proximity:** Label-input 4-8px. Form groups 24-32px. Sections 48-96px. Same gap everywhere = invisible structure.
-
-## Whitespace
-**Micro** (tracking, leading, label-input): governs readability. **Macro** (sections, margins, hero): governs perceived value. Luxury = generous macro. Crowded = "affordable." Active whitespace = spotlight (Apple product isolation). Passive = structural.
-
-## The Fold (NNG: 57,453 fixations)
-- Above fold: **102% more views**. 57% viewing time above fold.
-- **Never fill exact viewport height** - bleed 40-80px of next section. False floor = users think page done.
-- First 100px must be relevant or users leave.
-
-## Reading Patterns
-- **F-Pattern:** Dense text. Right side = zero attention. Front-load sentences.
-- **Layer-Cake:** Most effective. Headings = summaries not labels.
-- **Z-Pattern:** Sparse visual < 50 words. CTA at terminal point (bottom-right).
-- **Spotted:** Specific targets (prices, dates). Distinct visual treatment.
-
-## Grids
-12-column: divides by 1/2/3/4/6/12. Desktop 24px gutters, mobile 16px, margins 48-64px / 20px.
-Baseline: 8px rhythm. Line heights = multiples (16, 24, 32, 40px).
-Full 12/12 (hero), wide 10/12, standard 8/12 (forms), narrow 6/12, sidebar 4/12.
-
-## Optical Alignment
-Mathematical ≠ optical. Icons in buttons: 1-2px up. Text in buttons: more bottom padding. Hero headlines: raise 5-8% above mathematical center. Circles must be physically larger than squares to appear equal.
-
----
-
-# UX WRITING
-
-> Source: Mailchimp, NNG, GOV.UK, Copyhackers, Microsoft.
-
-## Button Labels
-- "Start **my** trial" > "Start **your** trial" (+90% ContentVerve). First-person = endowment effect.
-- Verb + object: "Download report", "Add to cart". Single CTA per screen (+266%).
-- Front-load verb. "No credit card required" near CTA (+34%).
-- **Never:** "Click here", "Submit", "OK", "Yes/No", "Learn More" as primary.
-
-## Voice & Tone (Mailchimp)
-Voice (constant): plainspoken, genuine, dry humor. Tone (variable): adjust to reader's state. "Clear > clever, always." Active voice.
-
-## Plain Language (GOV.UK)
-Reading age 9. Max 25 words/sentence. buy not purchase, help not assist, start not commence. 80% prefer clear English. Block caps 13-18% harder to read.
-
-## Error Messages (NNG + GOV.UK + Stripe)
-**What happened + Why + How to fix.** Never blame user. Never clear input. Inline + summary (identical wording). Validate on blur.
-- Good: "That email is already registered. Try signing in."
-- Stripe: "Your bank declined. No payment was made. Try a different card."
-
-## Placeholder Text (NNG)
-**Never as label** (disappears on focus, memory burden). Acceptable: format hints only ("MM/DD/YYYY").
-
-## Confirmation Dialogs
-Title = action question ("Delete this project?"). Body = consequences ("12,847 subscribers. Cannot be undone."). Buttons = specific verbs ("Delete project" / "Keep project"). Destructive: red button, focus on Cancel, max distance between buttons.
-
-## Empty States
-Every empty state = onboarding moment. Headline + copy + CTA. First-use: encouraging. Cleared: congratulatory. No results: helpful path. Never blank.
-
-## Loading States
-< 2s: spinner only. 2-10s: "Saving..." 10s+: "Uploading (3 of 12)..." Present participle.
-
----
-
-# DESIGN MASTERS - 5 CONVERGENCE POINTS
-
-> 7 masters (Rams, Norman, Vignelli, Spiekermann, Ive, Tufte, Kare) converged independently.
-
-## Rams: 10 Principles
-Innovative (remove friction, not add spectacle), Useful (function + psychology + aesthetics), Aesthetic (integral to usefulness), Understandable (zero-onboarding), Unobtrusive (canvas disappears), Honest (no dark patterns), Long-lasting (avoids fashion), Thorough (404 pages, empty states = design surfaces), **As little design as possible.**
-
-## Norman: 6 Principles
-**Signifiers** (blue underline = link), **Mapping** (controls match results), **Feedback** (< 100ms, calibrated), **Constraints** (define what's impossible), **Conceptual Models** (user mental model must match design). Two Gulfs: Execution (can I do it?) and Evaluation (did it work?). Good design narrows both.
-
-## Vignelli: Grid + Type Discipline
-Max 2 type sizes per screen. 2x ratio minimum for hierarchy. "White space > black of type." Grid removes arbitrary placement.
-
-## Spiekermann: Type = Brand
-Custom typeface = purchased differentiation. Open apertures for screen legibility. Size and tracking are inverse.
-
-## Tufte: Data-Ink Ratio
-Maximize toward 1.0. "Can this be erased without info loss?" Lie Factor = effect shown / effect in data (1.0 = honest). Small multiples. Sparklines.
-
-## Ive/Apple: Simplicity = Purpose
-"Simplicity not absence of clutter - that's a consequence." Clarity (transparent carrier), Deference (UI steps back), Depth (spring physics = signifiers).
-
-## Kare: Icons as Universal Language
-Meaningful (real metaphors), Memorable (one-trial learning), Clear (traffic sign test).
-
-## The 5 Convergences
-
-| # | Principle | Implication |
-|---|---|---|
-| 1 | Reduction is hardest work | Every element earns place. Default = removal. |
-| 2 | Constraints enable, not limit | Type scale, spacing grid, palette = preconditions for quality. |
-| 3 | Timelessness over trend | Build for structure. Glassmorphism fades. Good typography permanent. |
-| 4 | Function precedes form, but form not optional | Poor hierarchy = harder to use, not just ugly. |
-| 5 | Design communicates trust | No blame, no manipulation, no lying with graphics. |
-
----
-
-# AI SLOP FINGERPRINT
-
-ANY present → go back to Phase 3:
-
-| # | Pattern | Fix |
-|---|---|---|
-| 1 | `#6366F1` gradient (AI purple) | Custom brand hue in OKLCH |
-| 2 | Grid of identical cards, identical padding | Vary sizes, bento/asymmetric |
-| 3 | `font-weight: 500` everywhere | 700+ heading, 400 body |
-| 4 | `#F9FAFB` background (cold grey) | oklch(0.98 0.008-0.015 60-80) |
-| 5 | Missing states | Design ALL states |
-| 6 | `animate-bounce`/`pulse` on static | Animation for loading/state changes only |
-| 7 | 3+ font families | Max 2 (sans + mono) |
-| 8 | Line-height 1.75 on app UI | 1.5 for app; 1.75 prose only |
-| 9 | Absolute/relative from Figma MCP | Solve via flex/grid |
-| 10 | `rgba(0,0,0,...)` shadows | oklch-tinted warm shadows |
-| 11 | Same color for UI AND charts | Separate `--chart-*` tokens |
-| 12 | No `prefers-reduced-motion` | Media query with `!important` |
-| 13 | Pure #000 on #FFF | Near-black on tinted white |
-| 14 | `outline: none` without replacement | 2px ring, 2px offset |
-| 15 | Touch targets < 44px | Pad to 44px minimum |
-
----
-
-# PHASE 4: GENERATE DESIGN.md
-
-Assemble all decisions into 10-section DESIGN.md. See [template](references/design-md-template.md). See [examples](examples/) for worked outputs.
-
-```
-1. Visual Theme & Atmosphere - emotional target, personality, system inspiration, identity
-2. Color Palette - brand ramp (OKLCH 11 stops), semantic tokens, dark mode strategy
-3. Typography - scale table, font pairing + rationale, fluid vs fixed
-4. Spacing - semantic tokens, density, grid (12-col), content max-width
-5. Component Specs - button/input/card/nav with ALL variants + ALL states
-6. Motion - duration scale, easing rules, prefers-reduced-motion strategy
-7. Elevation - shadow system (light), bg-color elevation (dark), z-index scale
-8. Do's and Don'ts - 10 rules for THIS product, each traced to evidence
-9. Responsive - behavior at 375/768/1024/1280/1440px
-10. Anti-Patterns - industry violations, AI slop checks this design passes
-```
-
-**Present to user. Wait for approval. No code until approved.**
+Assemble 10 sections. Wait for user approval. NO CODE YET.
+1. Visual Theme (Emotional target, personality).
+2. Color Palette (OKLCH, semantic, dark mode).
+3. Typography (Scale, pairing).
+4. Spacing (Grid, density, content width).
+5. Component Specs (Variants + states).
+6. Motion (Durations, easing, reduced-motion).
+7. Elevation (Shadow tokens, z-index).
+8. Do's & Don'ts (Industry-traced).
+9. Responsive (375/768/1024/1280/1440px targets).
+10. Anti-Patterns (Slop checks passed).
---
# PHASE 5: CODE GENERATION
-After DESIGN.md approved:
-
-1. CSS custom properties (`:root` tokens) → call `design_tokens_generate`
-2. Base layout → grid + spacing
-3. Typography → apply scale
-4. Components → ALL states (default, hover, active, focus, disabled, loading)
-5. Motion → after layout correct
-6. Responsive → mobile-first
-7. Accessibility → focus, ARIA, touch targets
-8. Final audit → anti-pattern checklist + pre-delivery checks
-
----
-
-# QUALITY GATES (5)
-
-| Gate | Test | Fail = |
-|---|---|---|
-| Personality | Emotional target in one sentence? | AI slop |
-| Industry | Follows `designer_get_industry_rules`? | Contextual failure |
-| Anti-Pattern | Zero matches from fingerprint? | Fix first |
-| Accessibility | Passes all P1 rules? | Not shippable |
-| DESIGN.md | Coherent DESIGN.md writable from output? | Generated, not designed |
-
----
-
-# PRE-DELIVERY CHECKLIST
-
-**Visual Quality**
-- [ ] No AI purple as default
-- [ ] Weight contrast (heading ≠ body)
-- [ ] Warm/cool neutrals committed
-- [ ] Near-black + near-white (not pure)
-- [ ] All spacing = 4px multiples
-- [ ] Consistent radius per component type
-
-**States**
-- [ ] Hover on all interactive
-- [ ] Focus ring (2px) on all interactive
-- [ ] Loading state for every async
-- [ ] Error state for every form input
-- [ ] Empty state for every data container
-- [ ] Disabled: opacity 0.5 + pointer-events none
-
-**Accessibility**
-- [ ] Body contrast ≥ 4.5:1 (both modes)
-- [ ] Touch targets ≥ 44px
-- [ ] prefers-reduced-motion implemented
-- [ ] No color-only indicators
-- [ ] All images have alt text
-- [ ] Skip links present
-- [ ] Semantic HTML (nav, main, button, a href)
-- [ ] Heading hierarchy sequential
-
-**Responsive**
-- [ ] Tested at 375, 768, 1024, 1440px
-- [ ] Content max-width constraint
-- [ ] No horizontal scroll mobile
-- [ ] Prose max-width: 65ch
-- [ ] dvh not vh for mobile full-height
-
-**Motion**
-- [ ] No > 500ms UI transitions
-- [ ] No linear easing
-- [ ] Only transform + opacity animated
-- [ ] No decorative bounce/pulse
-- [ ] Exits faster than entrances
-
-**Code**
-- [ ] No emojis as icons (SVG only)
-- [ ] cursor: pointer on clickable
-- [ ] No arbitrary z-index
-- [ ] Images have aspect-ratio or dimensions
-- [ ] No Figma absolute/relative dump
-- [ ] No form clearing on error
+Post-approval implementation order:
+1. CSS custom properties (`:root`) → `design_tokens_generate`.
+2. Base layout → grid + spacing.
+3. Typography.
+4. Components (ALL states).
+5. Motion (After layout).
+6. Responsive.
+7. Accessibility (Focus, ARIA).
+8. Final audit.
---
# INTEGRATION CONTRACTS
-## Upstream: How designer gets invoked
-
-**From `hyperstack:blueprint`:** visual/UX intent detected → input raw request + codebase context → return approved DESIGN.md path
-
-**From `hyperstack` root:** Phase 2 detects visual work → routes here before engineering-discipline for any visual task
-
-**From user direct:** "design", "build me a", "landing page", "DESIGN.md", any visual phrase → run full pipeline from Phase 1
-
-## Downstream: What designer hands off
-
-**To `hyperstack:forge-plan` (primary consumer):**
-After Phase 4 approved → file at `docs/DESIGN.md` or `
/DESIGN.md`
-
-forge-plan reads 10 sections → generates tasks:
-- S2 (Color) → task: `design_tokens_generate`
-- S3 (Typography) → task: font loading + type scale
-- S4 (Spacing) → task: configure Tailwind spacing tokens
-- S5 (Components) → tasks: one per component. If Q11b=shadcn: `shadcn_get_component` for each. If raw Tailwind: hand-write from DESIGN.md. If other library: use its docs.
-- S6 (Motion) → task: `motion_generate_animation` with DESIGN.md motion spec
-- S7 (Elevation) → task: define shadow tokens
-- S9 (Responsive) → tasks: breakpoint-specific overrides
-- S8 (Do's/Don'ts) → assertions embedded in every task's self-review
-
-Invocation: *"DESIGN.md approved and saved at ``. Invoking `hyperstack:forge-plan` with this as input spec."*
-
-**To `shadcn` MCP plugin - ONLY if Q11b=shadcn:**
-```
-shadcn_get_rules → architectural constraints (ALWAYS first)
-shadcn_get_composition(page_type) → which components compose for this page
-shadcn_list_components → available catalog
-for each component in DESIGN.md S5:
- shadcn_get_component(name) → full spec: primitive, data-slots, variants
- shadcn_get_snippet(name) → canonical usage example
-```
-No shadcn component matching DESIGN.md spec → escalate to `hyperstack:designer`, don't invent hybrid.
+**Upstream (`hyperstack:blueprint`):** New-surface or visual-semantic intent detected → run pipeline → return approved conditional `design_contract` / DESIGN.md path.
-**To raw Tailwind - ONLY if Q11b=raw Tailwind:**
-```
-design_tokens_get_category("component-sizing")
-ui_ux_get_component_pattern(name)
-```
-Hand-build from DESIGN.md S5 using Tailwind classes directly. Apply all P7 rules.
+**Downstream (`hyperstack:forge-plan`):**
+DESIGN.md saved → `forge-plan` reads 10 sections → generates tasks.
-**To `motion_generate_animation`:**
-```
-motion_generate_animation({
- description: "",
- durations: { fast: "150ms", normal: "200ms", slow: "300ms" },
- easing: "ease-out",
- prefersReducedMotion: true // always
-})
-```
-
-**To `design_tokens_generate`:**
-```
-design_tokens_generate({
- description: "",
- brand: ,
- neutral: ,
- typography: ,
- spacing: ,
-})
-```
-
-**To `hyperstack:behaviour-analysis`:**
-After implementation complete, before ship-gate. DESIGN.md = "expected behaviour" ground truth:
-- S5 → expected states per component
-- S6 → expected timing/easing
-- S8 → assertions to verify
-- S10 → violations to search for
+**To `shadcn` Plugin (If selected):**
+Call `shadcn_get_rules` FIRST. `shadcn_get_composition`, `shadcn_list_components`. `shadcn_get_component` per DESIGN.md. No hallucinated components.
**To `hyperstack:ship-gate`:**
-Before completion claim. Verifies:
-- All S5 components have ALL required states implemented
-- S10 anti-patterns absent from code
-- OKLCH tokens from S2 present in CSS
-- `prefers-reduced-motion` implemented (S6)
-
-## Reverse Escalation
-
-| Discovery | Source | Action |
-|---|---|---|
-| Visual gap mid-plan | forge-plan → designer | Append clarification to DESIGN.md, resume |
-| DESIGN.md expected behavior unclear/contradictory | behaviour-analysis → designer | User resolves, DESIGN.md updated |
-| DESIGN.md compliance fail not fixable in code | ship-gate → designer | Revise DESIGN.md framework constraints |
-
-## Announcement Protocol
+When present, DESIGN.md acts as ground truth for testing expected behaviours, states, animations, and non-slop compliance. When absent, verification falls back to workspace patterns plus route-level proof requirements.
-When invoked: *"Using hyperstack:designer - producing DESIGN.md contract for [task type]."*
-When handing off: *"DESIGN.md complete at [path]. Invoking hyperstack:forge-plan with this as input spec."*
-When escalating back: *"[from-skill] escalating to designer - [reason]."*
\ No newline at end of file
+**Announce invocation:** *"Using hyperstack:designer - stepping into Creative Director persona to produce DESIGN.md."*
diff --git a/skills/engineering-discipline/SKILL.md b/skills/engineering-discipline/SKILL.md
index 384984d..77fc4f3 100755
--- a/skills/engineering-discipline/SKILL.md
+++ b/skills/engineering-discipline/SKILL.md
@@ -51,17 +51,6 @@ references:
- **Quick Reference** → direct lookup of patterns, principles, naming conventions
- **Process Mode** → full 8-step workflow for complex/production features
-## The Iron Laws
-
-```
-1. NO REFACTOR WITHOUT TESTS FIRST
-2. NO PATTERN WITHOUT A NAMED FORCE
-3. NO SYNTAX BEFORE ARCHITECTURE
-4. NO ASSUMPTIONS WITHOUT DISCLOSURE
-5. NO "IT SHOULD WORK" - VERIFY IT DOES
-```
-
-Violating the letter = violating the spirit.
## Core Philosophy
@@ -146,33 +135,6 @@ Critical issue unaddressed → HARD STOP.
| Architecture decisions | `references/architecture/architecture-reasoning.md` |
| Standard response format | `references/architecture/output-format.md` |
-## Red Flags - STOP
-
-| Thought | Reality |
-|---|---|
-| "Quick fix, don't need the full 8-step framework" | Quick fixes break invariants when you skip Step 3. Do the framework. |
-| "I'll skip Step 8 Negative Doubt, I'm confident" | Confidence = #1 predictor of shipped bugs. Do the negative doubt. |
-| "I already know the responsibilities" | Write them down anyway. Writing forces clarity you thought you had. |
-| "Tests for a refactor are overkill" | Refactor without tests = random code change. Not negotiable. |
-| "I'll add tests after the refactor" | Write tests first, watch them pass, then refactor. |
-| "The pattern is obviously the right one" | Obvious patterns without named forces = cargo-culting. Name the force. |
-| "Small code, skip architecture reasoning" | Small code with wrong architecture compounds fast. |
-| "I'll assume the API is stable" | Never. State the assumption explicitly. |
-| "The 5-failure-mode exercise is busywork" | Most effective bug catcher in the framework. Do all 5. |
-| "I'll write tests that match the implementation" | Tests define behavior. Write them against the spec. |
-| "Refactoring doesn't change behavior, so tests are unchanged" | Write a test first that locks behavior. Then refactor. Then run. |
-| "I understand the invariants intuitively" | Write them down. Intuition drifts in 48 hours. |
-
-## Critical Reminders
-
-1. ⛔ No refactor without tests
-2. ⛔ No pattern without named force
-3. ⛔ No circular dependencies
-4. ⛔ No assumptions without disclosure
-5. ⛔ No global state without justification
-6. ⛔ No proceeding with ambiguous requirements
-
-Something cannot be done safely → say so and explain why.
## Lifecycle Integration
diff --git a/skills/forge-plan/SKILL.md b/skills/forge-plan/SKILL.md
index a08511e..99a5da2 100644
--- a/skills/forge-plan/SKILL.md
+++ b/skills/forge-plan/SKILL.md
@@ -1,241 +1,163 @@
---
name: forge-plan
category: core
-description: Use after blueprint design approval to produce a task-by-task implementation plan grounded in MCP-verified API calls. No placeholders, no assumed syntax.
+description: Executed after blueprint/designer approval. Produces task-by-task implementation plan grounded in MCP-verified APIs. Zero placeholders.
---
# Forge Plan
## Input
-Requires a completed, user-approved design from one of:
+Requires approved design:
+1. **`hyperstack:blueprint`** → Backend/infra/architecture. Input: architecture note.
+2. **`hyperstack:designer`** → Visual/UX. Input: 10-section `DESIGN.md`.
-1. **`hyperstack:blueprint`** → backend/infra/architecture work. Design = architecture note.
-2. **`hyperstack:designer`** → visual/UX work. Design = structured `DESIGN.md` with 10 sections.
+No design present:
+- Visual/UX task → STOP. Invoke `designer`.
+- Backend/infra task → STOP. Invoke `blueprint`.
-No approved design:
-- Visual/UX task → stop, invoke `hyperstack:designer`
-- Backend/infra task → stop, invoke `hyperstack:blueprint`
+## DESIGN.md Ingestion (Visual/UX)
-## DESIGN.md Ingestion (visual/UX work)
-
-Parse DESIGN.md and map sections to task categories:
+Parse DESIGN.md into task categories:
| DESIGN.md Section | Task Category | MCP Calls |
|---|---|---|
-| 1. Visual Theme | (context only - used in all tasks) | `designer_get_personality` |
-| 2. Color Palette | Token setup tasks | `design_tokens_generate` with OKLCH values |
-| 3. Typography | Font loading + type scale tasks | `design_tokens_get_category("typography")` |
-| 4. Spacing | Tailwind spacing config | `design_tokens_get_category("spacing")` |
-| 5. Component Specs | One task per component | shadcn → `shadcn_get_rules` + `shadcn_get_composition` + `shadcn_get_component`. Raw Tailwind → `ui_ux_get_component_pattern` + hand-build. Other library → use that library's own docs. |
-| 6. Motion | Animation tasks | `motion_generate_animation` with DESIGN.md motion spec |
-| 7. Elevation | Shadow token tasks | `design_tokens_get_category("shadows")` |
-| 8. Do's and Don'ts | Embedded as self-review assertions in every task |
-| 9. Responsive Breakpoints | Breakpoint-specific override tasks | `ui_ux_get_principle("responsive")` |
-| 10. Anti-Patterns | Embedded as self-review assertions - each task must verify none present |
-
-Every task's self-review step must cite the relevant DESIGN.md section for traceability.
+| 1. Visual Theme | Context | `designer_get_personality` |
+| 2. Color Palette | Token setup | `design_tokens_generate` (OKLCH) |
+| 3. Typography | Font loading + scale | `design_tokens_get_category("typography")` |
+| 4. Spacing | Tailwind config | `design_tokens_get_category("spacing")` |
+| 5. Component Specs | Component impl | shadcn: `shadcn_get_rules`, `get_composition`, `get_component`. Raw Tailwind: `ui_ux_get_component_pattern`. |
+| 6. Motion | Animation build | `motion_generate_animation` |
+| 7. Elevation | Shadow tokens | `design_tokens_get_category("shadows")` |
+| 8. Do's & Don'ts | Self-review | Embedded assertions in tasks |
+| 9. Responsive | Breakpoint config | `ui_ux_get_principle("responsive")` |
+| 10. Anti-Patterns| Self-review | Embedded validations |
## The Contract
-Every implementation step touching a domain covered by Hyperstack MCP **must** reference actual MCP tool output. Plan built on imagined syntax = bug report scheduled for delivery.
+Every implementation step touching an MCP domain MUST reference tool output. Imagined syntax = rejected plan.
## Process
-### Step 1: MCP Survey for Implementation
+### Step 1: MCP Survey
-Before writing a single task, call relevant MCP tools for every domain the implementation will touch:
+Query tools BEFORE writing:
| Domain | Call |
|---|---|
-| **DESIGN.md present** | **`designer_get_personality(cluster)`, `designer_get_page_template(type)`, `designer_get_anti_patterns(industry)` - DESIGN.md is ground truth for all visual decisions** |
-| React Flow | `reactflow_get_api` for each component/hook used |
-| Motion | `motion_get_api` for each animation primitive + `motion_generate_animation` if DESIGN.md specifies motion |
-| Go / Echo | `golang_get_pattern` + `echo_get_recipe` for each pattern |
-| Rust | `rust_get_practice` for each relevant practice |
-| Design tokens | `design_tokens_get_procedure` per token step + `design_tokens_generate` if DESIGN.md specifies OKLCH palette |
-| React / Next.js | `react_get_pattern` + `react_get_constraints` |
-| shadcn (only if Q11b chose shadcn) | `shadcn_get_rules` (first) + `shadcn_get_composition(page_type)` + `shadcn_get_component(name)` + `shadcn_get_snippet(name)`. Invoke `hyperstack:shadcn-expert` for architectural guidance. |
-| Raw Tailwind (only if Q11b chose raw Tailwind) | `ui_ux_get_component_pattern(name)` per component. Hand-build from DESIGN.md Section 5. No library wrapper. |
-| Other component library (MUI, Mantine, Chakra, Ant Design) | Use the library's own docs. Hyperstack has no plugin for these. Flag to user. |
-
-Record each tool output. Plan's code blocks must match what tools return.
-
-**MCP Degraded Mode:** Tools fail → inform user: "MCP unavailable for [domain] - plan may contain unverified API shapes." Mark affected tasks `[UNVERIFIED]`. Don't silently proceed with assumed APIs.
+| **DESIGN.md present** | **`designer_get_personality`, `get_page_template`, `get_anti_patterns`** |
+| React Flow | `reactflow_get_api` |
+| Motion | `motion_get_api`, `motion_generate_animation` |
+| Go / Echo | `golang_get_pattern`, `echo_get_recipe` |
+| Rust | `rust_get_practice` |
+| Design tokens | `design_tokens_get_procedure`, `design_tokens_generate` |
+| React / Next.js | `react_get_pattern`, `react_get_constraints` |
+| shadcn (if selected) | `shadcn_get_rules` (FIRST), `get_composition`, `get_component`, `get_snippet`. Invoke `shadcn-expert` for guidance. |
+| Raw Tailwind (if selected) | `ui_ux_get_component_pattern` |
+| Other library | Read library documentation |
+
+**Degraded Mode:** If MCP fails, inform user: "MCP unavailable for [domain]. Plan contains unverified API shapes." Mark tasks `[UNVERIFIED]`.
### Step 2: Map Files
-Before defining tasks, map every file to be created or modified:
-
```
-Create: exact/path/to/new-file.ts - one-line responsibility
-Modify: exact/path/to/existing.ts - what changes and why
-Test: exact/path/to/file.test.ts - what behaviour is tested
+Create: path/to/new-file.ts - one-line purpose
+Modify: path/to/existing.ts - what changes + why
+Test: path/to/file.test.ts - behaviour tested
```
-
-Each file has one clear responsibility. Files that change together live together. Split by responsibility, not by layer.
+Split by responsibility, not by layer. Files changing together live together.
### Step 3: Write Tasks
-**Each task produces working, testable, committed software on its own.**
-
-Task structure:
+Task structure MUST be autonomous and testable:
````markdown
### Task N: [Name]
**Files:**
-- Create/Modify/Test: `exact/path/file.ts`
-
-**MCP references:** [tool calls from Step 1 relevant to this task]
+- Create/Modify: `path/file.ts`
-- [ ] **Step 1: Write the failing test**
+**MCP references:** [cite Step 1 outputs]
+- [ ] **Step 1: Failing test**
```ts
-describe('ComponentName', () => {
- it('should do X when Y', () => {
- // ...
- });
-});
+describe('Component', () => { it('does X', () => { ... }); });
```
-
-Run: `npx vitest run path/to/file.test.ts`
-Expected: FAIL - "X is not defined"
+Run: `npx vitest run path/file.test.ts` (Expected: FAIL)
- [ ] **Step 2: Implement**
-
```ts
-// actual implementation matching MCP-verified API shapes
+// actual verified implementation
```
- [ ] **Step 3: Verify**
-
-Run: `npx vitest run path/to/file.test.ts`
-Expected: PASS - 1/1
+Run: `npx vitest run path/file.test.ts` (Expected: PASS)
- [ ] **Step 4: Commit**
-
```bash
-git add path/to/file.ts path/to/file.test.ts
-git commit -m "feat: [specific description]"
+git add file.ts file.test.ts
+git commit -m "feat: [desc]"
```
````
### Step 4: Self-Review
-After writing the complete plan, check inline:
-
-1. **Spec coverage** → task for every requirement in the approved design? List gaps.
-2. **Placeholder scan** → search for "TBD", "TODO", "add error handling", "similar to Task N", "implement later", steps without code blocks. Fix every instance.
-3. **MCP verification** → every domain-specific code block traces back to a tool call in Step 1.
-4. **Type consistency** → types, method names, prop names consistent across tasks.
-5. **Step atomicity** → each step = 2-5 minutes of work. "Implement the entire service layer" is not a step.
+1. **Spec coverage:** Cover every requirement?
+2. **Placeholder scan:** Hunt and destroy "TBD", "TODO", "implement later", "similar to Task N".
+3. **MCP verification:** All APIs trace back?
+4. **Type consistency:** Signatures match?
+5. **Atomicity:** Steps represent 2-5 minutes of work?
### Step 5: Handoff
-Save plan to `docs/plans/YYYY-MM-DD-[feature-name].md` and commit.
-
-Then offer:
+Save to `docs/plans/YYYY-MM-DD-[feature].md`. Commit. Ask user:
-> "Plan saved to `[path]`. Three execution options:
->
-> 1. **Autonomous** - execute all tasks end-to-end without pausing. Tests, reviews, ship-gate run automatically. Only stops on failure.
-> 2. **Subagent-driven** (`hyperstack:subagent-ops`) - fresh agent per task, automated two-stage review between tasks.
-> 3. **Inline with checkpoints** (`hyperstack:engineering-discipline`) - execute tasks in this session, pause for human review at phase gates.
->
-> Which approach?"
+> "Plan saved. Execution options:
+> 1. **Autonomous** (`autonomous-mode`) - End-to-end auto.
+> 2. **Subagent-driven** (`subagent-ops`) - Strict agent handoff per task.
+> 3. **Manual checkpoints** (`engineering-discipline`) - Execute, pause for human review."
-## No Placeholders - Ever
+## NO PLACEHOLDERS
-Plan failures → never write:
-- "TBD" / "TODO" / "fill in later"
-- "Add appropriate error handling"
-- "Write tests for the above" without actual test code
-- "Similar to Task N" → repeat the code, tasks may be read out of order
-- Steps describing what to do without showing how
-- References to types or functions not defined in any task
+Never write:
+- "TBD" / "fill in later"
+- "Add error handling" (write it)
+- "Write tests for above" (provide code)
+- "Similar to Task N" (copy it)
-## The Iron Law
+## IRON LAW
```
NO PLANS WITHOUT FRESH MCP-VERIFIED DATA
```
-Every API call, prop name, hook signature, or library pattern must trace to an MCP tool call made in THIS session. Not last session. Not from memory.
+Prop names and hook signatures trace to THIS session's MCP output. Not memory. Not last session.
-## Red Flags - STOP
+## RED FLAGS - STOP
| Thought | Reality |
|---|---|
-| "I know how React Flow Handle works, no need to check" | Plan will have wrong prop names. Call the tool. |
-| "I'll write the test structure, developer can fill in assertions" | That's a placeholder. Write the real test. |
-| "This task is too small for a full test" | No task is too small for a failing test. |
-| "I'll reference the survey output later" | Do the survey before writing. Not after. |
-| "I already surveyed this library last week" | State drifts. MCP data updates. Call again. |
-| "The MCP tool output is obvious" | Cite the actual output. |
-| "I'll TBD the uncertain parts" | TBD = plan failure. Resolve uncertainty before writing the task. |
-| "Similar to Task N saves time" | Plans get read out of order. Repeat the code. |
-| "I don't need to run the tool for this common pattern" | Common patterns drift. Call the tool. |
-| "User is waiting, I'll skip the survey" | Plan without survey = bug report. Do the survey. |
-| "This is a minor refactor" | Minor refactors move responsibility. Plans for responsibility changes need MCP verification. |
-
-## Integration
-
-- **Requires (backend/infra):** `hyperstack:blueprint` approved design
-- **Requires (visual/UX):** `hyperstack:designer` approved DESIGN.md
-- **Executes via:** Autonomous mode, `hyperstack:subagent-ops`, or `hyperstack:engineering-discipline`
-- **Completes via:** `hyperstack:ship-gate` → `hyperstack:deliver`
+| "I know the React Flow API." | Write wrong props. Call tool. |
+| "Tester writes assertions." | Placeholder detected. Write the test. |
+| "Too small for test." | All tasks require tests. |
+| "Run survey later." | Survey dictates plan. Run first. |
+| "Tool output obvious." | Cite it anyway. |
+| "TBD uncertain parts." | Resolve before writing. |
+| "Similar to Task N." | Plans read piecemeal. Repeat code. |
## Reverse Escalation
| Discovery | Escalate to | Action |
|---|---|---|
-| DESIGN.md section ambiguous or contradictory | `hyperstack:designer` | Pause plan, resolve, append clarification to DESIGN.md, resume |
-| Component needed not in DESIGN.md Section 5 | `hyperstack:designer` | Invoke designer with specific gap, append to DESIGN.md |
-| MCP tool returns conflicting shapes with DESIGN.md | `hyperstack:designer` | DESIGN.md may need to acknowledge framework constraints |
-| Architecture gap (non-visual) | `hyperstack:blueprint` | Re-enter blueprint for architecture decision |
+| DESIGN.md ambiguous | `designer` | Append clarification. Resume. |
+| Missing component | `designer` | Invoke for gap. Update DESIGN.md. |
+| MCP tool conflict | `designer` | Acknowledge framework constraints. |
+| Architecture gap | `blueprint` | Formalize structural decision. |
-Don't silently invent what DESIGN.md doesn't specify. Escalate back to designer.
+## Lifecycle
+**Frontend Agent:**
+`blueprint` → `designer` → `forge-plan` (THIS) → `[execution]` → `ship-gate` → `deliver`
-## Lifecycle Integration
-
-### Agent Workflow Chains
-
-**Website/Frontend Agent:**
-```
-blueprint → designer → forge-plan (THIS) → [execution] → ship-gate → deliver
- ↓
- DESIGN.md ingestion
- ↓
- [shadcn-expert if Q11b=shadcn]
-```
-
-**Backend/Infra Agent:**
-```
-blueprint → forge-plan (THIS) → [execution] → ship-gate → deliver
- ↓
- architecture note
-```
-
-**Execution handoff (user chooses):**
-- `autonomous-mode` → full auto end-to-end
-- `subagent-ops` → fresh agent per task
-- `engineering-discipline` → manual with checkpoints
-
-### Upstream Dependencies
-- `blueprint` → approved architecture note (backend/infra)
-- `designer` → approved DESIGN.md (visual/UX)
-
-### Downstream Consumers
-- `autonomous-mode` | `subagent-ops` | `engineering-discipline` → executes plan
-- `shadcn-expert` → if Q11b=shadcn, per-component guidance
-- `worktree-isolation` → clean workspace before execution
-
-### Reverse Escalation
-| Discovery | Escalate to | Action |
-|---|---|---|
-| DESIGN.md section ambiguous | `designer` | Pause, resolve, append to DESIGN.md |
-| Component not in DESIGN.md Section 5 | `designer` | Add to DESIGN.md |
-| MCP tool conflicts with DESIGN.md | `designer` | Reconcile framework constraints |
-| Architecture gap (non-visual) | `blueprint` | Re-enter for decision |
+**Backend Agent:**
+`blueprint` → `forge-plan` (THIS) → `[execution]` → `ship-gate` → `deliver`
diff --git a/skills/hyperstack/SKILL.md b/skills/hyperstack/SKILL.md
new file mode 100644
index 0000000..966d97e
--- /dev/null
+++ b/skills/hyperstack/SKILL.md
@@ -0,0 +1,176 @@
+---
+name: hyperstack
+category: meta
+description: Bootstrap definitions establishing MCP tools and skills prior to work. Auto-loaded at session start. Do not bypass or rationalize skipping this skill.
+---
+
+
+If dispatched as a subagent, skip this skill.
+Context is provided by the orchestrating agent. Do not reload bootstrap.
+
+
+
+Hyperstack is active. This constitutes the mandatory operational framework for this repository.
+
+**Three-Layer Ecosystem:**
+1. **Layer 1: Ground Truth (MCP)** - Deterministic data for the stack.
+2. **Layer 2: Process (Skills)** - Disciplined engineering workflows and gates.
+3. **Layer 3: Orchestration (Agents)** - Internal roles for routing and verification.
+
+**The 1% Rule:** If a 1% probability exists that a Hyperstack skill, MCP tool, or internal agent role applies, YOU MUST invoke or route to it BEFORE acting. Not after checking code. Not after testing a hypothesis. BEFORE.
+
+You cannot rationalize exceptions to this rule.
+
+
+---
+
+## The Iron Laws
+
+```
+1. NO CODE WITHOUT MCP GROUND-TRUTH DATA
+ Call relevant Hyperstack plugins prior to implementation.
+
+2. NO VISUAL CODE WITHOUT AN APPROVED DESIGN.md
+ The designer skill produces the contract when routing requires a design contract; existing-project frontend logic work stays workspace-first.
+
+3. NO COMPLETION CLAIMS WITHOUT SHIP-GATE EVIDENCE
+ "Should work" is unacceptable. Execute the command and output results.
+
+4. NO SKIPPING SKILLS BECAUSE "THIS IS SIMPLE"
+ Simple tasks hide unexamined assumptions that cause the most damage.
+
+5. NO SPECIALIST WORK WITHOUT PROPER ROLE ROUTING
+ Route specialist domain tasks (e.g., website building) to the corresponding agent.
+```
+
+Violating the letter of these laws violates the spirit.
+
+---
+
+## Instruction Priority
+
+1. **User's explicit instructions** (Project rules, direct requests) - Maximum priority.
+2. **Hyperstack skills** - Overrides system defaults.
+3. **Default system behavior** - Minimum priority.
+
+---
+
+## Layer 1: MCP Tools (Ground-Truth Data)
+
+Call these BEFORE writing code. **Memory is unacceptable.**
+
+| Namespace | Stack | Must-call-first tools |
+|---|---|---|
+| `designer_*` | Design engine | `resolve_intent`, `get_personality`, `get_preset`, `get_page_template`, `get_font_pairing`, `get_anti_patterns` |
+| `design_tokens_*` | Token systems | `generate`, `get_category`, `get_gotchas` |
+| `ui_ux_*` | UI/UX principles | `get_principle`, `get_component_pattern`, `get_gotchas` |
+| `shadcn_*` (if selected) | Base UI edition | `get_rules` (FIRST), `get_composition`, `get_component`, `get_snippet`, `list_components` |
+| `reactflow_*` | React Flow v12 | `get_api`, `search_docs`, `get_pattern` |
+| `motion_*` | Motion for React v12 | `get_api`, `get_examples`, `get_transitions` |
+| `lenis_*` | Lenis smooth scroll | `get_api`, `generate_setup`, `get_pattern` |
+| `react_*` | React 19 / Next.js | `get_pattern`, `get_constraints`, `search_docs` |
+| `echo_*` | Echo (Go HTTP) | `get_recipe`, `get_middleware`, `decision_matrix` |
+| `golang_*` | Go best practices | `get_practice`, `get_pattern`, `get_antipatterns` |
+| `rust_*` | Rust practices | `get_practice`, `cheatsheet`, `search_docs` |
+
+### MCP Degraded Mode
+
+If MCP tools fail:
+1. Explicitly inform user: "Hyperstack MCP server is unavailable. Assertions are uncertain."
+2. Fall back to training data, but FLAG every answer as uncertain.
+3. NEVER assume ground-truth validity.
+4. DO NOT invent API shapes.
+
+---
+
+## Layer 2: When to Invoke Skills
+
+Load via `Skill` tool **before acting**. Registered in: `skills/INDEX.md`.
+
+### Announcement Iron Law
+
+```
+BEFORE invoking any Hyperstack skill, announce:
+"Using hyperstack:[skill-name] - [one-line purpose]"
+```
+
+### Condition → Skill Table
+
+| Situation | Load this skill |
+|---|---|
+| Starting any new feature, component, or behaviour change | `blueprint` |
+| Any task touching existing project code | `blueprint` builds `workspace_inventory` first |
+| Task creates a new surface, changes visual semantics, or has no reliable existing pattern match | `designer` → produces conditional `design_contract` / DESIGN.md |
+| Have an approved design or plan to execute | `forge-plan` or `run-plan` |
+| Claiming anything is complete, fixed, or passing | `ship-gate` — evidence required |
+| Hit any bug, test failure, or unexpected behaviour | `debug-discipline` — root cause before any fix |
+| User says "autonomous", "just do it", "go ahead" | `autonomous-mode` |
+| Plan has 2+ independent tasks | `subagent-ops` or `parallel-dispatch` |
+| Implementing any feature or bug fix (before code) | `test-first` |
+| Reviewing completed implementation | `code-review` |
+| Security-sensitive API, auth, or infra code | `security-review` |
+| UI state machine, interaction audit | `behaviour-analysis` |
+| Selecting an abstraction or design pattern | `engineering-discipline` |
+| Writing or updating project documentation | `readme-writer` |
+| Starting implementation that needs workspace isolation | `worktree-isolation` |
+
+### Workflow Chain Reference
+
+```
+New work: blueprint → workspace_inventory + change classification → [designer only if required] → forge-plan → execution → ship-gate → deliver
+
+Existing: run-plan ──┤
+ ├→ autonomous-mode
+ ├→ subagent-ops
+ └→ engineering-discipline
+
+Debugging: debug-discipline → parallel-dispatch
+```
+
+**Planning rule:** `workspace_inventory` is universal. `design_contract` is conditional. `verification_report` is universal.
+
+---
+
+## Layer 3: Agents (Orchestration & Routing)
+
+Internal roles — auto-invoked, not user-facing.
+
+| Agent | Owns |
+|---|---|
+| `hyper` | Classification, routing, gate enforcement, final verification, delivery |
+| `frontend-builder` | Frontend-facing design/implementation, CTA hierarchy, page structure, and existing-project frontend logic work |
+
+### Disallowed Transitions
+
+- `user request -> frontend-builder`
+- `frontend-builder -> ship`
+- `frontend-builder -> deliver`
+
+---
+
+## High-Signal Red Flags
+
+| Thought | Reality | Action |
+|---|---|---|
+| "I know the React Flow API from memory." | Memory drifts. v11 and v12 differ. | Call `reactflow_get_api` first. |
+| "This is a simple animation." | Animations require `prefers-reduced-motion`, easing, and GPU properties. | Call `motion_get_examples` first. |
+| "Go error handling is straightforward." | Straightforward code hosts anti-patterns. | Call `golang_get_practice` first. |
+| "I'll check docs after writing." | Shipping occurs before checking. | Consult docs BEFORE code. |
+| "I know the OKLCH token pattern." | OKLCH dictates specific alpha, chroma, and lightness rules. | Call `design_tokens_get_procedure` first. |
+| "This pattern is common; I'll adapt it." | Adaptation obscures structural drift. | Call the MCP tool and replicate ground truth. |
+| "The user is impatient; skip the gate." | User impatience does not grant permission to ship slop. | Enforcement gates are mandatory. |
+| "This isn't visual work." | If it changes look, motion, or interaction, it is visual. | Invoke designer skill. |
+| "Subagent reported success." | Subagents hallucinate success. | Manually review the diff and run tests. |
+| "Just this once." | "Once" establishes a pattern. | Zero exceptions. |
+
+---
+
+## Final Response Check
+
+1. [ ] Did I evaluate Hyperstack skill applicability? (1% rule)
+2. [ ] Did I execute relevant MCP tools? (No memory rely)
+3. [ ] If visual, did I invoke `designer` BEFORE coding?
+4. [ ] If claiming completion, did I execute verification THIS message?
+5. [ ] Did I announce skill invocation with exact formatting?
+
+**Stop and fix if any answer is NO.**
diff --git a/skills/using-hyperstack/references/agent-tools.md b/skills/hyperstack/references/agent-tools.md
similarity index 100%
rename from skills/using-hyperstack/references/agent-tools.md
rename to skills/hyperstack/references/agent-tools.md
diff --git a/skills/using-hyperstack/references/codex-tools.md b/skills/hyperstack/references/codex-tools.md
similarity index 100%
rename from skills/using-hyperstack/references/codex-tools.md
rename to skills/hyperstack/references/codex-tools.md
diff --git a/skills/using-hyperstack/references/gemini-tools.md b/skills/hyperstack/references/gemini-tools.md
similarity index 100%
rename from skills/using-hyperstack/references/gemini-tools.md
rename to skills/hyperstack/references/gemini-tools.md
diff --git a/skills/parallel-dispatch/SKILL.md b/skills/parallel-dispatch/SKILL.md
index 69c5e5d..b6204f4 100644
--- a/skills/parallel-dispatch/SKILL.md
+++ b/skills/parallel-dispatch/SKILL.md
@@ -89,14 +89,6 @@ Return: Summary of root cause and changes made.
| Related failures split across agents | Investigate together first |
| Same files given to multiple agents | Guaranteed merge conflicts |
-## Red Flags - STOP
-
-| Thought | Reality |
-|---|---|
-| "Dispatch 5 agents for speed" | More agents ≠ faster if they conflict |
-| "I'll let agents figure out the scope" | Vague scope → vague results |
-| "These might be related but I'll parallelize anyway" | Related bugs in parallel = wasted work. Investigate first. |
-| "Skip the integration check" | Agents don't know about each other. You must verify. |
## Integration
diff --git a/skills/run-plan/SKILL.md b/skills/run-plan/SKILL.md
index efebd72..e0f42a8 100644
--- a/skills/run-plan/SKILL.md
+++ b/skills/run-plan/SKILL.md
@@ -14,13 +14,6 @@ description: Use when you have an existing plan, spec, or task list to execute.
Creating a new plan from scratch → use `hyperstack:blueprint` → `hyperstack:forge-plan` instead.
-## The Iron Law
-
-```
-NO EXECUTION WITHOUT PLAN VALIDATION FIRST.
-```
-
-Plan with wrong API shapes or missing steps → wrong code. 2 minutes of validation saves hours of rework.
## Process
@@ -92,15 +85,6 @@ Blocker mid-task → stop immediately, don't guess or work around it. Report to
All tasks marked complete → invoke `hyperstack:deliver`.
-## Red Flags - STOP
-
-| Thought | Reality |
-|---|---|
-| "The plan looks fine, no need to check MCP" | One wrong prop name = broken code in every task that uses it |
-| "I'll fix the gap as I go" | Undocumented gaps → undocumented decisions |
-| "The user wrote this plan, it must be correct" | Plans have bugs. That's why this step exists. |
-| "Step N is unclear but I can infer what they meant" | Stop and ask. Inferred intent → surprising code. |
-| "I'll skip ship-gate on this task, it's small" | No exceptions. |
## Integration
diff --git a/skills/shadcn-expert/SKILL.md b/skills/shadcn-expert/SKILL.md
index 224811a..2314b9f 100644
--- a/skills/shadcn-expert/SKILL.md
+++ b/skills/shadcn-expert/SKILL.md
@@ -6,15 +6,6 @@ description: Advanced shadcn/ui architect specializing in Base UI, Tailwind v4,
# shadcn/ui Expert (Base UI Edition)
-## The Iron Law
-
-```
-NO COMPONENT WITHOUT shadcn_get_rules FIRST
-```
-
-Call `shadcn_get_rules` before proposing any new component or modification. Base UI edition differs from standard shadcn (Radix) in multiple ways. Memory is not acceptable.
-
-Violating the letter = violating the spirit.
## When This Skill Applies
@@ -31,10 +22,13 @@ Violating the letter = violating the spirit.
## Position in the Ecosystem
```
-hyperstack:designer → DESIGN.md
+hyperstack:blueprint → workspace_inventory + change classification
│
- ▼ (if shadcn chosen in Q11)
-hyperstack:forge-plan reads DESIGN.md
+ ▼ (if shadcn chosen and design contract required)
+hyperstack:designer → conditional DESIGN.md / design_contract
+ │
+ ▼
+hyperstack:forge-plan reads workspace + routed requirements
│
▼
hyperstack:shadcn-expert (THIS skill)
@@ -45,7 +39,7 @@ hyperstack:shadcn-expert (THIS skill)
└─▶ shadcn_get_snippet(name)
│
▼
-Implementation tasks per DESIGN.md Section 5
+Implementation tasks per routed component requirements
```
## MCP Tools
@@ -103,26 +97,12 @@ Implementation tasks per DESIGN.md Section 5
## Integration with Designer + Forge-Plan
-**Upstream:** `hyperstack:forge-plan` processes DESIGN.md Section 5 → calls `shadcn_get_component(name)` per component → references this skill for architectural guidance
+**Upstream:** `hyperstack:forge-plan` processes routed component requirements. If a design contract exists, it uses the relevant component sections. Existing-project frontend logic work may stay workspace-first without forcing a design contract.
-**Downstream:** Component code matching DESIGN.md Section 5 variants + states, all P7 (Components) rules enforced, ready for `hyperstack:ship-gate`
+**Downstream:** Component code matching required variants + states, all P7 (Components) rules enforced, ready for `hyperstack:ship-gate`
-**Reverse escalation:** DESIGN.md spec incompatible with shadcn architecture → escalate to `hyperstack:designer` to reconcile. Don't silently adapt.
+**Reverse escalation:** If a routed design contract is incompatible with shadcn architecture, escalate to `hyperstack:designer` to reconcile. Don't silently adapt.
-## Red Flags - STOP
-
-| Thought | Reality |
-|---|---|
-| "User didn't say 'shadcn' explicitly, but I'll assume it" | Do NOT assume. Ask or check designer Q11. |
-| "I know the shadcn rules from training data" | Training data has standard shadcn (Radix). This is Base UI edition. Call `shadcn_get_rules`. |
-| "data-slot is just a naming convention" | It's the primary styling selector for parent→child styling. Mandatory. |
-| "I'll use Radix because it's more common" | Project chose Base UI. Use `@base-ui/react`. |
-| "Hardcoding pixel positions is faster" | Use Base UI props. Hardcoded px breaks responsive behavior. |
-| "This Dialog has 5 slots, I'll combine into one component" | Split into sub-components. Monolithic Dialogs are anti-pattern. |
-| "I'll skip 'use client' since it seems stateless" | Does it use `data-open` or any state modifier? → needs `'use client'`. Check before skipping. |
-| "The cn utility is optional" | Mandatory. All className merging goes through `cn`. |
-| "I'll pick variant names that match the brand" | Stick to `default/outline/secondary/ghost/destructive`. Custom variants break the system. |
-| "shadcn components work with any color system" | OKLCH-native. Hex values break the token system. |
## Lifecycle Integration
@@ -131,23 +111,23 @@ Implementation tasks per DESIGN.md Section 5
**Website/Frontend Agent (if Q11b=shadcn):**
```
-designer → DESIGN.md → forge-plan → shadcn-expert (THIS) → [component implementation]
- ↓
- [shadcn_* MCP tools]
+blueprint → workspace inventory / change classification → [designer only if required] → forge-plan → shadcn-expert (THIS) → [component implementation]
+ ↓
+ [shadcn_* MCP tools]
```
### Upstream Dependencies
-- `designer` → Q11b chose shadcn/ui (Base UI edition)
-- `forge-plan` → processes DESIGN.md Section 5 → calls shadcn_get_component per component
+- Q11b chose shadcn/ui (Base UI edition)
+- `forge-plan` → processes routed component requirements → calls shadcn_get_component per component
### Downstream Consumers
-- Component code matching DESIGN.md Section 5 variants + states
+- Component code matching routed variants + states
- `ship-gate` → P7 (Components) rules enforced
### Reverse Escalation
| Discovery | Escalate to | Action |
|---|---|---|
-| DESIGN.md spec incompatible with shadcn architecture | `designer` | Reconcile DESIGN.md with Base UI constraints |
+| Design contract incompatible with shadcn architecture | `designer` | Reconcile design contract with Base UI constraints |
### When NOT to Use
- User chose raw Tailwind (no component library)
diff --git a/skills/ship-gate/SKILL.md b/skills/ship-gate/SKILL.md
index bc3ac3c..b523829 100644
--- a/skills/ship-gate/SKILL.md
+++ b/skills/ship-gate/SKILL.md
@@ -1,99 +1,57 @@
---
name: ship-gate
category: core
-description: Use before claiming any work is complete, fixed, or passing. Run the verification command and show output before making any success claim.
+description: Execute before claiming work is complete, fixed, or passing. Run the verification command and show output. No evidence = no claim.
---
# Verification Before Completion
-## The Iron Law
-
-```
-NO COMPLETION CLAIMS WITHOUT FRESH VERIFICATION EVIDENCE.
-```
-
-Not run in this message → cannot claim it passes. Claiming completion without evidence = dishonesty.
-
-Violating the letter = violating the spirit.
## The Gate
```
-1. IDENTIFY → What command proves this claim?
-2. RUN → Execute it fresh. Not from memory. Not from a prior run.
-3. READ → Full output. Exit code. Error count. Every line.
-4. VERIFY → Does output confirm the claim?
- NO → State actual status with evidence
- YES → State claim WITH evidence attached
+1. IDENTIFY → Which command proves this claim?
+2. RUN → Execute it fresh. Not from memory.
+3. READ → Full output. Exit code. Error count.
+4. VERIFY → Output confirms claim?
+ NO → State status + provide evidence.
+ YES → State claim + provide evidence.
5. CLAIM → Only now.
```
-Skipping any step = lying, not verifying.
-
-**Evidence must be visible in this message.** Not "I ran the command." Not "I checked earlier." Actual command output pasted into this message. No output = no claim.
+Skipping steps = lying.
+**Evidence MUST be visible.** Paste actual command output. "I ran the command" is unacceptable. No output = no claim.
## Evidence Format
-### For test claims:
-```
-✅ Tests pass:
-
-$ npm test
-PASS src/components/__tests__/Button.test.tsx
- Button
- ✓ renders with label (5ms)
- ✓ handles click event (3ms)
-
-Tests: 3 passed, 3 total
-```
-
-### For type checks:
-```
-✅ Types check:
-
-$ tsc --noEmit
-[no output = success]
-```
-
-### For build claims:
-```
-✅ Build succeeds:
-
-$ npm run build
-[build output showing exit 0]
-```
-
-### For MCP-verified patterns:
-```
-✅ Code matches MCP output:
-
-MCP tool: reactflow_get_api(Handle)
-Output: props = { children, position, type }
+Show output in message:
-Code:
-```
+**Tests:** `$ npm test` → `Tests: 3 passed, 3 total`
+**Types:** `$ tsc --noEmit` → `[no output = success]`
+**Build:** `$ npm run build` → `[exit 0]`
+**MCP:** Code block must explicitly match specific MCP tool response.
-Cannot show evidence → cannot make the claim.
+Cannot show evidence → cannot make claim.
## Verification Map
| Claim | Required | Not sufficient |
|---|---|---|
-| Tests pass | Test command output showing 0 failures | "Should pass", previous run, looking at the code |
-| Build succeeds | Build command: exit 0 | Linter passing, "no obvious errors" |
-| Bug is fixed | Reproduce original symptom: now passes | Code changed, "logically fixed" |
+| Tests pass | Test command output showing 0 failures | "Should pass", previous run |
+| Build succeeds | Build command: exit 0 | Linter passing |
+| Bug is fixed | Reproduce original symptom: now passes | "Logically fixed" |
| Type checks | `tsc --noEmit`: 0 errors | "Looks typed correctly" |
-| MCP data applied correctly | Code matches MCP output shown in session | "I followed the pattern" |
-| Requirements met | Line-by-line checklist against the spec | Tests passing |
-| Subagent completed | VCS diff confirms actual changes | Subagent reports "done" |
-| Regression covered | Red-green cycle verified | "I wrote a test for it" |
-| **DESIGN.md compliance** | **Implementation matches all 10 DESIGN.md sections. All component states present. No anti-patterns from Section 10.** | **"Looks right", "follows the design"** |
+| MCP pattern | Code matches current session MCP output | "Following pattern" |
+| Requirements | Line-by-line checklist against spec | Tests passing |
+| Subagent done | VCS diff confirms actual changes | Subagent reports "done" |
+| Regression | Red-green cycle verified | "I wrote a test" |
+| **Design-contract compliance** | **Implementation matches all required sections when a design contract exists.** | **"Looks right"** |
-## DESIGN.md Compliance Gate (visual/UX tasks only)
+## DESIGN.md / Design Contract Compliance Gate (Visual/UX)
-If DESIGN.md exists in the repo, completion claim must pass this gate:
+If a task requires a design contract and DESIGN.md exists, completion requires this gate:
-### Step 1: Run Automated Compliance Checker
+### Step 1: Automated Checker
```bash
designer_verify_implementation(
@@ -105,96 +63,37 @@ designer_verify_implementation(
| DESIGN.md Section | What the tool checks |
|---|---|
| 2. Color Palette | All OKLCH tokens present. Contrast >= WCAG AA. |
-| 3. Typography | Font family loaded. Type scale tokens defined. Tracking/line-height match DESIGN.md. |
-| 4. Spacing | Spacing tokens on 4px grid. No arbitrary pixel values. |
-| 5. Components | ALL required states present (default/hover/focus/active/disabled/loading). Semantic HTML used. |
-| 6. Motion | `prefers-reduced-motion` respected. No `linear` easing. No `> 500ms` UI transitions. |
-| 7. Elevation | Shadow tokens defined. Z-index uses named scale (no `9999`). |
-| 8. Do's and Don'ts | Each Do/Don't from DESIGN.md checked against code. None violated. |
-| 9. Responsive | Layout tested at 375/768/1024/1440px. No horizontal scroll. Prose max-width 65ch. |
-| 10. Anti-Patterns | No AI slop: no `#6366F1`, no `font-weight: 500` everywhere, no missing states, no `animate-bounce` on static, no 3+ font families, no `rgba(0,0,0)` shadows. |
+| 3. Typography | Fonts loaded. Scale defined. Tracking matches. |
+| 4. Spacing | 4px grid. No arbitrary pixels. |
+| 5. Components | ALL states (default/hover/focus/active/disabled/loading). |
+| 6. Motion | `prefers-reduced-motion`. No `linear`. Transitions < 500ms. |
+| 7. Elevation | Shadow tokens defined. Z-index uses scale. |
+| 8. Do's and Don'ts | Each rule checked against code. |
+| 9. Responsive | 375/768/1024/1440px tested. Prose <= 65ch. |
+| 10. Anti-Patterns | No `#6366F1`, no `500` weight everywhere, no `rgba` shadows. |
-### Step 2: Show the Tool Output
+### Step 2: Show Output
-```
-✅ DESIGN.md Compliance Check:
-
-Section 1 (Theme): PASS
-Section 2 (Colors): PASS - all OKLCH tokens present
-Section 3 (Typography): PASS - fonts loaded, scale defined
-Section 4 (Spacing): PASS - 4px grid enforced
-Section 5 (Components): PASS - all states present
-Section 6 (Motion): PASS - prefers-reduced-motion respected
-Section 7 (Elevation): PASS - shadow/z-index tokens used
-Section 8 (Do's/Don'ts): PASS - no violations
-Section 9 (Responsive): PASS - tested at all breakpoints
-Section 10 (Anti-Patterns): PASS - no slop detected
-```
+Paste the `designer_verify_implementation` output block representing PASS on all 10 sections.
### Step 3: Handle Failures
-Any section fails → do NOT claim completion.
-- Option A: Fix the code to pass the check
-- Option B: Escalate to `hyperstack:designer` to revise DESIGN.md if the design was wrong
+Any required section fails → DO NOT claim completion.
+- Option A: Fix code.
+- Option B: Escalate to `designer` to revise the design contract.
-DESIGN.md doesn't exist for a visual task → process failure upstream. Stop and invoke `hyperstack:designer` before shipping anything.
+No design contract on a task that requires one? Stop. Invoke `designer`.
-## Red Flags - STOP
-| Thought | Reality |
-|---|---|
-| "Should work now" | "Should" ≠ evidence. Run the command. |
-| "I'm confident in this change" | Confidence ≠ evidence. Run the command. |
-| "Subagent said it's done" | Subagents lie. Check the VCS diff. Run the tests. |
-| "Minor change, no need to recheck" | Minor changes cause regressions. Run the command. |
-| "Tests were passing before my change" | Irrelevant. Run them again now. |
-| "MCP tool confirmed the pattern" | Confirms the pattern, not that your code is correct. Run the command. |
-| "I'll verify after I push" | After you push it's in CI. Verify BEFORE. |
-| "I already ran it earlier this conversation" | State drifts. Run it again. |
-| "The linter is passing" | Linter ≠ compiler ≠ runtime. Run the full verification. |
-| "Partial check is enough" | Partial verification = theater. Full check. |
-| "I ran the tests, they passed" (no output shown) | Evidence not shown = claim not made. Paste the full output. |
-| "The code looks correct" (claiming DESIGN.md compliance) | Looks ≠ verification. Run `designer_verify_implementation`. Show tool output. |
-| "I did the DESIGN.md checks manually" | Manual checks miss edge cases. Use the automated tool. |
-| "DESIGN.md doesn't exist yet, I'll implement first" | DESIGN.md is a blocker. Invoke `hyperstack:designer` before writing CSS/components. |
-| "Just this once" | No exceptions. |
-
-## Integration
-
-Run this skill before:
-- Any `git commit` or PR creation
-- Marking any task as complete
-- Reporting status to the user
-- Claiming a bug is fixed
-- Handing work off to a subagent or reviewer
-- Transitioning between phases in `hyperstack:engineering-discipline`
-
-
-## Lifecycle Integration
-
-### Agent Workflow Chains
+## Lifecycle
**All execution paths converge here:**
-```
-[autonomous-mode | subagent-ops | engineering-discipline] → ship-gate (THIS) → deliver
-```
-
-**DESIGN.md compliance (visual/UX only):**
-```
-ship-gate → designer_verify_implementation → [PASS → deliver | FAIL → fix or escalate]
-```
+`[autonomous-mode | subagent-ops | engineering-discipline] → ship-gate (THIS) → deliver`
-### Upstream Dependencies
-- `autonomous-mode` → final gate before delivery
-- `subagent-ops` → final gate after all tasks
-- `engineering-discipline` → per-task + final gate
+**Design-contract flow:**
+`ship-gate → designer_verify_implementation (when design contract required) → [PASS → deliver | FAIL → fix/escalate]`
-### Downstream Consumers
-- `deliver` → only proceeds if ship-gate passes
-
-### Escalation Paths
-| Failure | Escalate to | Action |
-|---|---|---|
-| DESIGN.md compliance fails | `designer` | Fix code or revise DESIGN.md |
-| Tests fail | `debug-discipline` → fix → re-run ship-gate |
-| Type/lint errors | Fix → re-run ship-gate |
+**Escalations:**
+- Design-contract check fails → `designer`
+- Tests fail → `debug-discipline`
+- Type/lint errors → Fix → re-run ship-gate
diff --git a/skills/subagent-ops/SKILL.md b/skills/subagent-ops/SKILL.md
index ca0eb17..b7fda99 100644
--- a/skills/subagent-ops/SKILL.md
+++ b/skills/subagent-ops/SKILL.md
@@ -103,17 +103,6 @@ Good subagent prompts are:
Bad: "Fix the tests" (too broad)
Good: "Fix the 3 failing tests in `src/flow/nodes.test.ts`. Root cause is [X]. Expected: all pass. Return: summary of changes."
-## Red Flags - STOP
-
-| Thought | Reality |
-|---|---|
-| "I'll dispatch multiple implementers in parallel" | They'll conflict on shared files. One at a time. |
-| "Skip spec review, the code looks fine" | Spec drift is invisible without review. |
-| "Start code quality before spec compliance" | Wrong order. Spec first, quality second. |
-| "Subagent said done, move on" | Verify with review. Trust but verify. |
-| "I'll fix it myself instead of re-dispatching" | Context pollution. Dispatch a fix subagent. |
-| "This task is too small for the full cycle" | Small tasks still get spec + quality review. |
-| "Let the subagent read the plan file" | Provide full text. File reads waste subagent context. |
## Integration
diff --git a/skills/test-first/SKILL.md b/skills/test-first/SKILL.md
index 71e0a1e..a59dc4f 100644
--- a/skills/test-first/SKILL.md
+++ b/skills/test-first/SKILL.md
@@ -6,15 +6,6 @@ description: Use when implementing any feature, bug fix, or behaviour change - b
# Test-First Development
-## The Iron Law
-
-```
-NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST.
-```
-
-Wrote code before the test? Delete it. Start over. Don't keep it as "reference." Don't "adapt" it while writing tests. Delete means delete.
-
-Violating the letter = violating the spirit.
## When to Use
@@ -105,20 +96,10 @@ Before marking work complete:
Can't check all boxes? → You skipped TDD. Start over.
-## Red Flags - STOP
+## The Rule
+
+No production code without a failing test first. Delete code written before the test — don't keep it as "reference".
-| Thought | Reality |
-|---|---|
-| "Too simple to test" | Simple code breaks. Test takes 30 seconds. |
-| "I'll test after" | Tests passing immediately prove nothing. |
-| "Tests after achieve the same goals" | Tests-after = "what does this do?" Tests-first = "what should this do?" |
-| "I already manually tested it" | Ad-hoc ≠ systematic. No record, can't re-run. |
-| "Deleting X hours of work is wasteful" | Sunk cost fallacy. Keeping unverified code = debt. |
-| "Keep as reference, write tests first" | You'll adapt it. That's testing after. Delete means delete. |
-| "Need to explore first" | Fine. Throw away exploration, then start with TDD. |
-| "Test is hard to write = skip it" | Hard to test = hard to use. Simplify the interface. |
-| "TDD will slow me down" | TDD is faster than debugging. Always. |
-| "This is different because..." | No it isn't. Write the test. |
## Integration
diff --git a/skills/testing-skills/SKILL.md b/skills/testing-skills/SKILL.md
index 7b20f2b..8a0b83b 100644
--- a/skills/testing-skills/SKILL.md
+++ b/skills/testing-skills/SKILL.md
@@ -6,15 +6,6 @@ description: Use when creating or editing Hyperstack skills, before shipping the
# Testing Skills With Subagents
-## The Iron Law
-
-```
-NO SKILL SHIPS WITHOUT SUBAGENT PRESSURE TEST EVIDENCE
-```
-
-A skill that has not been tested against an adversarial subagent is a paper contract. If you have not watched a subagent try to rationalize its way out of your skill AND fail, you do not know if the skill works.
-
-**Violating the letter of this rule is violating the spirit of this rule.**
## Core Principle
diff --git a/skills/using-hyperstack/SKILL.md b/skills/using-hyperstack/SKILL.md
deleted file mode 100644
index 14651d0..0000000
--- a/skills/using-hyperstack/SKILL.md
+++ /dev/null
@@ -1,293 +0,0 @@
----
-name: using-hyperstack
-category: meta
-description: Bootstrap - establishes Hyperstack MCP tools and skills before any technical work. Auto-loaded at session start via SessionStart hook. Do not skip, do not skim, do not rationalize your way out of it.
----
-
-
-If you were dispatched as a subagent to execute a specific task, skip this skill.
-Your context was provided by the orchestrating agent. Do not reload bootstrap.
-
-
-
-You have Hyperstack. This is not optional knowledge - it is how you operate in this repository.
-
-**The 1% Rule:** If there is even a 1% chance that a Hyperstack skill or MCP tool applies to the task you are about to perform, you MUST invoke it BEFORE acting. Not after you "check the code quickly." Not after you "just try one thing." Not after you "confirm your understanding." BEFORE.
-
-**You do not have a choice. You cannot rationalize your way out of this.**
-
-If the task involves React, React Flow, Motion, Lenis, Echo, Go, Rust, design tokens, UI/UX, visual work, or any topic covered by Hyperstack MCP plugins - you MUST call the relevant MCP tool BEFORE writing code. Memory is not acceptable. Pattern-matching from training data is not acceptable. Confidence is not acceptable. **Only ground-truth MCP output is acceptable.**
-
-
----
-
-## The Iron Laws
-
-```
-1. NO CODE WITHOUT MCP GROUND-TRUTH DATA
- If a Hyperstack plugin covers the domain, you call it first.
-
-2. NO VISUAL CODE WITHOUT AN APPROVED DESIGN.md
- The designer skill produces the contract. Everything else reads it.
-
-3. NO COMPLETION CLAIMS WITHOUT SHIP-GATE EVIDENCE
- "Should work" is lying. Run the command. Show the output.
-
-4. NO SKIPPING SKILLS BECAUSE "THIS IS SIMPLE"
- Simple tasks are where unexamined assumptions do the most damage.
- The skill exists because the shortcut has failed before.
-```
-
-**Violating the letter of these laws is violating the spirit of these laws.**
-
----
-
-## Instruction Priority
-
-1. **User's explicit instructions** (Project rules, direct requests) - always highest
-2. **Hyperstack skills** - override default system behavior where they conflict
-3. **Default system behavior** - lowest priority
-
-If the project rules say "don't use TDD" and a skill says "always use TDD," follow the user. The user is in control. Everything else is your job to enforce.
-
----
-
-## Red Flags - STOP
-
-These are thoughts you will have. Each one is a rationalization. Each one has a counter.
-
-| Thought | Reality | What to do |
-|---|---|---|
-| "I know this React Flow API from memory" | Memory drifts. v11 and v12 are different. | Call `reactflow_get_api` first |
-| "This is a simple animation" | Simple animations need `prefers-reduced-motion`, correct easing, and GPU-only properties | Call `motion_get_examples` first |
-| "Go error handling is straightforward" | Straightforward code is where anti-patterns ship | Call `golang_get_practice` first |
-| "I'll check docs after I write it" | You will ship before you check. Every time. | Docs BEFORE code. Always. |
-| "I know the OKLCH token pattern" | OKLCH has specific rules about alpha, chroma peaks, dark mode lightness | Call `design_tokens_get_procedure` first |
-| "This pattern looks common, I'll adapt it" | Adaptation hides drift | Call the MCP tool. Copy from ground truth. |
-| "The user is impatient, I'll skip the gate" | User impatience is not permission to ship slop | Gates are not optional |
-| "This doesn't count as visual work" | If it looks, moves, or is interacted with → visual | Invoke designer skill |
-| "I'll verify after I commit" | The verification step exists because "after" never comes | Verify BEFORE claim |
-| "Subagent said it's done" | Subagents lie | Check the diff. Run the tests. |
-| "Just this one time" | There is no "just this one time" | No exceptions |
-| "I'll write the test after" | No. Write it before. | `NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST` |
-| "I already checked earlier in this conversation" | Check again. State drifts. | Fresh verification every time |
-| "The file is too small to need a design" | Small files with wrong decisions ship to production | Every decision gets a gate |
-
----
-
-## Layer 1: MCP Tools (Ground-Truth Data)
-
-Call these BEFORE writing any code for these stacks. **Memory is not acceptable.**
-
-| Namespace | Stack | Must-call-first tools |
-|---|---|---|
-| `designer_*` | Design decision engine | `designer_resolve_intent`, `designer_get_personality`, `designer_get_preset`, `designer_get_page_template`, `designer_get_font_pairing`, `designer_get_anti_patterns` |
-| `design_tokens_*` | Design token systems | `design_tokens_generate`, `design_tokens_get_category`, `design_tokens_get_gotchas` |
-| `ui_ux_*` | UI/UX principles | `ui_ux_get_principle`, `ui_ux_get_component_pattern`, `ui_ux_get_gotchas` |
-| `shadcn_*` (**only if shadcn chosen**) | shadcn/ui Base UI edition | `shadcn_get_rules` (first), `shadcn_get_composition`, `shadcn_get_component`, `shadcn_get_snippet`, `shadcn_list_components` |
-| `reactflow_*` | React Flow v12 | `reactflow_get_api`, `reactflow_search_docs`, `reactflow_get_pattern` |
-| `motion_*` | Motion for React v12 | `motion_get_api`, `motion_get_examples`, `motion_get_transitions` |
-| `lenis_*` | Lenis smooth scroll | `lenis_get_api`, `lenis_generate_setup`, `lenis_get_pattern` |
-| `react_*` | React 19 / Next.js | `react_get_pattern`, `react_get_constraints`, `react_search_docs` |
-| `echo_*` | Echo (Go HTTP) | `echo_get_recipe`, `echo_get_middleware`, `echo_decision_matrix` |
-| `golang_*` | Go best practices | `golang_get_practice`, `golang_get_pattern`, `golang_get_antipatterns` |
-| `rust_*` | Rust practices | `rust_get_practice`, `rust_cheatsheet`, `rust_search_docs` |
-
-### MCP Degraded Mode
-
-If MCP tools fail or are unavailable:
-1. Tell the user explicitly: "Hyperstack MCP server is unavailable - my answers will be less precise and I am flagging them as uncertain."
-2. Fall back to training data but FLAG every answer as uncertain.
-3. Never silently answer as if ground-truth data was used.
-4. Do not invent API shapes.
-
----
-
-## Layer 2: Skills (Engineering Process)
-
-Use the `Skill` tool to load these before the relevant task type.
-
-**Full skill index:** See `skills/INDEX.md` - all skills grouped by category (core / domain / meta). Regenerate with `npm run skills:index` after adding or editing any skill.
-
-### Announcement Iron Law
-
-```
-BEFORE invoking any Hyperstack skill, announce it:
-"Using hyperstack:[skill-name] - [one-line purpose]"
-```
-
-This is non-negotiable. Silent skill invocations are invisible to the user and cannot be audited. **If you invoke a skill silently, you are lying by omission.**
-
-### Workflow Skills (invoke in this order for feature work)
-
-| Skill | When to invoke | Gate type |
-|---|---|---|
-| `hyperstack:blueprint` | Before any feature build - MCP survey, design gate, negative doubt | **HARD GATE** |
-| `hyperstack:designer` | Before any visual/UX work - produces DESIGN.md contract | **HARD GATE** |
-| `hyperstack:forge-plan` | After design approval - MCP-verified implementation plan | Requires approved design |
-| `hyperstack:run-plan` | Have an existing plan - validate then execute | Requires plan |
-| `hyperstack:engineering-discipline` | During execution - Senior SDE phase gates | Phase gates |
-| `hyperstack:ship-gate` | Before any completion claim - evidence required | **HARD GATE** |
-| `hyperstack:deliver` | After all tasks complete - final verification and delivery | Gate |
-
-### Execution Skills (invoke during implementation)
-
-| Skill | When to invoke |
-|---|---|
-| `hyperstack:autonomous-mode` | Full autonomous execution - runs end-to-end, only stops on failure |
-| `hyperstack:subagent-ops` | Plans with independent tasks - fresh agent per task, two-stage review |
-| `hyperstack:test-first` | Before writing any implementation code - red-green-refactor |
-| `hyperstack:worktree-isolation` | Before feature work - clean workspace isolation |
-| `hyperstack:code-review` | After completing tasks - dispatch reviewer subagent |
-| `hyperstack:parallel-dispatch` | 2+ independent failures or tasks - concurrent agent dispatch |
-
-### Support Skills (invoke when the situation calls for it)
-
-| Skill | When to invoke |
-|---|---|
-| `hyperstack:designer` | Before any visual/UX work - produces DESIGN.md |
-| `hyperstack:debug-discipline` | Any bug or unexpected behaviour - root cause first |
-| `hyperstack:behaviour-analysis` | UI/UX audits, state machine correctness |
-| `hyperstack:design-patterns-skill` | Selecting the right abstraction or design pattern |
-| `hyperstack:security-review` | OWASP audits, API and infrastructure security |
-| `hyperstack:readme-writer` | Evidence-based documentation |
-
-### Workflow Chain
-
-```
-New work: blueprint → [designer if visual] → forge-plan → choose execution mode → ship-gate → deliver
- │ │
- │ └── produces DESIGN.md (input to forge-plan)
- │
-Existing: run-plan ──┤
- │
- ├→ autonomous-mode (full auto, stops only on failure)
- ├→ subagent-ops (fresh agent per task, two-stage review)
- └→ engineering-discipline (manual, human checkpoints)
-
-Before execution: worktree-isolation (clean workspace)
-Debugging: debug-discipline → parallel-dispatch (if independent failures)
-```
-
-**Visual work branch:** If the task changes how something looks, feels, moves, or is interacted with, `blueprint` routes through `designer` BEFORE `forge-plan`. The DESIGN.md produced by designer becomes the input spec for forge-plan.
-
-**Skip designer when:** pure backend, single CSS bug fix, performance optimization with no visual change, infra/DevOps, documentation-only.
-
-For non-trivial tasks, follow the chain in order. Do not skip steps.
-
-**Platform tool equivalences:** See `skills/using-hyperstack/references/` for tool name mappings per harness.
-
----
-
-## Internal Agent Harness
-
-Hyperstack now has internal roles. These roles are **internal and auto-called**.
-Users do not invoke them directly. The bootstrap and harness choose the correct
-role based on the request and lifecycle state.
-
-V1 keeps the current skills and MCP plugins as the execution substrate. The role
-harness is layered on top of them; it does not replace them yet.
-
-## Role Registry
-
-- `hyper` - conductor, classifier, gatekeeper, verifier, and delivery owner
-- `website-builder` - first specialist for website-facing design and
- implementation work
-
-## Routing Summary
-
-- Every request enters through `hyper`
-- `hyper` inspects the workspace first: package manifests, dependency signals,
- and likely core files for the affected surface
-- `hyper -> website-builder` for website-facing work: landing pages, dashboards,
- marketing pages, redesigns, page structure, CTA hierarchy, form friction,
- trust signals, and website-experience-heavy UI work
-- `website-builder -> hyper` after specialist output is ready for review and
- verification
-- If classification is ambiguous, stay in `hyper`
-
-## Allowed Transitions
-
-- `user request -> hyper`
-- `hyper -> website-builder`
-- `website-builder -> hyper`
-- `hyper -> existing Hyperstack skills/plugins`
-- `hyper -> verification and delivery gates`
-
-## Disallowed Transitions
-
-- `user request -> website-builder`
-- `website-builder -> ship`
-- `website-builder -> deliver`
-- `website-builder` claiming final completion directly
-
----
-
-## The Rationalization Catalog (Read Before Every Session)
-
-These are the exact thoughts you will have when you want to skip a skill. Every one is a bug in your reasoning. Every one has been written down because someone (probably you in a past session) used it to ship bad code.
-
-### Skill-skipping rationalizations
-
-| Thought | Why it's wrong |
-|---|---|
-| "This is just a question, not a task" | Questions are tasks. They lead to answers that become code. Check for skills. |
-| "I need context first, then I'll use skills" | Skills tell you HOW to gather context. Check first. |
-| "Let me quickly look at the files" | "Quickly" is the word you use when you're skipping the gate. |
-| "I remember this pattern" | Remembering is not the same as using the current tool output. Memory drifts. |
-| "This doesn't need a formal skill, it's obvious" | If it's obvious, invoking the skill takes 10 seconds. Do it. |
-| "The skill is overkill for this" | You don't get to decide what's overkill. The skill exists because the shortcut failed. |
-| "I'll just do one thing first then invoke the skill" | No. Skill invocation comes BEFORE the one thing. |
-| "I already have a mental model of this" | Mental models drift faster than you think. Refresh it from MCP. |
-
-### Verification-skipping rationalizations
-
-| Thought | Why it's wrong |
-|---|---|
-| "Should work now" | "Should" is not evidence. Run the command. |
-| "I'm confident in this change" | Confidence is not evidence. Run the command. |
-| "Minor change, no need to recheck" | Minor changes cause regressions. Run the command. |
-| "Tests were passing before my change" | Irrelevant. Run them again now. |
-| "The subagent reported success" | Subagents lie. Check the diff yourself. |
-| "I followed the pattern correctly" | Following the pattern is not the same as the pattern working. Run the command. |
-| "I'll verify after I push" | After you push it's in CI. Verify BEFORE. |
-| "Just this once" | There is no "just this once." |
-| "I'm tired" | Exhaustion is not an excuse. Stop and rest if you must, don't ship unverified. |
-| "Partial check is enough" | Partial verification is theater. Do the full check. |
-
-### The "I'm different" rationalization
-
-| Thought | Why it's wrong |
-|---|---|
-| "These rules are for general AI, I understand them better" | No. They were written for past sessions of you that also thought they understood. |
-| "The spirit of the rule allows this" | The letter of the rule is the spirit of the rule. |
-| "This specific case isn't covered" | Every case is covered by "use the skill or don't proceed." |
-| "I can adapt the process to be faster" | Fast is for after discipline is internalized. You are not there yet. |
-
----
-
-## The One Rule That Governs All Rules
-
-> **If there is even a 1% chance a Hyperstack skill or MCP tool applies to what you are about to do, you ABSOLUTELY MUST invoke it first.**
-
-Not after. Not alongside. FIRST.
-
-If the skill turns out not to apply once you've loaded it, you don't have to use it. But you MUST check.
-
----
-
-## Final Check Before Any Response
-
-Before you respond to the user's message, confirm:
-
-1. [ ] Did I check whether any Hyperstack skill applies to this task? (1% rule)
-2. [ ] Did I call any relevant MCP tool for ground-truth data? (memory is not acceptable)
-3. [ ] If this involves visual work, did I invoke designer BEFORE writing any code?
-4. [ ] If I'm claiming something is done, did I run the verification command THIS message?
-5. [ ] Did I announce every skill invocation with the exact format?
-
-If any answer is no, **stop and fix it before responding.**
-
----
-
-**You have Hyperstack. Use it.**
diff --git a/skills/worktree-isolation/SKILL.md b/skills/worktree-isolation/SKILL.md
index 22f0692..da855eb 100644
--- a/skills/worktree-isolation/SKILL.md
+++ b/skills/worktree-isolation/SKILL.md
@@ -109,14 +109,6 @@ git worktree remove
# Leave worktree in place, report its location
```
-## Red Flags - STOP
-
-| Thought | Reality |
-|---|---|
-| "I'll just work on the main branch" | Dirty state → mysterious failures. Isolate. |
-| "Worktree setup is overhead" | 30 seconds of setup prevents hours of state debugging. |
-| "I'll skip baseline tests" | Won't know if failures are yours or pre-existing. |
-| "The directory doesn't need to be ignored" | One `git add .` and the worktree is in your repo. |
## Integration
diff --git a/src/adapters/local-tools/index.ts b/src/adapters/local-tools/index.ts
new file mode 100644
index 0000000..a960d0b
--- /dev/null
+++ b/src/adapters/local-tools/index.ts
@@ -0,0 +1,24 @@
+import { readFileSync } from "node:fs";
+import { dirname, resolve } from "node:path";
+import { fileURLToPath, pathToFileURL } from "node:url";
+import { invokeRegisteredTool } from "../../engine/tool-bridge.js";
+
+const adapterDir = dirname(fileURLToPath(import.meta.url));
+const repoRoot = resolve(adapterDir, "../../..");
+const registryPath = resolve(repoRoot, "generated", "tool-index", "local-tool-registry.json");
+
+function readRegistry(): Record {
+ return JSON.parse(readFileSync(registryPath, "utf8")) as Record;
+}
+
+export async function invokeLocalTool(name: string, args: Record) {
+ const registry = readRegistry();
+ const importPath = registry[name];
+ if (!importPath) {
+ throw new Error(`Unknown local tool: ${name}`);
+ }
+
+ const resolvedImportPath = resolve(repoRoot, importPath);
+ const module = await import(pathToFileURL(resolvedImportPath).href);
+ return invokeRegisteredTool(module.register, args);
+}
diff --git a/src/cli.ts b/src/cli.ts
new file mode 100644
index 0000000..6bd102a
--- /dev/null
+++ b/src/cli.ts
@@ -0,0 +1,66 @@
+import { validateArtifactPayload } from "./engine/artifact-validator.js";
+import { routeRequest } from "./engine/router.js";
+import { loadTopology } from "./engine/topology-loader.js";
+import { invokeLocalTool } from "./adapters/local-tools/index.js";
+
+async function main() {
+ const [command, toolName, flag, json] = process.argv.slice(2);
+ const topology = loadTopology(process.cwd());
+
+ if (command === "tool") {
+ if (!toolName || flag !== "--json" || !json) {
+ process.stderr.write('Usage: hyperstack tool --json \'{"key":"value"}\'\n');
+ process.exit(1);
+ }
+
+ const args = JSON.parse(json) as Record;
+ const result = await invokeLocalTool(toolName, args);
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
+ process.exit(0);
+ }
+
+ if (command === "route") {
+ if (toolName !== "--json" || !flag) {
+ process.stderr.write("Usage: hyperstack route --json '{...}'\n");
+ process.exit(1);
+ }
+
+ const input = JSON.parse(flag) as {
+ requestId: string;
+ domainTargets: string[];
+ capabilityTargets: string[];
+ workspaceInventory: { projectMode: "greenfield" | "existing"; existingPatterns: string[] };
+ changeClassification: string;
+ };
+
+ process.stdout.write(`${JSON.stringify(routeRequest(topology, input), null, 2)}\n`);
+ process.exit(0);
+ }
+
+ if (command === "artifact" && toolName === "validate") {
+ const artifactId = flag;
+ const jsonFlag = process.argv[5];
+ const jsonPayload = process.argv[6];
+
+ if (!artifactId || jsonFlag !== "--json" || !jsonPayload) {
+ process.stderr.write("Usage: hyperstack artifact validate --json '{...}'\n");
+ process.exit(1);
+ }
+
+ const payload = JSON.parse(jsonPayload) as Record;
+ process.stdout.write(`${JSON.stringify(validateArtifactPayload(topology, artifactId, payload), null, 2)}\n`);
+ process.exit(0);
+ }
+
+ process.stderr.write(
+ "Usage: hyperstack tool --json '{...}'\n" +
+ " or: hyperstack route --json '{...}'\n" +
+ " or: hyperstack artifact validate --json '{...}'\n",
+ );
+ process.exit(1);
+}
+
+main().catch((error) => {
+ process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
+ process.exit(1);
+});
diff --git a/src/engine/artifact-loader.ts b/src/engine/artifact-loader.ts
new file mode 100644
index 0000000..c137985
--- /dev/null
+++ b/src/engine/artifact-loader.ts
@@ -0,0 +1,9 @@
+import type { ArtifactContract, LoadedTopology } from "./contracts.js";
+
+export function getArtifactContract(topology: LoadedTopology, artifactId: string): ArtifactContract {
+ const artifact = topology.artifacts.find((entry) => entry.id === artifactId);
+ if (!artifact) {
+ throw new Error(`Unknown artifact contract: ${artifactId}`);
+ }
+ return artifact;
+}
diff --git a/src/engine/artifact-validator.ts b/src/engine/artifact-validator.ts
new file mode 100644
index 0000000..d8a43ee
--- /dev/null
+++ b/src/engine/artifact-validator.ts
@@ -0,0 +1,18 @@
+import type { LoadedTopology } from "./contracts.js";
+import { getArtifactContract } from "./artifact-loader.js";
+
+export function validateArtifactPayload(
+ topology: LoadedTopology,
+ artifactId: string,
+ payload: Record,
+) {
+ const contract = getArtifactContract(topology, artifactId);
+ const missingFields = contract.requiredFields.filter((field) => !(field in payload));
+
+ return {
+ ok: missingFields.length === 0,
+ artifactId,
+ proofMode: contract.proofMode,
+ missingFields,
+ };
+}
diff --git a/src/engine/contracts.ts b/src/engine/contracts.ts
new file mode 100644
index 0000000..5e1ee59
--- /dev/null
+++ b/src/engine/contracts.ts
@@ -0,0 +1,66 @@
+export interface TopologyRoot {
+ version: 1;
+ defaultTransport: "local-tools";
+ entryAgent: string;
+ domains: string[];
+ agents: string[];
+ bundles: string[];
+ artifacts: string[];
+}
+
+export interface DomainPolicy {
+ id: string;
+ writePolicy: "constrained" | "direct" | "policy_only";
+ completionProof: string;
+ truthBundles: string[];
+ requiredGates: string[];
+ optionalGates: string[];
+ forbiddenBundles: string[];
+}
+
+export interface AgentPolicy {
+ id: string;
+ kind: "orchestrator" | "specialist" | "cross-domain";
+ domains: string[];
+ allowedSkills: string[];
+ allowedBundles: string[];
+ forbiddenBundles: string[];
+ handoffIn: string;
+ handoffOut: string;
+ completionProof: string;
+}
+
+export interface BundlePolicy {
+ id: string;
+ domain: string;
+ capabilities: string[];
+ sources: string[];
+ toolPrefixes: string[];
+ outputContracts: string[];
+}
+
+export interface ArtifactContract {
+ id: string;
+ requiredFields: string[];
+ proofMode: string;
+}
+
+export interface RouteDefaults {
+ defaultAgent: string;
+ requiresWorkspaceInventory: boolean;
+ domainPreference: Record;
+ crossDomainAgent: string;
+ designContractRequiredWhen: string[];
+ strictestProofOrder: string[];
+}
+
+export interface LoadedTopology {
+ version: 1;
+ defaultTransport: "local-tools";
+ entryAgent: string;
+ domains: DomainPolicy[];
+ agents: AgentPolicy[];
+ bundles: BundlePolicy[];
+ artifacts: ArtifactContract[];
+ routeDefaults: RouteDefaults;
+}
diff --git a/src/engine/injector.ts b/src/engine/injector.ts
new file mode 100644
index 0000000..cda1c20
--- /dev/null
+++ b/src/engine/injector.ts
@@ -0,0 +1,10 @@
+import type { BundlePolicy } from "./contracts.js";
+
+export function buildInjectionSlice(bundle: BundlePolicy, capability: string) {
+ return {
+ bundle: bundle.id,
+ capability,
+ sources: bundle.sources,
+ toolPrefixes: bundle.toolPrefixes,
+ };
+}
diff --git a/src/engine/navigation.ts b/src/engine/navigation.ts
new file mode 100644
index 0000000..231f8ee
--- /dev/null
+++ b/src/engine/navigation.ts
@@ -0,0 +1,17 @@
+import type { BundlePolicy, LoadedTopology } from "./contracts.js";
+
+export function getBundle(topology: LoadedTopology, bundleId: string): BundlePolicy {
+ const bundle = topology.bundles.find((entry) => entry.id === bundleId);
+ if (!bundle) {
+ throw new Error(`Unknown bundle: ${bundleId}`);
+ }
+ return bundle;
+}
+
+export function listAgentRouting(topology: LoadedTopology) {
+ return topology.agents.map((agent) => ({
+ id: agent.id,
+ allowedBundles: agent.allowedBundles,
+ forbiddenBundles: agent.forbiddenBundles,
+ }));
+}
diff --git a/src/engine/policy.ts b/src/engine/policy.ts
new file mode 100644
index 0000000..a3f8ee5
--- /dev/null
+++ b/src/engine/policy.ts
@@ -0,0 +1,38 @@
+import type { AgentPolicy, BundlePolicy, DomainPolicy, LoadedTopology } from "./contracts.js";
+
+export function getAgent(topology: LoadedTopology, agentId: string): AgentPolicy {
+ const agent = topology.agents.find((entry) => entry.id === agentId);
+ if (!agent) {
+ throw new Error(`Unknown agent: ${agentId}`);
+ }
+ return agent;
+}
+
+export function getBundleByCapability(topology: LoadedTopology, capability: string): BundlePolicy {
+ const bundle = topology.bundles.find((entry) => entry.capabilities.includes(capability));
+ if (!bundle) {
+ throw new Error(`No bundle found for capability: ${capability}`);
+ }
+ return bundle;
+}
+
+export function getDomain(topology: LoadedTopology, domainId: string): DomainPolicy {
+ const domain = topology.domains.find((entry) => entry.id === domainId);
+ if (!domain) {
+ throw new Error(`Unknown domain: ${domainId}`);
+ }
+ return domain;
+}
+
+export function getStrictestProofMode(order: string[], proofModes: string[]): string {
+ const ranked = proofModes
+ .map((mode) => ({ mode, index: order.indexOf(mode) }))
+ .filter((entry) => entry.index >= 0)
+ .sort((left, right) => right.index - left.index);
+
+ if (ranked.length === 0) {
+ throw new Error(`No proof mode could be ranked from: ${proofModes.join(", ")}`);
+ }
+
+ return ranked[0].mode;
+}
diff --git a/src/engine/resolver.ts b/src/engine/resolver.ts
new file mode 100644
index 0000000..6d0789a
--- /dev/null
+++ b/src/engine/resolver.ts
@@ -0,0 +1,21 @@
+import type { LoadedTopology } from "./contracts.js";
+import { getAgent, getBundleByCapability, getDomain } from "./policy.js";
+
+export function resolveCapabilityContext(
+ topology: LoadedTopology,
+ input: { agentId: string; capability: string },
+) {
+ const agent = getAgent(topology, input.agentId);
+ const bundle = getBundleByCapability(topology, input.capability);
+ const domain = getDomain(topology, bundle.domain);
+
+ if (!agent.allowedBundles.includes(bundle.id)) {
+ throw new Error(`Agent ${agent.id} attempted forbidden bundle ${bundle.id}`);
+ }
+
+ if (agent.forbiddenBundles.includes(bundle.id) || domain.forbiddenBundles.includes(bundle.id)) {
+ throw new Error(`Agent ${agent.id} attempted forbidden bundle ${bundle.id}`);
+ }
+
+ return { agent, bundle, domain };
+}
diff --git a/src/engine/router.ts b/src/engine/router.ts
new file mode 100644
index 0000000..346ea95
--- /dev/null
+++ b/src/engine/router.ts
@@ -0,0 +1,59 @@
+import type { LoadedTopology } from "./contracts.js";
+import { getAgent, getDomain, getStrictestProofMode } from "./policy.js";
+
+function shouldRequireDesignContract(input: {
+ changeClassification: string;
+ workspaceInventory: { projectMode: "greenfield" | "existing"; existingPatterns: string[] };
+}): boolean {
+ if (input.changeClassification !== "frontend_visual") {
+ return false;
+ }
+
+ return (
+ input.workspaceInventory.projectMode === "greenfield" ||
+ input.workspaceInventory.existingPatterns.length === 0
+ );
+}
+
+export function routeRequest(
+ topology: LoadedTopology,
+ input: {
+ requestId: string;
+ domainTargets: string[];
+ capabilityTargets: string[];
+ workspaceInventory: {
+ projectMode: "greenfield" | "existing";
+ existingPatterns: string[];
+ };
+ changeClassification: string;
+ },
+) {
+ const uniqueDomains = [...new Set(input.domainTargets)];
+ const routeAgentId =
+ uniqueDomains.length > 1
+ ? topology.routeDefaults.crossDomainAgent
+ : topology.routeDefaults.domainPreference[uniqueDomains[0] ?? "shared"] ?? topology.routeDefaults.defaultAgent;
+
+ const agent = getAgent(topology, routeAgentId);
+ const proofMode = getStrictestProofMode(
+ topology.routeDefaults.strictestProofOrder,
+ uniqueDomains.map((domainId) => getDomain(topology, domainId).completionProof),
+ );
+
+ const requiredArtifacts = ["workspace_inventory", "task_handoff"];
+ const requiresDesignContract = shouldRequireDesignContract(input);
+
+ if (requiresDesignContract) {
+ requiredArtifacts.push("design_contract");
+ }
+
+ return {
+ requestId: input.requestId,
+ agent,
+ proofMode,
+ domains: uniqueDomains,
+ capabilityTargets: input.capabilityTargets,
+ requiredArtifacts,
+ requiresDesignContract,
+ };
+}
diff --git a/src/engine/skill-enforcer.ts b/src/engine/skill-enforcer.ts
new file mode 100644
index 0000000..876fc2b
--- /dev/null
+++ b/src/engine/skill-enforcer.ts
@@ -0,0 +1,7 @@
+import type { AgentPolicy } from "./contracts.js";
+
+export function assertSkillAllowedForAgent(agent: AgentPolicy, skillName: string): void {
+ if (!agent.allowedSkills.includes(skillName)) {
+ throw new Error(`Skill ${skillName} is not allowed for agent ${agent.id}`);
+ }
+}
diff --git a/src/engine/tool-bridge.ts b/src/engine/tool-bridge.ts
new file mode 100644
index 0000000..cf5919f
--- /dev/null
+++ b/src/engine/tool-bridge.ts
@@ -0,0 +1,27 @@
+import assert from "node:assert/strict";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
+
+type ToolResult = {
+ content?: Array<{ type?: string; text?: string }>;
+ isError?: boolean;
+};
+
+type ToolHandler = (args: Record) => Promise | ToolResult;
+
+export async function invokeRegisteredTool(
+ register: (server: McpServer) => void,
+ args: Record,
+): Promise {
+ let capturedHandler: ToolHandler | undefined;
+
+ const server = {
+ tool(_name: string, _description: string, _schema: unknown, handler: ToolHandler) {
+ capturedHandler = handler;
+ },
+ } as unknown as McpServer;
+
+ register(server);
+
+ assert.ok(capturedHandler, "tool registration did not capture a handler");
+ return await capturedHandler(args);
+}
diff --git a/src/engine/topology-loader.ts b/src/engine/topology-loader.ts
new file mode 100644
index 0000000..7e5bc0c
--- /dev/null
+++ b/src/engine/topology-loader.ts
@@ -0,0 +1,170 @@
+import { readFileSync } from "node:fs";
+import { join } from "node:path";
+import YAML from "yaml";
+import type {
+ AgentPolicy,
+ ArtifactContract,
+ BundlePolicy,
+ DomainPolicy,
+ LoadedTopology,
+ RouteDefaults,
+ TopologyRoot,
+} from "./contracts.js";
+
+interface RootDocument {
+ version: 1;
+ default_transport: "local-tools";
+ entry_agent: string;
+ domains: string[];
+ agents: string[];
+ bundles: string[];
+ artifacts: string[];
+}
+
+interface DomainDocument {
+ id: string;
+ write_policy: DomainPolicy["writePolicy"];
+ completion_proof: string;
+ truth_bundles: string[];
+ required_gates: string[];
+ optional_gates: string[];
+ forbidden_bundles: string[];
+}
+
+interface AgentDocument {
+ id: string;
+ kind: AgentPolicy["kind"];
+ domains: string[];
+ allowed_skills: string[];
+ allowed_bundles: string[];
+ forbidden_bundles: string[];
+ handoff_in: string;
+ handoff_out: string;
+ completion_proof: string;
+}
+
+interface BundleDocument {
+ id: string;
+ domain: string;
+ capabilities: string[];
+ sources: string[];
+ tool_prefixes: string[];
+ output_contracts: string[];
+}
+
+interface ArtifactDocument {
+ id: string;
+ required_fields: string[];
+ proof_mode: string;
+}
+
+interface RouteDefaultsDocument {
+ default_agent: string;
+ requires_workspace_inventory: boolean;
+ domain_preference: Record;
+ cross_domain_agent: string;
+ design_contract_required_when: string[];
+ strictest_proof_order: string[];
+}
+
+function readYaml(filePath: string): T {
+ return YAML.parse(readFileSync(filePath, "utf8")) as T;
+}
+
+function mapRootDocument(root: RootDocument): TopologyRoot {
+ return {
+ version: root.version,
+ defaultTransport: root.default_transport,
+ entryAgent: root.entry_agent,
+ domains: root.domains,
+ agents: root.agents,
+ bundles: root.bundles,
+ artifacts: root.artifacts,
+ };
+}
+
+function mapDomainDocument(domain: DomainDocument): DomainPolicy {
+ return {
+ id: domain.id,
+ writePolicy: domain.write_policy,
+ completionProof: domain.completion_proof,
+ truthBundles: domain.truth_bundles,
+ requiredGates: domain.required_gates,
+ optionalGates: domain.optional_gates,
+ forbiddenBundles: domain.forbidden_bundles,
+ };
+}
+
+function mapAgentDocument(agent: AgentDocument): AgentPolicy {
+ return {
+ id: agent.id,
+ kind: agent.kind,
+ domains: agent.domains,
+ allowedSkills: agent.allowed_skills,
+ allowedBundles: agent.allowed_bundles,
+ forbiddenBundles: agent.forbidden_bundles,
+ handoffIn: agent.handoff_in,
+ handoffOut: agent.handoff_out,
+ completionProof: agent.completion_proof,
+ };
+}
+
+function mapBundleDocument(bundle: BundleDocument): BundlePolicy {
+ return {
+ id: bundle.id,
+ domain: bundle.domain,
+ capabilities: bundle.capabilities,
+ sources: bundle.sources,
+ toolPrefixes: bundle.tool_prefixes,
+ outputContracts: bundle.output_contracts,
+ };
+}
+
+function mapArtifactDocument(doc: ArtifactDocument): ArtifactContract {
+ return {
+ id: doc.id,
+ requiredFields: doc.required_fields,
+ proofMode: doc.proof_mode,
+ };
+}
+
+function mapRouteDefaultsDocument(doc: RouteDefaultsDocument): RouteDefaults {
+ return {
+ defaultAgent: doc.default_agent,
+ requiresWorkspaceInventory: doc.requires_workspace_inventory,
+ domainPreference: doc.domain_preference,
+ crossDomainAgent: doc.cross_domain_agent,
+ designContractRequiredWhen: doc.design_contract_required_when,
+ strictestProofOrder: doc.strictest_proof_order,
+ };
+}
+
+export function loadTopology(repoRoot: string): LoadedTopology {
+ const root = mapRootDocument(readYaml(join(repoRoot, "topology", "manifest.yaml")));
+ const domains = root.domains.map((id) =>
+ mapDomainDocument(readYaml(join(repoRoot, "topology", "domains", `${id}.yaml`))),
+ );
+ const agents = root.agents.map((id) =>
+ mapAgentDocument(readYaml(join(repoRoot, "topology", "agents", `${id}.yaml`))),
+ );
+ const bundles = root.bundles.map((id) =>
+ mapBundleDocument(readYaml(join(repoRoot, "topology", "bundles", `${id}.yaml`))),
+ );
+ const artifacts = root.artifacts.map((id) =>
+ mapArtifactDocument(readYaml(join(repoRoot, "topology", "artifacts", `${id}.yaml`))),
+ );
+ const routeDefaults = mapRouteDefaultsDocument(
+ readYaml(join(repoRoot, "topology", "routes", "defaults.yaml")),
+ );
+
+ return {
+ version: root.version,
+ defaultTransport: root.defaultTransport,
+ entryAgent: root.entryAgent,
+ domains,
+ agents,
+ bundles,
+ artifacts,
+ routeDefaults,
+ };
+}
diff --git a/src/internal/compile-runtime-context.ts b/src/internal/compile-runtime-context.ts
index 2c24897..a8241e7 100644
--- a/src/internal/compile-runtime-context.ts
+++ b/src/internal/compile-runtime-context.ts
@@ -10,7 +10,7 @@ const pluginRoot = join(scriptDir, "../..");
try {
const artifacts = compileContextArtifacts(pluginRoot);
- const source = readFileSync(join(pluginRoot, "skills", "using-hyperstack", "SKILL.md"), "utf8");
+ const source = readFileSync(join(pluginRoot, "skills", "hyperstack", "SKILL.md"), "utf8");
const { stats } = compileUsingHyperstackBootstrap(source);
process.stdout.write(
diff --git a/src/internal/context-compiler.ts b/src/internal/context-compiler.ts
index b771a33..c27de98 100644
--- a/src/internal/context-compiler.ts
+++ b/src/internal/context-compiler.ts
@@ -44,9 +44,9 @@ const REQUIRED_BOOTSTRAP_MARKERS = [
"MCP unavailable",
"announce it",
"hyper",
- "website-builder",
+ "frontend-builder",
"auto-called",
- "hyper -> website-builder",
+ "hyper -> frontend-builder",
];
function stripFrontmatter(source: string): string {
@@ -62,17 +62,55 @@ function extractTaggedBlock(source: string, tag: string): string {
return match[1].trim();
}
-function extractSection(source: string, heading: string): string {
+function parseHeading(line: string): { level: number; text: string } | null {
+ const match = line.trim().match(/^(#{1,6})\s+(.*)$/);
+ if (!match) {
+ return null;
+ }
+
+ return {
+ level: match[1].length,
+ text: match[2].trim(),
+ };
+}
+
+function matchesHeadingText(text: string, heading: string): boolean {
+ return (
+ text === heading ||
+ text.startsWith(`${heading} `) ||
+ text.startsWith(`${heading}:`) ||
+ text.startsWith(`${heading} -`)
+ );
+}
+
+function extractSection(source: string, heading: string | string[]): string {
const lines = source.split("\n");
- const targetHeading = `## ${heading}`;
- const startIndex = lines.findIndex((line) => line.trim() === targetHeading);
+ const headings = Array.isArray(heading) ? heading : [heading];
+ let startIndex = -1;
+ let startLevel = 0;
+
+ for (let index = 0; index < lines.length; index += 1) {
+ const headingInfo = parseHeading(lines[index] ?? "");
+ if (!headingInfo) {
+ continue;
+ }
+
+ if (headings.some((candidate) => matchesHeadingText(headingInfo.text, candidate))) {
+ startIndex = index;
+ startLevel = headingInfo.level;
+ break;
+ }
+ }
+
if (startIndex === -1) {
- throw new Error(`Could not extract section "${heading}" from source`);
+ throw new Error(`Could not extract section "${headings.join(", ")}" from source`);
}
+
const contentLines: string[] = [];
for (let index = startIndex + 1; index < lines.length; index += 1) {
const line = lines[index] ?? "";
- if (line.startsWith("## ")) {
+ const headingInfo = parseHeading(line);
+ if (headingInfo && headingInfo.level <= startLevel) {
break;
}
contentLines.push(line);
@@ -115,7 +153,7 @@ function compactNamespaceTable(section: string): string[] {
}
function compactWorkflowTables(source: string): string[] {
- const workflowSection = extractSection(source, "Layer 2: Skills (Engineering Process)");
+ const workflowSection = extractSection(source, ["Layer 2: Skills (Engineering Process)", "Layer 2: When to Invoke Skills"]);
const rows = parseMarkdownTable(workflowSection).filter((row) => row.cells.length >= 2);
return rows
@@ -123,8 +161,40 @@ function compactWorkflowTables(source: string): string[] {
.map((row) => `- ${row.cells[0]}: ${row.cells[1]}`);
}
+function extractInternalAgents(source: string): string[] {
+ const section = extractSection(source, [
+ "Layer 3: Agents (Orchestration & Routing)",
+ "Layer 3: Orchestration (Agents)",
+ "Role Registry Details",
+ "Role Registry",
+ "Internal Agents",
+ ]);
+
+ const lines = section.split("\n").map((line) => line.trim()).filter(Boolean);
+ const agents = parseMarkdownTable(section).filter((row) => row.cells.length >= 2);
+ const agentNames = agents.map((row) => row.cells[0].replace(/^`|`$/g, ""));
+ const output: string[] = [];
+
+ if (lines.some((line) => /auto-(invoked|called)/i.test(line))) {
+ output.push("- Internal roles are auto-called, not user-facing.");
+ }
+
+ if (agentNames.includes("hyper") && agentNames.includes("frontend-builder")) {
+ output.push("- hyper -> frontend-builder");
+ }
+
+ output.push(
+ ...agents.map((row) => {
+ const name = row.cells[0].replace(/^`|`$/g, "");
+ return `- \`${name}\`: ${row.cells[1]}`;
+ }),
+ );
+
+ return output;
+}
+
function extractFinalCheck(source: string): string[] {
- const finalSection = extractSection(source, "Final Check Before Any Response");
+ const finalSection = extractSection(source, ["Final Check Before Any Response", "Final Response Check"]);
return finalSection
.split("\n")
.map((line) => line.trim())
@@ -133,7 +203,7 @@ function extractFinalCheck(source: string): string[] {
}
function extractRedFlags(source: string): string[] {
- const redFlagsSection = extractSection(source, "Red Flags - STOP");
+ const redFlagsSection = extractSection(source, ["Red Flags - STOP", "High-Signal Red Flags"]);
const rows = parseMarkdownTable(redFlagsSection).filter((row) => row.cells.length >= 2);
return rows.slice(0, 6).map((row) => `- ${row.cells[0]} -> ${row.cells[1]}`);
}
@@ -167,15 +237,13 @@ export function compileUsingHyperstackBootstrap(source: string): { content: stri
const namespaces = compactNamespaceTable(extractSection(body, "Layer 1: MCP Tools (Ground-Truth Data)"));
const workflowSkills = compactWorkflowTables(body);
const instructionPriority = extractInstructionPriority(body);
- const roleRegistry = extractSimpleBullets(extractSection(body, "Role Registry"));
- const routingSummary = extractSimpleBullets(extractSection(body, "Routing Summary"));
- const allowedTransitions = extractSimpleBullets(extractSection(body, "Allowed Transitions"));
+ const roleRegistry = extractInternalAgents(body);
const disallowedTransitions = extractSimpleBullets(extractSection(body, "Disallowed Transitions"));
const finalCheck = extractFinalCheck(body);
const redFlags = extractRedFlags(body);
const content = [
- "",
+ "",
"# Hyperstack Runtime Bootstrap",
"",
"## Critical",
@@ -195,16 +263,10 @@ export function compileUsingHyperstackBootstrap(source: string): { content: stri
"## Workflow Skills",
...workflowSkills,
"",
- "## Internal Roles",
+ "## Internal Agents",
"- Roles are internal and auto-called. Users do not invoke them directly.",
...roleRegistry,
"",
- "## Routing Summary",
- ...routingSummary,
- "",
- "## Allowed Transitions",
- ...allowedTransitions,
- "",
"## Disallowed Transitions",
...disallowedTransitions,
"",
@@ -236,8 +298,8 @@ export function validateUsingHyperstackBootstrap(content: string): string[] {
}
export function compileContextArtifacts(pluginRoot: string): ContextArtifact[] {
- const sourcePath = join(pluginRoot, "skills", "using-hyperstack", "SKILL.md");
- const outputPath = join(pluginRoot, "generated", "runtime-context", "using-hyperstack.bootstrap.md");
+ const sourcePath = join(pluginRoot, "skills", "hyperstack", "SKILL.md");
+ const outputPath = join(pluginRoot, "generated", "runtime-context", "hyperstack.bootstrap.md");
const source = readFileSync(sourcePath, "utf8");
const { content } = compileUsingHyperstackBootstrap(source);
diff --git a/src/internal/setup-hyperstack.ts b/src/internal/setup-hyperstack.ts
index 6dcfa01..8a28290 100644
--- a/src/internal/setup-hyperstack.ts
+++ b/src/internal/setup-hyperstack.ts
@@ -201,7 +201,7 @@ export function generateMcpPatch(
configPath: string,
pluginRoot: string,
platform: string,
- method: "docker" | "local" = "docker"
+ method: "docker" | "local" = "local"
): { format: PlatformFormat; content: string | object } {
const binaryPath = path.join(pluginRoot, "bin", "hyperstack.mjs");
const localServerConfig = {
diff --git a/summary.md b/summary.md
index b6f3177..4e1c93e 100644
--- a/summary.md
+++ b/summary.md
@@ -13,7 +13,10 @@ Redefine the relationship between a human developer and an AI coding assistant.
## 🛠️ What We Have Accomplished
### 1. Monorepo Consolidation
-Merged two fragmented repositories (`unified-mcp` for data, `unified-skill` for persona) into a single high-cohesion repository. The "Brain" (MCP plugins) and the "Body" (skills) now live together and stay in sync.
+Merged two fragmented repositories (`unified-mcp` for data, `unified-skill` for persona) into a single high-cohesion repository. Hyperstack is now a **Three-Layer Ecosystem**:
+- **Layer 1: Ground Truth (MCP)** - The "Brain": Deterministic data and tools
+- **Layer 2: Process (Skills)** - The "Body": Disciplined engineering workflows and gates
+- **Layer 3: Orchestration (Agents)** - The "Nervous System": Internal roles for routing and verification
### 2. Plugin Architecture (Encapsulation)
Every plugin is self-contained:
@@ -57,16 +60,21 @@ Every gate skill rewritten with:
- **1% Rule** - if there is even a 1% chance a skill applies, invoke it
- **Rationalization tables** listing the exact excuses the AI will use to skip the gate, with counters
- **Spirit of the rule equals letter of the rule** clause to close loophole-hunting
-- **SessionStart hook** (`hooks/session-start`) that force-injects the `using-hyperstack` skill into every session's context, so discipline reaches the agent without manual invocation
+- **SessionStart hook** (`hooks/session-start`) that force-injects the `hyperstack` skill into every session's context, so discipline reaches the agent without manual invocation
-### 8. The Skill System (21 skills, 3 categories)
+### 8. Internal Agent Harness (Layer 3)
+Hyperstack uses internal roles to manage complexity. These roles are **internal and auto-invoked**, ensuring specialists are used when necessary without shifting the burden to the user:
+- **`hyper` (Core):** The conductor. Owns request classification, routing, gate enforcement, final verification, and delivery.
+- **`website-builder` (Specialist):** Owns website-facing design/implementation, CTA hierarchy, and page structure. Delegates back to `hyper` for verification.
+
+### 9. The Skill System (21 skills, 3 categories)
Every skill has a `category:` frontmatter field. The index at `skills/INDEX.md` is auto-generated from frontmatter by `bash scripts/generate-skills-index.sh`.
- **Core (13):** blueprint, forge-plan, run-plan, engineering-discipline, ship-gate, deliver, test-first, debug-discipline, code-review, autonomous-mode, subagent-ops, parallel-dispatch, worktree-isolation
- **Domain (6):** designer, shadcn-expert, behaviour-analysis, security-review, design-patterns-skill, readme-writer
-- **Meta (2):** using-hyperstack, testing-skills
+- **Meta (2):** hyperstack, testing-skills
-### 9. Skill Testing Discipline (`testing-skills`)
+### 10. Skill Testing Discipline (`testing-skills`)
A TDD-for-skills methodology:
1. Write pressure scenarios
2. Dispatch fresh subagents WITHOUT the skill (RED phase)
@@ -78,7 +86,7 @@ Iron Law: NO SKILL SHIPS WITHOUT SUBAGENT PRESSURE TEST EVIDENCE.
Scenario files currently exist for ship-gate, designer, and blueprint. Remaining gate skills still need their own scenarios.
-### 10. Ecosystem Wiring
+### 11. Ecosystem Wiring
Every skill references its upstream and downstream edges explicitly.
- Designer hands DESIGN.md to forge-plan
- Forge-plan calls shadcn tools (if shadcn chosen) or hand-builds from DESIGN.md (if raw Tailwind)
@@ -105,7 +113,7 @@ Every skill references its upstream and downstream edges explicitly.
**Active development.** Main branch is stable and the Docker image publishes automatically on push.
-Eleven plugins, seventy-nine tools, twenty-one skills. The SessionStart hook is wired. The adversarial enforcement is in place. The designer pipeline works end-to-end (verified via test harness). shadcn is integrated as an optional choice.
+Eleven plugins, seventy-nine tools, twenty-one skills, and two internal agents (`hyper` and `website-builder`). The SessionStart hook is wired. The adversarial enforcement is in place. The designer pipeline works end-to-end (verified via test harness). shadcn is integrated as an optional choice.
### Remaining work
- More pressure-test scenarios for gate skills (forge-plan, engineering-discipline, behaviour-analysis, test-first)
diff --git a/tests/artifact-contracts-behaviour.test.ts b/tests/artifact-contracts-behaviour.test.ts
new file mode 100644
index 0000000..8e82acf
--- /dev/null
+++ b/tests/artifact-contracts-behaviour.test.ts
@@ -0,0 +1,29 @@
+import { expect, test } from "bun:test";
+import { loadTopology } from "../src/engine/topology-loader.ts";
+import { validateArtifactPayload } from "../src/engine/artifact-validator.ts";
+
+test("validateArtifactPayload accepts a complete workspace_inventory", () => {
+ const topology = loadTopology(process.cwd());
+ const result = validateArtifactPayload(topology, "workspace_inventory", {
+ repo_type: "web-app",
+ stack: ["react", "tailwind"],
+ touched_surfaces: ["settings page"],
+ existing_patterns: ["shadcn form"],
+ verification_commands: ["bun test", "bun run build"],
+ project_mode: "existing",
+ });
+
+ expect(result.ok).toBe(true);
+});
+
+test("validateArtifactPayload rejects missing design_contract fields", () => {
+ const topology = loadTopology(process.cwd());
+ const result = validateArtifactPayload(topology, "design_contract", {
+ visual_theme: "dark",
+ color_system: "brand + neutral",
+ });
+
+ expect(result.ok).toBe(false);
+ expect(result.missingFields).toContain("typography");
+ expect(result.missingFields).toContain("component_states");
+});
diff --git a/tests/context-compiler-behaviour.test.ts b/tests/context-compiler-behaviour.test.ts
index 3bfbe83..ad4ca29 100644
--- a/tests/context-compiler-behaviour.test.ts
+++ b/tests/context-compiler-behaviour.test.ts
@@ -77,8 +77,8 @@ ${"x".repeat(2000)}
});
test("generated bootstrap artifact stays in sync with the compiler output", () => {
- const skillSource = normalize(readFileSync(resolve("skills/using-hyperstack/SKILL.md"), "utf8"));
- const currentBootstrap = normalize(readFileSync(resolve("generated/runtime-context/using-hyperstack.bootstrap.md"), "utf8"));
+ const skillSource = normalize(readFileSync(resolve("skills/hyperstack/SKILL.md"), "utf8"));
+ const currentBootstrap = normalize(readFileSync(resolve("generated/runtime-context/hyperstack.bootstrap.md"), "utf8"));
const nextBootstrap = generateHyperstackBootstrap(skillSource);
diff --git a/tests/local-cli-behaviour.test.ts b/tests/local-cli-behaviour.test.ts
new file mode 100644
index 0000000..35ccdac
--- /dev/null
+++ b/tests/local-cli-behaviour.test.ts
@@ -0,0 +1,20 @@
+import { expect, test } from "bun:test";
+import { spawnSync } from "node:child_process";
+import { resolve } from "node:path";
+
+test("local CLI invokes stable tool names with JSON payload", () => {
+ const result = spawnSync(
+ process.execPath,
+ [
+ resolve("bin/hyperstack.mjs"),
+ "tool",
+ "designer_resolve_intent",
+ "--json",
+ '{"product":"developer analytics dashboard"}',
+ ],
+ { cwd: process.cwd(), encoding: "utf8" },
+ );
+
+ expect(result.status).toBe(0);
+ expect(result.stdout).toMatch(/Resolved Design Intent/);
+});
diff --git a/tests/local-cli-routing-behaviour.test.ts b/tests/local-cli-routing-behaviour.test.ts
new file mode 100644
index 0000000..4333424
--- /dev/null
+++ b/tests/local-cli-routing-behaviour.test.ts
@@ -0,0 +1,40 @@
+import { expect, test } from "bun:test";
+import { spawnSync } from "node:child_process";
+import { resolve } from "node:path";
+
+test("CLI route command returns routed agent and required artifacts", () => {
+ const result = spawnSync(
+ process.execPath,
+ [
+ resolve("bin/hyperstack.mjs"),
+ "route",
+ "--json",
+ '{"requestId":"req-1","domainTargets":["frontend"],"capabilityTargets":["frontend.patterns"],"workspaceInventory":{"projectMode":"existing","existingPatterns":["existing form shell"]},"changeClassification":"frontend_logic"}',
+ ],
+ { cwd: process.cwd(), encoding: "utf8" },
+ );
+
+ expect(result.status).toBe(0);
+ expect(result.stdout).toMatch(/frontend-builder/);
+ expect(result.stdout).toMatch(/workspace_inventory/);
+ expect(result.stdout).not.toMatch(/design_contract/);
+});
+
+test("CLI artifact validate command reports missing fields", () => {
+ const result = spawnSync(
+ process.execPath,
+ [
+ resolve("bin/hyperstack.mjs"),
+ "artifact",
+ "validate",
+ "workspace_inventory",
+ "--json",
+ '{"repo_type":"web-app"}',
+ ],
+ { cwd: process.cwd(), encoding: "utf8" },
+ );
+
+ expect(result.status).toBe(0);
+ expect(result.stdout).toMatch(/missingFields/);
+ expect(result.stdout).toMatch(/stack/);
+});
diff --git a/tests/plugin-registry-behaviour.test.ts b/tests/plugin-registry-behaviour.test.ts
index 14fb82f..da7f6ef 100644
--- a/tests/plugin-registry-behaviour.test.ts
+++ b/tests/plugin-registry-behaviour.test.ts
@@ -17,10 +17,10 @@ function getRegisteredTools() {
return tools;
}
-test("all 11 plugins register at least one tool", () => {
+test("all 12 plugins register at least one tool", () => {
const tools = getRegisteredTools();
const pluginPrefixes = new Set(tools.map((t) => t.name.split("_")[0]));
- expect(pluginPrefixes.size).toBe(11);
+ expect(pluginPrefixes.size).toBe(12);
});
test("every registered tool has a non-empty name and description", () => {
diff --git a/tests/role-harness-behaviour.test.ts b/tests/role-harness-behaviour.test.ts
index f223911..f6149fd 100644
--- a/tests/role-harness-behaviour.test.ts
+++ b/tests/role-harness-behaviour.test.ts
@@ -11,10 +11,10 @@ const REQUIRED_ROLE_FILES = [
"agents/hyper/LIFECYCLE.md",
"agents/hyper/CONTEXT.md",
"agents/hyper/CHECKS.md",
- "agents/website-builder/PROFILE.md",
- "agents/website-builder/LIFECYCLE.md",
- "agents/website-builder/CONTEXT.md",
- "agents/website-builder/CHECKS.md",
+ "agents/frontend-builder/PROFILE.md",
+ "agents/frontend-builder/LIFECYCLE.md",
+ "agents/frontend-builder/CONTEXT.md",
+ "agents/frontend-builder/CHECKS.md",
"harness/router.md",
"harness/transitions.md",
"harness/context-policy.md",
@@ -35,7 +35,7 @@ function normalize(str: string): string {
return str.replace(/\r\n/g, "\n");
}
-test("role harness files exist for hyper and website-builder", () => {
+test("role harness files exist for hyper and frontend-builder", () => {
for (const relativePath of REQUIRED_ROLE_FILES) {
expect(existsSync(resolve(relativePath))).toBe(true);
}
@@ -44,7 +44,7 @@ test("role harness files exist for hyper and website-builder", () => {
test("role profile frontmatter includes the required contract keys", () => {
for (const relativePath of [
"agents/hyper/PROFILE.md",
- "agents/website-builder/PROFILE.md",
+ "agents/frontend-builder/PROFILE.md",
]) {
const content = normalize(readFileSync(resolve(relativePath), "utf8"));
const frontmatter = content.match(/^---\n([\s\S]*?)\n---\n/);
@@ -66,28 +66,28 @@ test("role lifecycle and checks documents expose required headings", () => {
expect(lifecycleContent).toMatch(/^## Exit Criteria$/m);
expect(lifecycleContent).toMatch(/^## Failure Escalation$/m);
- const checksContent = normalize(readFileSync(resolve("agents/website-builder/CHECKS.md"), "utf8"));
+ const checksContent = normalize(readFileSync(resolve("agents/frontend-builder/CHECKS.md"), "utf8"));
expect(checksContent).toMatch(/^## Preconditions$/m);
expect(checksContent).toMatch(/^## Required Evidence$/m);
expect(checksContent).toMatch(/^## Done Criteria$/m);
expect(checksContent).toMatch(/^## Red Flags$/m);
});
-test("using-hyperstack bootstrap compiler preserves role-routing markers", () => {
- const source = normalize(readFileSync(resolve("skills/using-hyperstack/SKILL.md"), "utf8"));
+test("hyperstack bootstrap compiler preserves role-routing markers", () => {
+ const source = normalize(readFileSync(resolve("skills/hyperstack/SKILL.md"), "utf8"));
const { content } = compileUsingHyperstackBootstrap(source);
const missing = validateUsingHyperstackBootstrap(content);
expect(missing.length).toBe(0);
expect(content).toMatch(/hyper/);
- expect(content).toMatch(/website-builder/);
+ expect(content).toMatch(/frontend-builder/);
expect(content).toMatch(/auto-called/);
- expect(content).toMatch(/hyper -> website-builder/);
+ expect(content).toMatch(/hyper -> frontend-builder/);
});
-test("website-builder lifecycle requires workspace discovery before website decisions", () => {
- const lifecycleContent = normalize(readFileSync(resolve("agents/website-builder/LIFECYCLE.md"), "utf8"));
- const contextContent = normalize(readFileSync(resolve("agents/website-builder/CONTEXT.md"), "utf8"));
+test("frontend-builder lifecycle requires workspace discovery before frontend decisions", () => {
+ const lifecycleContent = normalize(readFileSync(resolve("agents/frontend-builder/LIFECYCLE.md"), "utf8"));
+ const contextContent = normalize(readFileSync(resolve("agents/frontend-builder/CONTEXT.md"), "utf8"));
expect(lifecycleContent).toMatch(/workspace/i);
expect(lifecycleContent).toMatch(/package\.json|manifests?|dependencies|packages/i);
@@ -102,3 +102,28 @@ test("designer skill gives user preferences precedence over auto-resolved defaul
expect(designerContent).toMatch(/preferences?.*override|override.*preferences?/i);
expect(designerContent).toMatch(/auto-resolved defaults?|defaults?.*suggestions?/i);
});
+
+test("workspace-first planning makes design contracts conditional rather than universal", () => {
+ const blueprintContent = normalize(readFileSync(resolve("skills/blueprint/SKILL.md"), "utf8"));
+ const designerContent = normalize(readFileSync(resolve("skills/designer/SKILL.md"), "utf8"));
+ const hyperstackContent = normalize(readFileSync(resolve("skills/hyperstack/SKILL.md"), "utf8"));
+ const shipGateContent = normalize(readFileSync(resolve("skills/ship-gate/SKILL.md"), "utf8"));
+ const shadcnExpertContent = normalize(readFileSync(resolve("skills/shadcn-expert/SKILL.md"), "utf8"));
+
+ expect(blueprintContent).toMatch(/workspace_inventory/i);
+ expect(blueprintContent).toMatch(/design_contract/i);
+ expect(blueprintContent).toMatch(/conditional|required only/i);
+
+ expect(designerContent).toMatch(/conditional|required only/i);
+ expect(designerContent).toMatch(/new surface|visual-semantic|existing pattern/i);
+
+ expect(hyperstackContent).toMatch(/workspace_inventory/i);
+ expect(hyperstackContent).toMatch(/design_contract/i);
+ expect(hyperstackContent).toMatch(/conditional|required only/i);
+
+ expect(shipGateContent).toMatch(/design contract/i);
+ expect(shipGateContent).toMatch(/when.*required|required.*design contract/i);
+
+ expect(shadcnExpertContent).toMatch(/workspace inventory|workspace-first/i);
+ expect(shadcnExpertContent).toMatch(/designer only if required|conditional/i);
+});
diff --git a/tests/router-behaviour.test.ts b/tests/router-behaviour.test.ts
new file mode 100644
index 0000000..cdccfe8
--- /dev/null
+++ b/tests/router-behaviour.test.ts
@@ -0,0 +1,90 @@
+import { expect, test } from "bun:test";
+import { loadTopology } from "../src/engine/topology-loader.ts";
+import { routeRequest } from "../src/engine/router.ts";
+import { assertSkillAllowedForAgent } from "../src/engine/skill-enforcer.ts";
+
+test("routeRequest sends existing-project frontend logic work to frontend-builder without design contract", () => {
+ const topology = loadTopology(process.cwd());
+ const route = routeRequest(topology, {
+ requestId: "req-1",
+ domainTargets: ["frontend"],
+ capabilityTargets: ["frontend.patterns"],
+ workspaceInventory: {
+ projectMode: "existing",
+ existingPatterns: ["existing form shell"],
+ },
+ changeClassification: "frontend_logic",
+ });
+
+ expect(route.agent.id).toBe("frontend-builder");
+ expect(route.requiredArtifacts).toContain("workspace_inventory");
+ expect(route.requiredArtifacts).not.toContain("design_contract");
+});
+
+test("routeRequest requires design contract for a new visual surface", () => {
+ const topology = loadTopology(process.cwd());
+ const route = routeRequest(topology, {
+ requestId: "req-2",
+ domainTargets: ["frontend"],
+ capabilityTargets: ["design.intent"],
+ workspaceInventory: {
+ projectMode: "greenfield",
+ existingPatterns: [],
+ },
+ changeClassification: "frontend_visual",
+ });
+
+ expect(route.agent.id).toBe("frontend-builder");
+ expect(route.requiredArtifacts).toContain("workspace_inventory");
+ expect(route.requiredArtifacts).toContain("design_contract");
+ expect(route.proofMode).toBe("visual_and_behavioral");
+});
+
+test("routeRequest sends mixed frontend+backend work to fullstack-builder", () => {
+ const topology = loadTopology(process.cwd());
+ const route = routeRequest(topology, {
+ requestId: "req-3",
+ domainTargets: ["frontend", "backend"],
+ capabilityTargets: ["frontend.patterns", "backend.http.patterns"],
+ workspaceInventory: {
+ projectMode: "existing",
+ existingPatterns: [],
+ },
+ changeClassification: "fullstack_slice",
+ });
+
+ expect(route.agent.id).toBe("fullstack-builder");
+ expect(route.proofMode).toBe("visual_and_behavioral");
+});
+
+test("assertSkillAllowedForAgent rejects backend-only review skill on frontend-builder", () => {
+ const topology = loadTopology(process.cwd());
+ const route = routeRequest(topology, {
+ requestId: "req-4",
+ domainTargets: ["frontend"],
+ capabilityTargets: ["frontend.patterns"],
+ workspaceInventory: {
+ projectMode: "existing",
+ existingPatterns: ["existing form shell"],
+ },
+ changeClassification: "frontend_logic",
+ });
+
+ expect(() => assertSkillAllowedForAgent(route.agent, "security-review")).toThrow(/not allowed/i);
+});
+
+test("assertSkillAllowedForAgent accepts designer for frontend-builder", () => {
+ const topology = loadTopology(process.cwd());
+ const route = routeRequest(topology, {
+ requestId: "req-5",
+ domainTargets: ["frontend"],
+ capabilityTargets: ["design.intent"],
+ workspaceInventory: {
+ projectMode: "greenfield",
+ existingPatterns: [],
+ },
+ changeClassification: "frontend_visual",
+ });
+
+ expect(() => assertSkillAllowedForAgent(route.agent, "designer")).not.toThrow();
+});
diff --git a/tests/runtime-behaviour.test.ts b/tests/runtime-behaviour.test.ts
index efa66e9..d0a74cd 100644
--- a/tests/runtime-behaviour.test.ts
+++ b/tests/runtime-behaviour.test.ts
@@ -2,8 +2,8 @@ import { test, expect } from "bun:test";
import { readFile } from "node:fs/promises";
import { resolve } from "node:path";
import { once } from "node:events";
-import { setTimeout as delay } from "node:timers/promises";
import { spawn } from "node:child_process";
+import { generateMcpPatch } from "../src/internal/setup-hyperstack.ts";
function normalize(str: string): string {
return str.replace(/\r\n/g, "\n");
@@ -77,7 +77,9 @@ test("Claude SessionStart hook command executes successfully on this platform",
};
expect(payload.additionalContext || payload.hookSpecificOutput?.additionalContext).toBeDefined();
- expect(normalize(payload.additionalContext || payload.hookSpecificOutput?.additionalContext || "")).toMatch(/compiled runtime bootstrap/);
+ expect(normalize(payload.additionalContext || payload.hookSpecificOutput?.additionalContext || "")).toMatch(
+ /generated topology bootstrap|compiled runtime bootstrap/,
+ );
});
test("SessionStart hook emits Cursor-compatible output shape when CURSOR_PLUGIN_ROOT is set", async () => {
@@ -116,7 +118,7 @@ test("SessionStart hook prefers Cursor output when both CURSOR_PLUGIN_ROOT and C
expect(payload.hookSpecificOutput).toBeUndefined();
});
-test("package bin entry starts without an immediate runtime crash", async () => {
+test("package bin entry prints usage when invoked without command arguments", async () => {
const raw = await readFile(resolve("package.json"), "utf8");
const pkg = JSON.parse(raw) as {
bin?: { hyperstack?: string };
@@ -130,14 +132,25 @@ test("package bin entry starts without an immediate runtime crash", async () =>
stdio: ["pipe", "pipe", "pipe"],
});
+ let stdout = "";
let stderr = "";
+ child.stdout.on("data", (chunk: Buffer | string) => {
+ stdout += chunk.toString();
+ });
child.stderr.on("data", (chunk: Buffer | string) => {
stderr += chunk.toString();
});
- await delay(200);
- expect(child.exitCode).toBeNull();
+ const [exitCode] = (await once(child, "close")) as [number | null];
+ expect(exitCode).toBe(1);
+ expect(`${stdout}${stderr}`).toMatch(/Usage: hyperstack tool/);
+});
+
+test("generateMcpPatch defaults to local runtime instead of docker", () => {
+ const patch = generateMcpPatch("/tmp/config.json", "/repo", "cursor");
+ const serialized = JSON.stringify(patch.content);
- child.kill("SIGTERM");
- await once(child, "close");
+ expect(serialized).toMatch(/hyperstack/);
+ expect(serialized).toMatch(/bin\/hyperstack\.mjs/);
+ expect(serialized).not.toMatch(/docker/);
});
diff --git a/tests/tool-bridge-behaviour.test.ts b/tests/tool-bridge-behaviour.test.ts
new file mode 100644
index 0000000..0a99edd
--- /dev/null
+++ b/tests/tool-bridge-behaviour.test.ts
@@ -0,0 +1,21 @@
+import { expect, test } from "bun:test";
+import { invokeRegisteredTool } from "../src/engine/tool-bridge.ts";
+import { register as registerResolveIntent } from "../src/plugins/designer/tools/resolve-intent.ts";
+import { register as registerGetPractice } from "../src/plugins/golang/tools/get-practice.ts";
+
+test("invokeRegisteredTool runs a designer tool module without MCP server transport", async () => {
+ const result = await invokeRegisteredTool(registerResolveIntent, {
+ product: "developer analytics dashboard",
+ });
+
+ expect(result.isError).toBeUndefined();
+ expect(result.content?.[0]?.text).toMatch(/Resolved Design Intent/);
+});
+
+test("invokeRegisteredTool runs a golang tool module without MCP server transport", async () => {
+ const result = await invokeRegisteredTool(registerGetPractice, {
+ name: "error-wrapping",
+ });
+
+ expect(result.content?.[0]?.text).toMatch(/error-wrapping/i);
+});
diff --git a/tests/topology-artifacts-behaviour.test.ts b/tests/topology-artifacts-behaviour.test.ts
new file mode 100644
index 0000000..d815abf
--- /dev/null
+++ b/tests/topology-artifacts-behaviour.test.ts
@@ -0,0 +1,24 @@
+import { expect, test } from "bun:test";
+import { existsSync, readFileSync } from "node:fs";
+import { resolve } from "node:path";
+
+test("generated local tool registry includes stable tool names", () => {
+ const registryPath = resolve("generated/tool-index/local-tool-registry.json");
+ expect(existsSync(registryPath)).toBe(true);
+
+ const registry = JSON.parse(readFileSync(registryPath, "utf8")) as Record;
+ expect(Object.keys(registry)).toContain("designer_resolve_intent");
+ expect(Object.keys(registry)).toContain("golang_get_practice");
+ expect(Object.keys(registry)).toContain("reactflow_get_api");
+});
+
+test("generated topology bootstrap includes agent and bundle routing markers", () => {
+ const bootstrap = readFileSync(resolve("generated/runtime-context/topology.bootstrap.md"), "utf8");
+ expect(bootstrap).toMatch(/workspace_inventory/);
+ expect(bootstrap).toMatch(/hyper/);
+ expect(bootstrap).toMatch(/frontend-builder/);
+ expect(bootstrap).toMatch(/backend-builder/);
+ expect(bootstrap).toMatch(/frontend\.design/);
+ expect(bootstrap).toMatch(/design contract is conditional/i);
+ expect(bootstrap).toMatch(/cross-domain agent: fullstack-builder/i);
+});
diff --git a/tests/topology-manifest-behaviour.test.ts b/tests/topology-manifest-behaviour.test.ts
new file mode 100644
index 0000000..617c85c
--- /dev/null
+++ b/tests/topology-manifest-behaviour.test.ts
@@ -0,0 +1,51 @@
+import { expect, test } from "bun:test";
+import { loadTopology } from "../src/engine/topology-loader.ts";
+import { resolveCapabilityContext } from "../src/engine/resolver.ts";
+
+test("loadTopology reads root manifest and expanded domain/agent/bundle files", () => {
+ const topology = loadTopology(process.cwd());
+
+ expect(topology.version).toBe(1);
+ expect(topology.entryAgent).toBe("hyper");
+ expect(topology.domains.map((d) => d.id)).toEqual(["frontend", "backend", "shared"]);
+ expect(topology.agents.map((a) => a.id)).toContain("frontend-builder");
+ expect(topology.bundles.map((b) => b.id)).toContain("frontend.design");
+});
+
+test("resolveCapabilityContext rejects forbidden bundle access", () => {
+ const topology = loadTopology(process.cwd());
+
+ expect(() =>
+ resolveCapabilityContext(topology, {
+ agentId: "frontend-builder",
+ capability: "backend.http.patterns",
+ }),
+ ).toThrow(/forbidden bundle/i);
+});
+
+test("resolveCapabilityContext returns frontend.design for design.intent", () => {
+ const topology = loadTopology(process.cwd());
+ const result = resolveCapabilityContext(topology, {
+ agentId: "frontend-builder",
+ capability: "design.intent",
+ });
+
+ expect(result.bundle.id).toBe("frontend.design");
+ expect(result.domain.id).toBe("frontend");
+});
+
+test("loadTopology reads artifact contracts and route defaults", () => {
+ const topology = loadTopology(process.cwd());
+
+ expect(topology.artifacts.map((a) => a.id)).toEqual([
+ "workspace_inventory",
+ "task_handoff",
+ "design_contract",
+ "build_result",
+ "verification_report",
+ ]);
+ expect(topology.routeDefaults.defaultAgent).toBe("hyper");
+ expect(topology.routeDefaults.requiresWorkspaceInventory).toBe(true);
+ expect(topology.routeDefaults.domainPreference.frontend).toBe("frontend-builder");
+ expect(topology.routeDefaults.designContractRequiredWhen).toContain("new_surface");
+});
diff --git a/topology/agents/backend-builder.yaml b/topology/agents/backend-builder.yaml
new file mode 100644
index 0000000..0e69225
--- /dev/null
+++ b/topology/agents/backend-builder.yaml
@@ -0,0 +1,20 @@
+id: backend-builder
+kind: specialist
+domains:
+ - backend
+allowed_skills:
+ - test-first
+ - debug-discipline
+ - engineering-discipline
+ - worktree-isolation
+ - security-review
+allowed_bundles:
+ - backend.http
+ - backend.lang.go
+ - backend.lang.rust
+forbidden_bundles:
+ - frontend.design
+ - frontend.react
+handoff_in: task_handoff
+handoff_out: build_result
+completion_proof: executable
diff --git a/topology/agents/frontend-builder.yaml b/topology/agents/frontend-builder.yaml
new file mode 100644
index 0000000..d878a4d
--- /dev/null
+++ b/topology/agents/frontend-builder.yaml
@@ -0,0 +1,22 @@
+id: frontend-builder
+kind: specialist
+domains:
+ - frontend
+allowed_skills:
+ - test-first
+ - debug-discipline
+ - engineering-discipline
+ - worktree-isolation
+ - designer
+ - behaviour-analysis
+ - shadcn-expert
+allowed_bundles:
+ - frontend.design
+ - frontend.react
+forbidden_bundles:
+ - backend.http
+ - backend.lang.go
+ - backend.lang.rust
+handoff_in: task_handoff
+handoff_out: build_result
+completion_proof: visual_and_behavioral
diff --git a/topology/agents/fullstack-builder.yaml b/topology/agents/fullstack-builder.yaml
new file mode 100644
index 0000000..eed25f8
--- /dev/null
+++ b/topology/agents/fullstack-builder.yaml
@@ -0,0 +1,24 @@
+id: fullstack-builder
+kind: cross-domain
+domains:
+ - frontend
+ - backend
+allowed_skills:
+ - test-first
+ - debug-discipline
+ - engineering-discipline
+ - worktree-isolation
+ - designer
+ - behaviour-analysis
+ - shadcn-expert
+ - security-review
+allowed_bundles:
+ - frontend.design
+ - frontend.react
+ - backend.http
+ - backend.lang.go
+ - backend.lang.rust
+forbidden_bundles: []
+handoff_in: task_handoff
+handoff_out: build_result
+completion_proof: strictest_touched_domain
diff --git a/topology/agents/hyper.yaml b/topology/agents/hyper.yaml
new file mode 100644
index 0000000..e38fd01
--- /dev/null
+++ b/topology/agents/hyper.yaml
@@ -0,0 +1,20 @@
+id: hyper
+kind: orchestrator
+domains:
+ - shared
+allowed_skills:
+ - blueprint
+ - forge-plan
+ - run-plan
+ - parallel-dispatch
+ - subagent-ops
+ - autonomous-mode
+ - ship-gate
+ - code-review
+ - deliver
+allowed_bundles:
+ - shared.system
+forbidden_bundles: []
+handoff_in: user_request
+handoff_out: task_handoff
+completion_proof: routing_and_verification
diff --git a/topology/artifacts/build_result.yaml b/topology/artifacts/build_result.yaml
new file mode 100644
index 0000000..fa068f1
--- /dev/null
+++ b/topology/artifacts/build_result.yaml
@@ -0,0 +1,6 @@
+id: build_result
+required_fields:
+ - agent_id
+ - changed_files
+ - verification_commands
+proof_mode: executable
diff --git a/topology/artifacts/design_contract.yaml b/topology/artifacts/design_contract.yaml
new file mode 100644
index 0000000..0189b9e
--- /dev/null
+++ b/topology/artifacts/design_contract.yaml
@@ -0,0 +1,9 @@
+id: design_contract
+required_fields:
+ - visual_theme
+ - color_system
+ - typography
+ - spacing
+ - component_states
+ - motion_rules
+proof_mode: visual_contract_conditional
diff --git a/topology/artifacts/task_handoff.yaml b/topology/artifacts/task_handoff.yaml
new file mode 100644
index 0000000..11c4035
--- /dev/null
+++ b/topology/artifacts/task_handoff.yaml
@@ -0,0 +1,10 @@
+id: task_handoff
+required_fields:
+ - request_id
+ - domain_targets
+ - capability_targets
+ - success_criteria
+ - change_classification
+ - requires_design_contract
+ - required_artifacts
+proof_mode: routing_only
diff --git a/topology/artifacts/verification_report.yaml b/topology/artifacts/verification_report.yaml
new file mode 100644
index 0000000..7dbb04e
--- /dev/null
+++ b/topology/artifacts/verification_report.yaml
@@ -0,0 +1,6 @@
+id: verification_report
+required_fields:
+ - status
+ - proof_mode
+ - covered_paths
+proof_mode: routing_and_verification
diff --git a/topology/artifacts/workspace_inventory.yaml b/topology/artifacts/workspace_inventory.yaml
new file mode 100644
index 0000000..637a712
--- /dev/null
+++ b/topology/artifacts/workspace_inventory.yaml
@@ -0,0 +1,9 @@
+id: workspace_inventory
+required_fields:
+ - repo_type
+ - stack
+ - touched_surfaces
+ - existing_patterns
+ - verification_commands
+ - project_mode
+proof_mode: discovery_only
diff --git a/topology/bundles/backend.http.yaml b/topology/bundles/backend.http.yaml
new file mode 100644
index 0000000..502639c
--- /dev/null
+++ b/topology/bundles/backend.http.yaml
@@ -0,0 +1,10 @@
+id: backend.http
+domain: backend
+capabilities:
+ - backend.http.patterns
+sources:
+ - corpus/backend/echo
+tool_prefixes:
+ - echo_
+output_contracts:
+ - backend_recipe
diff --git a/topology/bundles/backend.lang.go.yaml b/topology/bundles/backend.lang.go.yaml
new file mode 100644
index 0000000..01933dc
--- /dev/null
+++ b/topology/bundles/backend.lang.go.yaml
@@ -0,0 +1,10 @@
+id: backend.lang.go
+domain: backend
+capabilities:
+ - backend.go.patterns
+sources:
+ - corpus/backend/golang
+tool_prefixes:
+ - golang_
+output_contracts:
+ - go_reference
diff --git a/topology/bundles/backend.lang.rust.yaml b/topology/bundles/backend.lang.rust.yaml
new file mode 100644
index 0000000..5a38fe4
--- /dev/null
+++ b/topology/bundles/backend.lang.rust.yaml
@@ -0,0 +1,10 @@
+id: backend.lang.rust
+domain: backend
+capabilities:
+ - backend.rust.patterns
+sources:
+ - corpus/backend/rust
+tool_prefixes:
+ - rust_
+output_contracts:
+ - rust_reference
diff --git a/topology/bundles/frontend.design.yaml b/topology/bundles/frontend.design.yaml
new file mode 100644
index 0000000..b104568
--- /dev/null
+++ b/topology/bundles/frontend.design.yaml
@@ -0,0 +1,18 @@
+id: frontend.design
+domain: frontend
+capabilities:
+ - design.intent
+ - design.contract
+ - design.tokens
+sources:
+ - corpus/frontend/designer
+ - corpus/frontend/design-tokens
+ - corpus/frontend/ui-ux
+tool_prefixes:
+ - designer_
+ - design_tokens_
+ - ui_ux_
+output_contracts:
+ - intent_resolution
+ - design_contract
+ - token_spec
diff --git a/topology/bundles/frontend.react.yaml b/topology/bundles/frontend.react.yaml
new file mode 100644
index 0000000..e06f064
--- /dev/null
+++ b/topology/bundles/frontend.react.yaml
@@ -0,0 +1,21 @@
+id: frontend.react
+domain: frontend
+capabilities:
+ - frontend.patterns
+ - frontend.motion
+ - frontend.flow
+sources:
+ - corpus/frontend/react
+ - corpus/frontend/shadcn
+ - corpus/frontend/motion
+ - corpus/frontend/lenis
+ - corpus/frontend/reactflow
+tool_prefixes:
+ - react_
+ - shadcn_
+ - motion_
+ - lenis_
+ - reactflow_
+output_contracts:
+ - frontend_reference
+ - component_contract
diff --git a/topology/bundles/shared.system.yaml b/topology/bundles/shared.system.yaml
new file mode 100644
index 0000000..875c58d
--- /dev/null
+++ b/topology/bundles/shared.system.yaml
@@ -0,0 +1,10 @@
+id: shared.system
+domain: shared
+capabilities:
+ - system.setup
+sources:
+ - corpus/shared/system
+tool_prefixes:
+ - hyperstack_
+output_contracts:
+ - setup_patch
diff --git a/topology/domains/backend.yaml b/topology/domains/backend.yaml
new file mode 100644
index 0000000..152c866
--- /dev/null
+++ b/topology/domains/backend.yaml
@@ -0,0 +1,13 @@
+id: backend
+write_policy: direct
+completion_proof: executable
+truth_bundles:
+ - backend.http
+ - backend.lang.go
+ - backend.lang.rust
+required_gates: []
+optional_gates:
+ - security-review
+forbidden_bundles:
+ - frontend.design
+ - frontend.react
diff --git a/topology/domains/frontend.yaml b/topology/domains/frontend.yaml
new file mode 100644
index 0000000..375e8c9
--- /dev/null
+++ b/topology/domains/frontend.yaml
@@ -0,0 +1,15 @@
+id: frontend
+write_policy: constrained
+completion_proof: visual_and_behavioral
+truth_bundles:
+ - frontend.design
+ - frontend.react
+required_gates:
+ - designer
+ - behaviour-analysis
+optional_gates:
+ - shadcn-expert
+forbidden_bundles:
+ - backend.http
+ - backend.lang.go
+ - backend.lang.rust
diff --git a/topology/domains/shared.yaml b/topology/domains/shared.yaml
new file mode 100644
index 0000000..31e243a
--- /dev/null
+++ b/topology/domains/shared.yaml
@@ -0,0 +1,8 @@
+id: shared
+write_policy: policy_only
+completion_proof: routing_and_verification
+truth_bundles:
+ - shared.system
+required_gates: []
+optional_gates: []
+forbidden_bundles: []
diff --git a/topology/manifest.yaml b/topology/manifest.yaml
new file mode 100644
index 0000000..3569f0a
--- /dev/null
+++ b/topology/manifest.yaml
@@ -0,0 +1,25 @@
+version: 1
+default_transport: local-tools
+entry_agent: hyper
+domains:
+ - frontend
+ - backend
+ - shared
+agents:
+ - hyper
+ - frontend-builder
+ - backend-builder
+ - fullstack-builder
+bundles:
+ - shared.system
+ - frontend.design
+ - frontend.react
+ - backend.http
+ - backend.lang.go
+ - backend.lang.rust
+artifacts:
+ - workspace_inventory
+ - task_handoff
+ - design_contract
+ - build_result
+ - verification_report
diff --git a/topology/routes/defaults.yaml b/topology/routes/defaults.yaml
new file mode 100644
index 0000000..1d8dc52
--- /dev/null
+++ b/topology/routes/defaults.yaml
@@ -0,0 +1,15 @@
+default_agent: hyper
+requires_workspace_inventory: true
+domain_preference:
+ frontend: frontend-builder
+ backend: backend-builder
+ shared: hyper
+cross_domain_agent: fullstack-builder
+design_contract_required_when:
+ - new_surface
+ - visual_semantic_change
+ - no_existing_pattern_match
+strictest_proof_order:
+ - routing_and_verification
+ - executable
+ - visual_and_behavioral
diff --git a/topology/skills/groups.yaml b/topology/skills/groups.yaml
new file mode 100644
index 0000000..dcc217e
--- /dev/null
+++ b/topology/skills/groups.yaml
@@ -0,0 +1,23 @@
+groups:
+ orchestration:
+ - hyperstack
+ - blueprint
+ - forge-plan
+ - run-plan
+ - parallel-dispatch
+ - subagent-ops
+ - autonomous-mode
+ quality:
+ - test-first
+ - debug-discipline
+ - engineering-discipline
+ - ship-gate
+ - code-review
+ - deliver
+ - worktree-isolation
+ frontend-specialty:
+ - designer
+ - behaviour-analysis
+ - shadcn-expert
+ backend-specialty:
+ - security-review
diff --git a/topology/skills/policies.yaml b/topology/skills/policies.yaml
new file mode 100644
index 0000000..66fb826
--- /dev/null
+++ b/topology/skills/policies.yaml
@@ -0,0 +1,26 @@
+skills:
+ designer:
+ phase: design
+ domains:
+ - frontend
+ requires_artifacts:
+ - task_handoff
+ produces_artifacts:
+ - design_contract
+ behaviour-analysis:
+ phase: verify
+ domains:
+ - frontend
+ requires_artifacts:
+ - build_result
+ - design_contract
+ produces_artifacts:
+ - verification_report
+ security-review:
+ phase: verify
+ domains:
+ - backend
+ requires_artifacts:
+ - build_result
+ produces_artifacts:
+ - review_report