From 7131ac30e52c136625125e65dc3fc645facb3147 Mon Sep 17 00:00:00 2001 From: Kailas Mahavarkar <66670953+KailasMahavarkar@users.noreply.github.com> Date: Wed, 15 Apr 2026 05:43:20 +0530 Subject: [PATCH 01/23] feat: rename using-hyperstack to hyperstack and implement 3-layer architecture --- AGENTS.md | 2 +- GEMINI.md | 2 +- README.md | 14 +++---- agents/hyper/PROFILE.md | 2 +- ...k.bootstrap.md => hyperstack.bootstrap.md} | 21 ++++++---- hooks/session-start.mjs | 6 +-- install.md | 4 +- skills/INDEX.md | 2 +- skills/blueprint/SKILL.md | 2 +- skills/code-review/SKILL.md | 2 +- .../{using-hyperstack => hyperstack}/SKILL.md | 42 +++++++++++++------ .../references/agent-tools.md | 0 .../references/codex-tools.md | 0 .../references/gemini-tools.md | 0 src/internal/compile-runtime-context.ts | 2 +- src/internal/context-compiler.ts | 6 +-- summary.md | 22 ++++++---- tests/context-compiler-behaviour.test.ts | 4 +- tests/role-harness-behaviour.test.ts | 4 +- 19 files changed, 82 insertions(+), 55 deletions(-) rename generated/runtime-context/{using-hyperstack.bootstrap.md => hyperstack.bootstrap.md} (84%) rename skills/{using-hyperstack => hyperstack}/SKILL.md (89%) rename skills/{using-hyperstack => hyperstack}/references/agent-tools.md (100%) rename skills/{using-hyperstack => hyperstack}/references/codex-tools.md (100%) rename skills/{using-hyperstack => hyperstack}/references/gemini-tools.md (100%) diff --git a/AGENTS.md b/AGENTS.md index c1baad3..8f25497 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -44,7 +44,7 @@ rules: load and obey when present: * `~/.gemini/settings.json`, `.cursorrules`, or similar ide-specific project rules -* `./skills/using-hyperstack/SKILL.md` +* `./skills/hyperstack/SKILL.md` * any task-specific skill doc the user points to * repo-local agent or steering docs diff --git a/GEMINI.md b/GEMINI.md index d26be52..25e616d 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -2,5 +2,5 @@ Disciplined MCP server + skill system with adversarial enforcement. Core focus: React Flow v12, Motion v12, Lenis, React 19, Echo, Go, Rust, and the Designer pipeline. -@./skills/using-hyperstack/SKILL.md +@./skills/hyperstack/SKILL.md diff --git a/README.md b/README.md index 3288e50..ee1b212 100755 --- a/README.md +++ b/README.md @@ -186,7 +186,7 @@ The MCP server gives you tools. The skills give you discipline. Install both: git clone https://github.com/orkait/hyperstack.git ~/.claude/skills/hyperstack ``` -After installing, the SessionStart hook (at `hooks/session-start.mjs`) will auto-inject the `using-hyperstack` skill into every session. No manual activation needed. +After installing, the SessionStart hook (at `hooks/session-start.mjs`) will auto-inject the `hyperstack` skill into every session. No manual activation needed. ### πŸ’» From source @@ -204,9 +204,9 @@ Node 18+ required. --- -## 🧠 The Two-Layer System +## 🧠 The Three-Layer System -Hyperstack's strength comes from the friction between **Ground Truth** (MCP) and **Enforcement** (Skills). +Hyperstack's strength comes from the friction between **Ground Truth** (MCP), **Enforcement** (Skills), and **Orchestration** (Agents). ### Layer 1: MCP Plugins (Ground Truth) @@ -236,9 +236,9 @@ Markdown with adversarial enforcement. Each skill contains an **Iron Law** that These laws are backed by **Rationalization Tables**-pre-written counters to every excuse an AI agent uses to skip quality gates. -### Internal Harness (role routing + bootstrap) +### Layer 3: Agents (Orchestration & Routing) -The internal harness is what ties the public layers together: +The internal harness is what ties the public layers together by managing process and domains: - bootstrap is injected at session start from generated runtime context - `hyper` owns classification, routing, gates, and verification @@ -285,7 +285,7 @@ The internal harness is what ties the public layers together: | Skill | Role | |---|---| -| `using-hyperstack` | Force-injected at session start via hook - the enforcement payload | +| `hyperstack` | Force-injected at session start via hook - the enforcement payload | | `testing-skills` | RED-GREEN-REFACTOR pressure testing for skills using subagents | @@ -309,7 +309,7 @@ Ordinary skill markdown is a polite suggestion. Polite suggestion fails when an When you say, **β€œbuild me a SaaS dashboard”**: -1. **SessionStart** already puts in `using-hyperstack`, so AI know system is there. +1. **SessionStart** already puts in `hyperstack`, so AI know system is there. 2. **Blueprint skill** sees visual job and sends it to `hyperstack:designer`. 3. **Designer skill** runs `designer_resolve_intent(product)` to guess industry, personality, style, density, and mode. 4. Designer asks **3 questions** in base mode, or **12 questions** in advanced mode. diff --git a/agents/hyper/PROFILE.md b/agents/hyper/PROFILE.md index 33bec81..793718a 100644 --- a/agents/hyper/PROFILE.md +++ b/agents/hyper/PROFILE.md @@ -18,7 +18,7 @@ must_not_do: delegates_to: - website-builder requires: - - current bootstrap invariants from using-hyperstack + - current bootstrap invariants from hyperstack - approved design before implementation when required - verification evidence before completion or delivery --- diff --git a/generated/runtime-context/using-hyperstack.bootstrap.md b/generated/runtime-context/hyperstack.bootstrap.md similarity index 84% rename from generated/runtime-context/using-hyperstack.bootstrap.md rename to generated/runtime-context/hyperstack.bootstrap.md index e482a91..8333360 100644 --- a/generated/runtime-context/using-hyperstack.bootstrap.md +++ b/generated/runtime-context/hyperstack.bootstrap.md @@ -1,15 +1,13 @@ - + # Hyperstack Runtime Bootstrap ## Critical 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. +**The 1% Rule:** If there is even a 1% chance that a Hyperstack skill, MCP tool, or internal agent role applies to the task you are about to perform, you MUST invoke/route 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.** - ## Iron Laws ``` 1. NO CODE WITHOUT MCP GROUND-TRUTH DATA @@ -23,7 +21,9 @@ If the task involves React, React Flow, Motion, Lenis, Echo, Go, Rust, design to 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. + +5. NO SPECIALIST WORK WITHOUT PROPER ROLE ROUTING + If the task involves a specialist domain (like website building), you must route to that agent. ``` ## Instruction Priority @@ -65,10 +65,13 @@ If the task involves React, React Flow, Motion, Lenis, Echo, Go, Rust, design to - `hyperstack:security-review`: OWASP audits, API and infrastructure security - `hyperstack:readme-writer`: Evidence-based documentation -## Internal Roles -- Roles are internal and auto-called. Users do not invoke them directly. -- `hyper` - conductor, classifier, gatekeeper, verifier, and delivery owner -- `website-builder` - first specialist for website-facing design and +## Layer 3: Agents (Orchestration & Routing) +- Hyperstack uses internal roles to manage complexity. These roles are internal and auto-invoked. +- `hyper` - Core: Classification, routing, gate enforcement, final verification, delivery. +- `website-builder` - Specialist: Website-facing design/implementation, CTA hierarchy, page structure. +- Every request starts in `hyper`. +- `hyper` classifies and delegates to specialists (e.g., `website-builder`) when domain-specific work is detected. +- Specialists MUST hand back to `hyper` for final verification and ship-gate. ## Routing Summary - Every request enters through `hyper` diff --git a/hooks/session-start.mjs b/hooks/session-start.mjs index 62a738e..6f04cdf 100644 --- a/hooks/session-start.mjs +++ b/hooks/session-start.mjs @@ -12,8 +12,8 @@ function emit(payload) { } try { - const compiledBootstrapPath = join(pluginRoot, "generated", "runtime-context", "using-hyperstack.bootstrap.md"); - const fallbackSkillPath = join(pluginRoot, "skills", "using-hyperstack", "SKILL.md"); + const compiledBootstrapPath = join(pluginRoot, "generated", "runtime-context", "hyperstack.bootstrap.md"); + const fallbackSkillPath = join(pluginRoot, "skills", "hyperstack", "SKILL.md"); let bootstrapContent; let bootstrapLabel; @@ -23,7 +23,7 @@ try { bootstrapLabel = "compiled runtime bootstrap"; } catch { bootstrapContent = readFileSync(fallbackSkillPath, "utf8"); - bootstrapLabel = "full content of your 'hyperstack:using-hyperstack' skill"; + bootstrapLabel = "full content of your 'hyperstack:hyperstack' skill"; } const sessionContext = `\nYou have Hyperstack.\n\n**Below is the ${bootstrapLabel} - your introduction to using Hyperstack. For all other skills, use the 'Skill' tool:**\n\n${bootstrapContent}\n`; diff --git a/install.md b/install.md index 7f22353..ae652a2 100644 --- a/install.md +++ b/install.md @@ -19,7 +19,7 @@ Three tightly-coupled pieces, installed together: 1. **An internal harness** - bootstrap, internal role routing, and workflow control. Current internal roles include `main` and `website-builder`. 2. **An MCP server** with 12 plugins and 80 tools - deterministic knowledge for React Flow v12, Motion v12, Lenis, React 19 / Next.js, Echo, Go, Rust, design tokens, UI/UX principles, shadcn/ui (Base UI edition), and the `designer` DESIGN.md pipeline. -3. **A skill system** with 21 skills including adversarial enforcement gates (`blueprint`, `designer`, `forge-plan`, `ship-gate`, `engineering-discipline`) and a SessionStart hook that force-injects the `using-hyperstack` skill at every session start. +3. **A skill system** with 21 skills including adversarial enforcement gates (`blueprint`, `designer`, `forge-plan`, `ship-gate`, `engineering-discipline`) and a SessionStart hook that face-injects the `hyperstack` skill at every session start. The install steps below wire the public pieces the user actually needs: the MCP server and the skills. The internal harness is shipped inside the repository and @@ -281,7 +281,7 @@ Then follow Step 2 of Option A to start the single persistent `hyperstack-mcp` c ### SessionStart hook does not fire -On Claude Code, hooks live in `.claude/hooks.json`. Confirm the file exists in the repository root and references `session-start.mjs`. If the hook is missing or malformed, the `using-hyperstack` skill will not be injected automatically. You can still invoke skills manually with `/using-hyperstack`. +On Claude Code, hooks live in `.claude/hooks.json`. Confirm the file exists in the repository root and references `session-start.mjs`. If the hook is missing or malformed, the `hyperstack` skill will not be injected automatically. You can still invoke skills manually with `/hyperstack`. On Qwen Code, there is no plugin system or hook mechanism. Skills are available on disk at `~/.qwen/skills/hyperstack/skills/INDEX.md` but must be referenced manually by the agent - no auto-injection occurs. diff --git a/skills/INDEX.md b/skills/INDEX.md index 85bef18..e467193 100644 --- a/skills/INDEX.md +++ b/skills/INDEX.md @@ -44,6 +44,6 @@ Categories: | Skill | Description | |---|---| | `testing-skills` | Use when creating or editing Hyperstack skills, before shipping them, to verify they actually work under pressure and re | -| `using-hyperstack` | Bootstrap - establishes Hyperstack MCP tools and skills before any technical work. Auto-loaded at session start via Sess | +| `hyperstack` | Bootstrap - establishes Hyperstack MCP tools and skills before any technical work. Auto-loaded at session start via Sess | diff --git a/skills/blueprint/SKILL.md b/skills/blueprint/SKILL.md index 02df601..f107311 100644 --- a/skills/blueprint/SKILL.md +++ b/skills/blueprint/SKILL.md @@ -153,7 +153,7 @@ blueprint (THIS) β†’ forge-plan β†’ [execution] β†’ ship-gate β†’ deliver ### Upstream Dependencies - None (entry point for feature work) -- `using-hyperstack` β†’ 1% rule enforcement +- `hyperstack` β†’ 1% rule enforcement ### Downstream Consumers - `forge-plan` β†’ reads approved design, builds MCP-verified task plan diff --git a/skills/code-review/SKILL.md b/skills/code-review/SKILL.md index df8ccde..5eae65b 100644 --- a/skills/code-review/SKILL.md +++ b/skills/code-review/SKILL.md @@ -40,7 +40,7 @@ HEAD_SHA=$(git rev-parse HEAD) - The git diff (`git diff $BASE_SHA..$HEAD_SHA`) - Specific question: "Does this match the spec? Flag missing, extra, or incorrect code." -**Note:** Review subagents get raw diff + spec only. Do not load bootstrap (`using-hyperstack`) β†’ `` gate prevents it anyway. Provide exactly what they need to evaluate. +**Note:** Review subagents get raw diff + spec only. Do not load bootstrap (`hyperstack`) β†’ `` gate prevents it anyway. Provide exactly what they need to evaluate. **3. Act on results:** diff --git a/skills/using-hyperstack/SKILL.md b/skills/hyperstack/SKILL.md similarity index 89% rename from skills/using-hyperstack/SKILL.md rename to skills/hyperstack/SKILL.md index 14651d0..d88f96e 100644 --- a/skills/using-hyperstack/SKILL.md +++ b/skills/hyperstack/SKILL.md @@ -1,5 +1,5 @@ --- -name: using-hyperstack +name: 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. --- @@ -12,11 +12,14 @@ 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. +Hyperstack is a **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. -**You do not have a choice. You cannot rationalize your way out of this.** +**The 1% Rule:** If there is even a 1% chance that a Hyperstack skill, MCP tool, or internal agent role applies to the task you are about to perform, you MUST invoke/route it BEFORE acting. Not after you "check the code quickly." Not after you "just try one thing." Not after you "confirm your understanding." BEFORE. -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.** +**You do not have a choice. You cannot rationalize your way out of this.** --- @@ -35,7 +38,9 @@ If the task involves React, React Flow, Motion, Lenis, Echo, Go, Rust, design to 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. + +5. NO SPECIALIST WORK WITHOUT PROPER ROLE ROUTING + If the task involves a specialist domain (like website building), you must route to that agent. ``` **Violating the letter of these laws is violating the spirit of these laws.** @@ -175,20 +180,31 @@ Debugging: debug-discipline β†’ parallel-dispatch (if independent failur 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. +**Platform tool equivalences:** See `skills/hyperstack/references/` for tool name mappings per harness. --- -## Internal Agent Harness +## Layer 3: Agents (Orchestration & Routing) + +Hyperstack uses internal roles to manage complexity. These roles are **internal and auto-invoked**. +The bootstrap and orchestrator (`hyper`) choose the correct role based on the request and lifecycle state. -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. +| Agent | Category | Owns | +|---|---|---| +| `hyper` | Core | Classification, routing, gate enforcement, final verification, delivery. | +| `website-builder` | Specialist | Website-facing design/implementation, CTA hierarchy, page structure. | + +### Role Hierarchy & Hand-off -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. +1. Every request starts in `hyper`. +2. `hyper` classifies and delegates to specialists (e.g., `website-builder`) when domain-specific work is detected. +3. Specialists implement the work following Layers 1 & 2 (MCP + Skills). +4. Specialists **MUST** hand back to `hyper` for final verification and ship-gate. +5. `hyper` performs the final check and delivers to the user. + +--- -## Role Registry +## Role Registry Details - `hyper` - conductor, classifier, gatekeeper, verifier, and delivery owner - `website-builder` - first specialist for website-facing design and 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/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..ab73278 100644 --- a/src/internal/context-compiler.ts +++ b/src/internal/context-compiler.ts @@ -175,7 +175,7 @@ export function compileUsingHyperstackBootstrap(source: string): { content: stri const redFlags = extractRedFlags(body); const content = [ - "", + "", "# Hyperstack Runtime Bootstrap", "", "## Critical", @@ -236,8 +236,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/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/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/role-harness-behaviour.test.ts b/tests/role-harness-behaviour.test.ts index f223911..d47fa3d 100644 --- a/tests/role-harness-behaviour.test.ts +++ b/tests/role-harness-behaviour.test.ts @@ -73,8 +73,8 @@ test("role lifecycle and checks documents expose required headings", () => { 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); From 1d729320de46ea17a5d30c6a54e135f2bc844008 Mon Sep 17 00:00:00 2001 From: Kailas Mahavarkar <66670953+KailasMahavarkar@users.noreply.github.com> Date: Fri, 17 Apr 2026 02:50:29 +0530 Subject: [PATCH 02/23] Stabilize hyperstack baseline compiler --- .../runtime-context/hyperstack.bootstrap.md | 134 ++++++++---------- skills/INDEX.md | 9 +- src/internal/context-compiler.ts | 98 ++++++++++--- 3 files changed, 143 insertions(+), 98 deletions(-) diff --git a/generated/runtime-context/hyperstack.bootstrap.md b/generated/runtime-context/hyperstack.bootstrap.md index 8333360..45d2b7e 100644 --- a/generated/runtime-context/hyperstack.bootstrap.md +++ b/generated/runtime-context/hyperstack.bootstrap.md @@ -2,104 +2,88 @@ # Hyperstack Runtime Bootstrap ## Critical -You have Hyperstack. This is not optional knowledge - it is how you operate in this repository. +Hyperstack is active. This constitutes the mandatory operational framework for this repository. -**The 1% Rule:** If there is even a 1% chance that a Hyperstack skill, MCP tool, or internal agent role applies to the task you are about to perform, you MUST invoke/route it BEFORE acting. Not after you "check the code quickly." Not after you "just try one thing." Not after you "confirm your understanding." BEFORE. +**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. -**You do not have a choice. You cannot rationalize your way out of this.** +**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. ## Iron Laws ``` 1. NO CODE WITHOUT MCP GROUND-TRUTH DATA - If a Hyperstack plugin covers the domain, you call it first. + Call relevant Hyperstack plugins prior to implementation. 2. NO VISUAL CODE WITHOUT AN APPROVED DESIGN.md - The designer skill produces the contract. Everything else reads it. + The designer skill produces the contract; everything else implements it. 3. NO COMPLETION CLAIMS WITHOUT SHIP-GATE EVIDENCE - "Should work" is lying. Run the command. Show the output. + "Should work" is unacceptable. Execute the command and output results. 4. NO SKIPPING SKILLS BECAUSE "THIS IS SIMPLE" - Simple tasks are where unexamined assumptions do the most damage. + Simple tasks hide unexamined assumptions that cause the most damage. 5. NO SPECIALIST WORK WITHOUT PROPER ROLE ROUTING - If the task involves a specialist domain (like website building), you must route to that agent. + Route specialist domain tasks (e.g., website building) to the corresponding agent. ``` ## Instruction Priority -- **User's explicit instructions** (Project rules, direct requests) - always highest -- **Hyperstack skills** - override default system behavior where they conflict -- **Default system behavior** - lowest priority +- **User's explicit instructions** (Project rules, direct requests) - Maximum priority. +- **Hyperstack skills** - Overrides system defaults. +- **Default system behavior** - Minimum priority. ## MCP Must-Call-First -- `designer_*` -> `designer_resolve_intent`, `designer_get_personality`, `designer_get_preset`, `designer_get_page_template`, `designer_get_font_pairing`, `designer_get_anti_patterns` -- `design_tokens_*` -> `design_tokens_generate`, `design_tokens_get_category`, `design_tokens_get_gotchas` -- `ui_ux_*` -> `ui_ux_get_principle`, `ui_ux_get_component_pattern`, `ui_ux_get_gotchas` -- `shadcn_*` (**only if shadcn chosen**) -> `shadcn_get_rules` (first), `shadcn_get_composition`, `shadcn_get_component`, `shadcn_get_snippet`, `shadcn_list_components` -- `reactflow_*` -> `reactflow_get_api`, `reactflow_search_docs`, `reactflow_get_pattern` -- `motion_*` -> `motion_get_api`, `motion_get_examples`, `motion_get_transitions` -- `lenis_*` -> `lenis_get_api`, `lenis_generate_setup`, `lenis_get_pattern` -- `react_*` -> `react_get_pattern`, `react_get_constraints`, `react_search_docs` -- `echo_*` -> `echo_get_recipe`, `echo_get_middleware`, `echo_decision_matrix` -- `golang_*` -> `golang_get_practice`, `golang_get_pattern`, `golang_get_antipatterns` -- `rust_*` -> `rust_get_practice`, `rust_cheatsheet`, `rust_search_docs` +- `designer_*` -> `resolve_intent`, `get_personality`, `get_preset`, `get_page_template`, `get_font_pairing`, `get_anti_patterns` +- `design_tokens_*` -> `generate`, `get_category`, `get_gotchas` +- `ui_ux_*` -> `get_principle`, `get_component_pattern`, `get_gotchas` +- `shadcn_*` (if selected) -> `get_rules` (FIRST), `get_composition`, `get_component`, `get_snippet`, `list_components` +- `reactflow_*` -> `get_api`, `search_docs`, `get_pattern` +- `motion_*` -> `get_api`, `get_examples`, `get_transitions` +- `lenis_*` -> `get_api`, `generate_setup`, `get_pattern` +- `react_*` -> `get_pattern`, `get_constraints`, `search_docs` +- `echo_*` -> `get_recipe`, `get_middleware`, `decision_matrix` +- `golang_*` -> `get_practice`, `get_pattern`, `get_antipatterns` +- `rust_*` -> `get_practice`, `cheatsheet`, `search_docs` ## Workflow Skills -- `hyperstack:blueprint`: Before any feature build - MCP survey, design gate, negative doubt -- `hyperstack:designer`: Before any visual/UX work - produces DESIGN.md contract -- `hyperstack:forge-plan`: After design approval - MCP-verified implementation plan -- `hyperstack:run-plan`: Have an existing plan - validate then execute -- `hyperstack:engineering-discipline`: During execution - Senior SDE phase gates -- `hyperstack:ship-gate`: Before any completion claim - evidence required -- `hyperstack:deliver`: After all tasks complete - final verification and delivery -- `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 -- `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 - -## Layer 3: Agents (Orchestration & Routing) -- Hyperstack uses internal roles to manage complexity. These roles are internal and auto-invoked. -- `hyper` - Core: Classification, routing, gate enforcement, final verification, delivery. -- `website-builder` - Specialist: Website-facing design/implementation, CTA hierarchy, page structure. -- Every request starts in `hyper`. -- `hyper` classifies and delegates to specialists (e.g., `website-builder`) when domain-specific work is detected. -- Specialists MUST hand back to `hyper` for final verification and ship-gate. - -## Routing Summary -- Every request enters through `hyper` -- `hyper` inspects the workspace first: package manifests, dependency signals, -- `hyper -> website-builder` for website-facing work: landing pages, dashboards, -- `website-builder -> hyper` after specialist output is ready for review and -- 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` +- Starting any new feature, component, or behaviour change: `blueprint` +- Task involves UI, animation, visuals, or interaction: `designer` β†’ produces DESIGN.md first +- 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` + +## Internal Agents +- Roles are internal and auto-called. Users do not invoke them directly. +- Internal roles are auto-called, not user-facing. +- hyper -> website-builder +- `hyper`: Classification, routing, gate enforcement, final verification, delivery +- `website-builder`: Website-facing design/implementation, CTA hierarchy, page structure ## Disallowed Transitions - `user request -> website-builder` - `website-builder -> ship` - `website-builder -> deliver` -- `website-builder` claiming final completion directly ## High-Signal Red Flags -- "I know this React Flow API from memory" -> Memory drifts. v11 and v12 are different. -- "This is a simple animation" -> Simple animations need `prefers-reduced-motion`, correct easing, and GPU-only properties -- "Go error handling is straightforward" -> Straightforward code is where anti-patterns ship -- "I'll check docs after I write it" -> You will ship before you check. Every time. -- "I know the OKLCH token pattern" -> OKLCH has specific rules about alpha, chroma peaks, dark mode lightness -- "This pattern looks common, I'll adapt it" -> Adaptation hides drift +- "I know the React Flow API from memory." -> Memory drifts. v11 and v12 differ. +- "This is a simple animation." -> Animations require `prefers-reduced-motion`, easing, and GPU properties. +- "Go error handling is straightforward." -> Straightforward code hosts anti-patterns. +- "I'll check docs after writing." -> Shipping occurs before checking. +- "I know the OKLCH token pattern." -> OKLCH dictates specific alpha, chroma, and lightness rules. +- "This pattern is common; I'll adapt it." -> Adaptation obscures structural drift. ## Degraded Mode - If MCP unavailable, tell the user explicitly: "MCP unavailable" and flag answers as uncertain. @@ -108,8 +92,8 @@ You have Hyperstack. This is not optional knowledge - it is how you operate in t - Before invoking any Hyperstack skill, announce it with the exact format and purpose so the user can audit it. ## Final Check -- [ ] Did I check whether any Hyperstack skill applies to this task? (1% rule) -- [ ] Did I call any relevant MCP tool for ground-truth data? (memory is not acceptable) -- [ ] If this involves visual work, did I invoke designer BEFORE writing any code? -- [ ] If I'm claiming something is done, did I run the verification command THIS message? -- [ ] Did I announce every skill invocation with the exact format? +- [ ] Did I evaluate Hyperstack skill applicability? (1% rule) +- [ ] Did I execute relevant MCP tools? (No memory rely) +- [ ] If visual, did I invoke `designer` BEFORE coding? +- [ ] If claiming completion, did I execute verification THIS message? +- [ ] Did I announce skill invocation with exact formatting? diff --git a/skills/INDEX.md b/skills/INDEX.md index e467193..b2823fb 100644 --- a/skills/INDEX.md +++ b/skills/INDEX.md @@ -15,15 +15,15 @@ Categories: | Skill | Description | |---|---| | `autonomous-mode` | Use when the user chooses fully autonomous execution. Aggressively uses the entire Hyperstack to implement the solution | -| `blueprint` | Use before any feature build, component creation, or behaviour modification. MCP-surveyed design with a hard gate before | +| `blueprint` | Execute before any feature build, component creation, or behavior change. Performs MCP survey and enforces a hard design | | `code-review` | Use when completing tasks, implementing features, or before merging - to dispatch a review subagent and handle feedback | | `debug-discipline` | Use when encountering any bug, test failure, or unexpected behaviour. Root cause investigation is mandatory before any f | | `deliver` | Use after all implementation tasks are complete. Runs final verification, confirms the branch is clean, and executes the | | `engineering-discipline` | Apply senior-level software engineering discipline including design patterns, SOLID principles, architectural reasoning, | -| `forge-plan` | Use after blueprint design approval to produce a task-by-task implementation plan grounded in MCP-verified API calls. No | +| `forge-plan` | Executed after blueprint/designer approval. Produces task-by-task implementation plan grounded in MCP-verified APIs. Zer | | `parallel-dispatch` | Use when facing 2+ independent tasks that can be investigated or executed without shared state or sequential dependencie | | `run-plan` | Use when you have an existing plan, spec, or task list to execute. Validates the plan for gaps and MCP accuracy before a | -| `ship-gate` | Use before claiming any work is complete, fixed, or passing. Run the verification command and show output before making | +| `ship-gate` | Execute before claiming work is complete, fixed, or passing. Run the verification command and show output. No evidence = | | `subagent-ops` | Use when executing implementation plans with independent tasks. Dispatches one fresh subagent per task with two-stage re | | `test-first` | Use when implementing any feature, bug fix, or behaviour change - before writing implementation code. Enforces test-befo | | `worktree-isolation` | Use when starting feature work that needs isolation from the current workspace, or before executing implementation plans | @@ -33,7 +33,6 @@ Categories: | Skill | Description | |---|---| | `behaviour-analysis` | Systematic UI/UX behaviour analysis for interactive applications. Audits every user action, state transition, view mode, | -| `design-patterns-skill` | Apply core programming principles and design patterns from Clean Code, The Pragmatic Programmer, Code Complete, Refactor | | `designer` | | | `readme-writer` | Writes or rewrites project README files using repository evidence instead of generic filler. Use when creating a new REA | | `security-review` | Security code review for vulnerabilities. Use when asked to "security review", "find vulnerabilities", "check for securi | @@ -43,7 +42,7 @@ Categories: | Skill | Description | |---|---| +| `hyperstack` | Bootstrap definitions establishing MCP tools and skills prior to work. Auto-loaded at session start. Do not bypass or ra | | `testing-skills` | Use when creating or editing Hyperstack skills, before shipping them, to verify they actually work under pressure and re | -| `hyperstack` | Bootstrap - establishes Hyperstack MCP tools and skills before any technical work. Auto-loaded at session start via Sess | diff --git a/src/internal/context-compiler.ts b/src/internal/context-compiler.ts index ab73278..d1987a1 100644 --- a/src/internal/context-compiler.ts +++ b/src/internal/context-compiler.ts @@ -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("website-builder")) { + output.push("- hyper -> website-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,9 +237,7 @@ 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); @@ -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, "", From e1bc9e5395b105c69eea67b21c3bd468b91652b5 Mon Sep 17 00:00:00 2001 From: Kailas Mahavarkar <66670953+KailasMahavarkar@users.noreply.github.com> Date: Fri, 17 Apr 2026 02:53:49 +0530 Subject: [PATCH 03/23] Verify bootstrap artifact sync From 0272aecf79e2d780d7226818c55f3f05549aa80f Mon Sep 17 00:00:00 2001 From: Kailas Mahavarkar <66670953+KailasMahavarkar@users.noreply.github.com> Date: Fri, 17 Apr 2026 02:56:10 +0530 Subject: [PATCH 04/23] fix: stabilize baseline compiler artifacts --- .../runtime-context/hyperstack.bootstrap.md | 32 +++--- skills/INDEX.md | 2 +- src/internal/context-compiler.ts | 98 +++++++++++++++---- 3 files changed, 92 insertions(+), 40 deletions(-) diff --git a/generated/runtime-context/hyperstack.bootstrap.md b/generated/runtime-context/hyperstack.bootstrap.md index 8333360..d811037 100644 --- a/generated/runtime-context/hyperstack.bootstrap.md +++ b/generated/runtime-context/hyperstack.bootstrap.md @@ -4,6 +4,11 @@ ## Critical You have Hyperstack. This is not optional knowledge - it is how you operate in this repository. +Hyperstack is a **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 there is even a 1% chance that a Hyperstack skill, MCP tool, or internal agent role applies to the task you are about to perform, you MUST invoke/route 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.** @@ -65,27 +70,12 @@ You have Hyperstack. This is not optional knowledge - it is how you operate in t - `hyperstack:security-review`: OWASP audits, API and infrastructure security - `hyperstack:readme-writer`: Evidence-based documentation -## Layer 3: Agents (Orchestration & Routing) -- Hyperstack uses internal roles to manage complexity. These roles are internal and auto-invoked. -- `hyper` - Core: Classification, routing, gate enforcement, final verification, delivery. -- `website-builder` - Specialist: Website-facing design/implementation, CTA hierarchy, page structure. -- Every request starts in `hyper`. -- `hyper` classifies and delegates to specialists (e.g., `website-builder`) when domain-specific work is detected. -- Specialists MUST hand back to `hyper` for final verification and ship-gate. - -## Routing Summary -- Every request enters through `hyper` -- `hyper` inspects the workspace first: package manifests, dependency signals, -- `hyper -> website-builder` for website-facing work: landing pages, dashboards, -- `website-builder -> hyper` after specialist output is ready for review and -- 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` +## Internal Agents +- Roles are internal and auto-called. Users do not invoke them directly. +- Internal roles are auto-called, not user-facing. +- hyper -> website-builder +- `hyper`: Core +- `website-builder`: Specialist ## Disallowed Transitions - `user request -> website-builder` diff --git a/skills/INDEX.md b/skills/INDEX.md index e467193..8bbde5e 100644 --- a/skills/INDEX.md +++ b/skills/INDEX.md @@ -43,7 +43,7 @@ Categories: | Skill | Description | |---|---| -| `testing-skills` | Use when creating or editing Hyperstack skills, before shipping them, to verify they actually work under pressure and re | | `hyperstack` | Bootstrap - establishes Hyperstack MCP tools and skills before any technical work. Auto-loaded at session start via Sess | +| `testing-skills` | Use when creating or editing Hyperstack skills, before shipping them, to verify they actually work under pressure and re | diff --git a/src/internal/context-compiler.ts b/src/internal/context-compiler.ts index ab73278..d1987a1 100644 --- a/src/internal/context-compiler.ts +++ b/src/internal/context-compiler.ts @@ -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("website-builder")) { + output.push("- hyper -> website-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,9 +237,7 @@ 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); @@ -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, "", From 8d2ef971e9ccdbbb2b8b24b41e910025faeb609c Mon Sep 17 00:00:00 2001 From: Kailas Mahavarkar <66670953+KailasMahavarkar@users.noreply.github.com> Date: Fri, 17 Apr 2026 02:56:26 +0530 Subject: [PATCH 05/23] docs: add topology migration spec and plan --- ...-17-topology-manifest-runtime-migration.md | 948 ++++++++++++++++++ .../2026-04-17-topology-manifest-design.md | 693 +++++++++++++ 2 files changed, 1641 insertions(+) create mode 100644 docs/superpowers/plans/2026-04-17-topology-manifest-runtime-migration.md create mode 100644 docs/superpowers/specs/2026-04-17-topology-manifest-design.md diff --git a/docs/superpowers/plans/2026-04-17-topology-manifest-runtime-migration.md b/docs/superpowers/plans/2026-04-17-topology-manifest-runtime-migration.md new file mode 100644 index 0000000..829761b --- /dev/null +++ b/docs/superpowers/plans/2026-04-17-topology-manifest-runtime-migration.md @@ -0,0 +1,948 @@ +# Topology Manifest Runtime Migration Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Replace the Docker-MCP-first runtime with a topology-manifest-driven local navigation engine that preserves stable tool-call names, enforces agent/skill/domain contracts, and keeps the researched corpus intact. + +**Architecture:** Keep deep researched content on disk, move runtime wiring into `topology/`, and add a local tool adapter layer that invokes existing tool modules directly through a production bridge instead of an MCP server. Implement this in small phases: manifest + loader, policy resolver, direct tool bridge, generated local tool registry, local CLI runtime, topology artifact generation, then cut Docker-first setup and stale tests. + +**Tech Stack:** TypeScript, Bun, Bun test, Zod, YAML parsing, existing `src/plugins/*/tools/*.ts` tool modules, existing `bin/hyperstack.mjs` + +--- + +## File Structure Lock-In + +### New Files + +- `topology/manifest.yaml` +- `topology/domains/frontend.yaml` +- `topology/domains/backend.yaml` +- `topology/domains/shared.yaml` +- `topology/agents/hyper.yaml` +- `topology/agents/frontend-builder.yaml` +- `topology/agents/backend-builder.yaml` +- `topology/agents/fullstack-builder.yaml` +- `topology/skills/groups.yaml` +- `topology/skills/policies.yaml` +- `topology/bundles/shared.system.yaml` +- `topology/bundles/frontend.design.yaml` +- `topology/bundles/frontend.react.yaml` +- `topology/bundles/backend.http.yaml` +- `topology/bundles/backend.lang.go.yaml` +- `topology/bundles/backend.lang.rust.yaml` +- `corpus/frontend/README.md` +- `corpus/backend/README.md` +- `corpus/shared/README.md` +- `src/engine/contracts.ts` +- `src/engine/topology-loader.ts` +- `src/engine/policy.ts` +- `src/engine/resolver.ts` +- `src/engine/injector.ts` +- `src/engine/tool-bridge.ts` +- `src/engine/navigation.ts` +- `src/adapters/local-tools/index.ts` +- `src/cli.ts` +- `scripts/generate-local-tool-registry.ts` +- `scripts/generate-topology-artifacts.ts` +- `tests/topology-manifest-behaviour.test.ts` +- `tests/tool-bridge-behaviour.test.ts` +- `tests/local-cli-behaviour.test.ts` +- `tests/topology-artifacts-behaviour.test.ts` + +### Generated Files + +- `generated/tool-index/local-tool-registry.ts` +- `generated/tool-index/local-tool-registry.json` +- `generated/runtime-context/topology.bootstrap.md` +- `generated/routing/allow-deny.md` + +### Files To Modify + +- `package.json` +- `bin/hyperstack.mjs` +- `src/index.ts` +- `src/internal/setup-hyperstack.ts` +- `scripts/setup.ts` +- `hooks/session-start.mjs` +- `tests/runtime-behaviour.test.ts` +- `tests/plugin-registry-behaviour.test.ts` +- `tests/context-compiler-behaviour.test.ts` +- `tests/role-harness-behaviour.test.ts` +- `README.md` + +### Responsibility Boundaries + +- `topology/` is the source of truth for runtime contracts and routing. +- `corpus/` holds file-backed knowledge roots and migration markers; V1 can reference legacy plugin data from these roots instead of rewriting all research immediately. +- `src/engine/` owns loading, policy enforcement, capability resolution, injection, and direct tool bridging. +- `scripts/` own generation of local tool registry and topology artifacts. +- `generated/` is derived only; never hand-edit. +- `src/plugins/` remains the existing research/tool implementation surface reused by the direct tool bridge during migration. + +--- + +### Task 1: Add Topology Manifest Scaffolding and Loader + +**Files:** +- Create: `topology/manifest.yaml` +- Create: `topology/domains/frontend.yaml` +- Create: `topology/domains/backend.yaml` +- Create: `topology/domains/shared.yaml` +- Create: `topology/agents/hyper.yaml` +- Create: `topology/agents/frontend-builder.yaml` +- Create: `topology/agents/backend-builder.yaml` +- Create: `topology/agents/fullstack-builder.yaml` +- Create: `topology/skills/groups.yaml` +- Create: `topology/skills/policies.yaml` +- Create: `topology/bundles/shared.system.yaml` +- Create: `topology/bundles/frontend.design.yaml` +- Create: `topology/bundles/frontend.react.yaml` +- Create: `topology/bundles/backend.http.yaml` +- Create: `topology/bundles/backend.lang.go.yaml` +- Create: `topology/bundles/backend.lang.rust.yaml` +- Create: `corpus/frontend/README.md` +- Create: `corpus/backend/README.md` +- Create: `corpus/shared/README.md` +- Create: `src/engine/contracts.ts` +- Create: `src/engine/topology-loader.ts` +- Modify: `package.json` +- Test: `tests/topology-manifest-behaviour.test.ts` + +- [ ] **Step 1: Write the failing topology loader test** + +```ts +import { expect, test } from "bun:test"; +import { loadTopology } from "../src/engine/topology-loader.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"); +}); +``` + +- [ ] **Step 2: Run the test to verify it fails** + +Run: `bun test tests/topology-manifest-behaviour.test.ts` +Expected: FAIL with `Cannot find module "../src/engine/topology-loader.ts"` or `loadTopology is not defined` + +- [ ] **Step 3: Add YAML parsing dependency and topology files** + +```json +{ + "dependencies": { + "@modelcontextprotocol/sdk": "^1.17.0", + "tsx": "^4.21.0", + "yaml": "^2.5.1", + "zod": "^3.23.0" + } +} +``` + +```yaml +# topology/manifest.yaml +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 +``` + +```yaml +# topology/domains/frontend.yaml +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 +``` + +- [ ] **Step 4: Implement topology contracts and loader** + +```ts +// src/engine/contracts.ts +export interface TopologyRoot { + version: 1; + defaultTransport: "local-tools"; + entryAgent: string; + domains: string[]; + agents: string[]; + bundles: 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 LoadedTopology { + version: 1; + defaultTransport: "local-tools"; + entryAgent: string; + domains: DomainPolicy[]; + agents: AgentPolicy[]; + bundles: BundlePolicy[]; +} +``` + +```ts +// src/engine/topology-loader.ts +import { readFileSync } from "node:fs"; +import { join } from "node:path"; +import YAML from "yaml"; +import type { AgentPolicy, BundlePolicy, DomainPolicy, LoadedTopology, TopologyRoot } from "./contracts.js"; + +function readYaml(filePath: string): T { + return YAML.parse(readFileSync(filePath, "utf8")) as T; +} + +export function loadTopology(repoRoot: string): LoadedTopology { + const root = readYaml(join(repoRoot, "topology", "manifest.yaml")); + const domains = root.domains.map((id) => readYaml(join(repoRoot, "topology", "domains", `${id}.yaml`))); + const agents = root.agents.map((id) => readYaml(join(repoRoot, "topology", "agents", `${id}.yaml`))); + const bundles = root.bundles.map((id) => readYaml(join(repoRoot, "topology", "bundles", `${id}.yaml`))); + + return { + version: root.version, + defaultTransport: root.defaultTransport, + entryAgent: root.entryAgent, + domains, + agents, + bundles, + }; +} +``` + +- [ ] **Step 5: Run the test and typecheck** + +Run: `bun test tests/topology-manifest-behaviour.test.ts && bun run build` +Expected: test PASS, `tsc --noEmit` exits `0` + +- [ ] **Step 6: Commit** + +```bash +git add package.json bun.lock topology corpus src/engine/contracts.ts src/engine/topology-loader.ts tests/topology-manifest-behaviour.test.ts +git commit -m "feat: add topology manifest scaffolding and loader" +``` + +--- + +### Task 2: Enforce Domain Policy and Capability Resolution + +**Files:** +- Create: `src/engine/policy.ts` +- Create: `src/engine/resolver.ts` +- Modify: `src/engine/contracts.ts` +- Test: `tests/topology-manifest-behaviour.test.ts` + +- [ ] **Step 1: Add a failing resolver test** + +```ts +import { expect, test } from "bun:test"; +import { loadTopology } from "../src/engine/topology-loader.ts"; +import { resolveCapabilityContext } from "../src/engine/resolver.ts"; + +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"); +}); +``` + +- [ ] **Step 2: Run the test to verify it fails** + +Run: `bun test tests/topology-manifest-behaviour.test.ts` +Expected: FAIL with `Cannot find module "../src/engine/resolver.ts"` or missing export + +- [ ] **Step 3: Implement policy helpers** + +```ts +// src/engine/policy.ts +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; +} +``` + +- [ ] **Step 4: Implement capability resolver with allow/deny enforcement** + +```ts +// src/engine/resolver.ts +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} cannot access 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 }; +} +``` + +- [ ] **Step 5: Re-run the focused test suite** + +Run: `bun test tests/topology-manifest-behaviour.test.ts` +Expected: PASS for loader + resolver assertions + +- [ ] **Step 6: Commit** + +```bash +git add src/engine/policy.ts src/engine/resolver.ts src/engine/contracts.ts tests/topology-manifest-behaviour.test.ts +git commit -m "feat: add topology policy and capability resolver" +``` + +--- + +### Task 3: Add a Production Direct Tool Bridge for Existing Tool Modules + +**Files:** +- Create: `src/engine/tool-bridge.ts` +- Test: `tests/tool-bridge-behaviour.test.ts` +- Reference: existing `src/plugins/*/tools/*.ts` + +- [ ] **Step 1: Write the failing tool bridge test** + +```ts +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); +}); +``` + +- [ ] **Step 2: Run the test to verify it fails** + +Run: `bun test tests/tool-bridge-behaviour.test.ts` +Expected: FAIL with `Cannot find module "../src/engine/tool-bridge.ts"` + +- [ ] **Step 3: Implement production tool capture** + +```ts +// src/engine/tool-bridge.ts +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); +} +``` + +- [ ] **Step 4: Run the tool bridge test** + +Run: `bun test tests/tool-bridge-behaviour.test.ts` +Expected: PASS for both direct-invocation cases + +- [ ] **Step 5: Commit** + +```bash +git add src/engine/tool-bridge.ts tests/tool-bridge-behaviour.test.ts +git commit -m "feat: add direct bridge for local tool invocation" +``` + +--- + +### Task 4: Generate and Use a Stable Local Tool Registry + +**Files:** +- Create: `scripts/generate-local-tool-registry.ts` +- Create: `src/adapters/local-tools/index.ts` +- Create: `generated/tool-index/local-tool-registry.ts` +- Create: `generated/tool-index/local-tool-registry.json` +- Modify: `package.json` +- Test: `tests/topology-artifacts-behaviour.test.ts` + +- [ ] **Step 1: Write the failing registry generation test** + +```ts +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"); +}); +``` + +- [ ] **Step 2: Run the test to verify it fails** + +Run: `bun test tests/topology-artifacts-behaviour.test.ts` +Expected: FAIL because the generated registry files do not exist + +- [ ] **Step 3: Add generation script entries to package.json** + +```json +{ + "scripts": { + "generate:local-tools": "tsx scripts/generate-local-tool-registry.ts", + "generate:topology": "tsx scripts/generate-topology-artifacts.ts" + } +} +``` + +- [ ] **Step 4: Implement registry generation** + +```ts +// scripts/generate-local-tool-registry.ts +import { mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs"; +import { join, resolve } from "node:path"; + +const repoRoot = resolve("."); +const pluginsRoot = join(repoRoot, "src", "plugins"); +const outputJson = join(repoRoot, "generated", "tool-index", "local-tool-registry.json"); +const outputTs = join(repoRoot, "generated", "tool-index", "local-tool-registry.ts"); + +const entries: Array<{ name: string; importPath: string; exportName: string }> = []; + +for (const plugin of readdirSync(pluginsRoot)) { + const toolsDir = join(pluginsRoot, plugin, "tools"); + try { + for (const file of readdirSync(toolsDir)) { + if (!file.endsWith(".ts")) continue; + const fullPath = join(toolsDir, file); + const source = readFileSync(fullPath, "utf8"); + const match = source.match(/server\\.tool\\(\\s*"([^"]+)"/); + if (!match?.[1]) continue; + entries.push({ + name: match[1], + importPath: `../../src/plugins/${plugin}/tools/${file.replace(/\\.ts$/, ".js")}`, + exportName: "register", + }); + } + } catch {} +} + +mkdirSync(join(repoRoot, "generated", "tool-index"), { recursive: true }); +writeFileSync(outputJson, JSON.stringify(Object.fromEntries(entries.map((entry) => [entry.name, entry.importPath])), null, 2)); +writeFileSync( + outputTs, + [ + "export const LOCAL_TOOL_REGISTRY = {", + ...entries.map((entry) => ` "${entry.name}": () => import("${entry.importPath}"),`), + "} as const;", + "", + ].join("\n"), +); +``` + +- [ ] **Step 5: Add local adapter entrypoint** + +```ts +// src/adapters/local-tools/index.ts +import { LOCAL_TOOL_REGISTRY } from "../../generated/tool-index/local-tool-registry.js"; +import { invokeRegisteredTool } from "../../src/engine/tool-bridge.js"; + +export async function invokeLocalTool(name: string, args: Record) { + const loader = LOCAL_TOOL_REGISTRY[name as keyof typeof LOCAL_TOOL_REGISTRY]; + if (!loader) throw new Error(`Unknown local tool: ${name}`); + const module = await loader(); + return invokeRegisteredTool(module.register, args); +} +``` + +- [ ] **Step 6: Generate the registry and run the test** + +Run: `bun run generate:local-tools && bun test tests/topology-artifacts-behaviour.test.ts` +Expected: generator writes both files, test PASS + +- [ ] **Step 7: Commit** + +```bash +git add package.json scripts/generate-local-tool-registry.ts src/adapters/local-tools/index.ts generated/tool-index tests/topology-artifacts-behaviour.test.ts +git commit -m "feat: generate stable local tool registry" +``` + +--- + +### Task 5: Build Local CLI Runtime and Replace Server-First Entry + +**Files:** +- Create: `src/cli.ts` +- Modify: `bin/hyperstack.mjs` +- Modify: `src/index.ts` +- Test: `tests/local-cli-behaviour.test.ts` +- Test: `tests/runtime-behaviour.test.ts` + +- [ ] **Step 1: Write the failing CLI test** + +```ts +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/); +}); +``` + +- [ ] **Step 2: Run the test to verify it fails** + +Run: `bun test tests/local-cli-behaviour.test.ts` +Expected: FAIL because `bin/hyperstack.mjs` still points at the old runtime + +- [ ] **Step 3: Implement CLI runtime** + +```ts +// src/cli.ts +import { invokeLocalTool } from "./adapters/local-tools/index.js"; + +async function main() { + const [command, toolName, flag, json] = process.argv.slice(2); + + if (command !== "tool" || !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`); +} + +main().catch((error) => { + process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`); + process.exit(1); +}); +``` + +- [ ] **Step 4: Redirect the binary to the CLI entrypoint** + +```js +// bin/hyperstack.mjs +#!/usr/bin/env node + +import { spawn } from "node:child_process"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +const binDir = dirname(fileURLToPath(import.meta.url)); +const rootDir = resolve(binDir, ".."); +const entrypoint = resolve(rootDir, "src/cli.ts"); +const nodeMajor = Number.parseInt(process.versions.node.split(".")[0] ?? "0", 10); +const tsxLoaderArgs = nodeMajor >= 20 ? ["--import", "tsx"] : ["--loader", "tsx"]; + +const child = spawn(process.execPath, [...tsxLoaderArgs, entrypoint, ...process.argv.slice(2)], { + cwd: rootDir, + env: process.env, + stdio: "inherit", +}); + +child.on("exit", (code) => process.exit(code ?? 1)); +child.on("error", (error) => { + console.error("Failed to start hyperstack:", error); + process.exit(1); +}); +``` + +- [ ] **Step 5: Preserve compatibility by making `src/index.ts` a thin re-export or deprecation shim** + +```ts +// src/index.ts +export { invokeLocalTool } from "./adapters/local-tools/index.js"; +``` + +- [ ] **Step 6: Run CLI and runtime tests** + +Run: `bun test tests/local-cli-behaviour.test.ts tests/runtime-behaviour.test.ts` +Expected: PASS, binary starts, local CLI returns JSON output + +- [ ] **Step 7: Commit** + +```bash +git add src/cli.ts bin/hyperstack.mjs src/index.ts tests/local-cli-behaviour.test.ts tests/runtime-behaviour.test.ts +git commit -m "feat: switch hyperstack binary to local tool runtime" +``` + +--- + +### Task 6: Generate Topology Artifacts and Session Bootstrap from Manifest + +**Files:** +- Create: `src/engine/navigation.ts` +- Create: `src/engine/injector.ts` +- Create: `scripts/generate-topology-artifacts.ts` +- Create: `generated/runtime-context/topology.bootstrap.md` +- Create: `generated/routing/allow-deny.md` +- Modify: `hooks/session-start.mjs` +- Modify: `tests/context-compiler-behaviour.test.ts` +- Modify: `tests/role-harness-behaviour.test.ts` +- Test: `tests/topology-artifacts-behaviour.test.ts` + +- [ ] **Step 1: Write the failing topology artifact test** + +```ts +import { expect, test } from "bun:test"; +import { readFileSync } from "node:fs"; +import { resolve } from "node:path"; + +test("generated topology bootstrap includes agent and bundle routing markers", () => { + const bootstrap = readFileSync(resolve("generated/runtime-context/topology.bootstrap.md"), "utf8"); + expect(bootstrap).toMatch(/hyper/); + expect(bootstrap).toMatch(/frontend-builder/); + expect(bootstrap).toMatch(/backend-builder/); + expect(bootstrap).toMatch(/frontend\\.design/); +}); +``` + +- [ ] **Step 2: Run the test to verify it fails** + +Run: `bun test tests/topology-artifacts-behaviour.test.ts` +Expected: FAIL because topology bootstrap does not exist + +- [ ] **Step 3: Implement injection and artifact generation** + +```ts +// src/engine/injector.ts +import type { BundlePolicy } from "./contracts.js"; + +export function buildInjectionSlice(bundle: BundlePolicy, capability: string) { + return { + bundle: bundle.id, + capability, + sources: bundle.sources, + toolPrefixes: bundle.toolPrefixes, + }; +} +``` + +```ts +// scripts/generate-topology-artifacts.ts +import { mkdirSync, writeFileSync } from "node:fs"; +import { resolve } from "node:path"; +import { loadTopology } from "../src/engine/topology-loader.js"; + +const repoRoot = resolve("."); +const topology = loadTopology(repoRoot); + +mkdirSync(resolve("generated/runtime-context"), { recursive: true }); +mkdirSync(resolve("generated/routing"), { recursive: true }); + +writeFileSync( + resolve("generated/runtime-context/topology.bootstrap.md"), + [ + "# Topology Runtime Bootstrap", + "", + `Entry agent: ${topology.entryAgent}`, + "", + "## Agents", + ...topology.agents.map((agent) => `- ${agent.id}: ${agent.kind} -> ${agent.domains.join(", ")}`), + "", + "## Bundles", + ...topology.bundles.map((bundle) => `- ${bundle.id}: ${bundle.capabilities.join(", ")}`), + "", + ].join("\n"), +); + +writeFileSync( + resolve("generated/routing/allow-deny.md"), + [ + "# Allow / Deny Matrix", + "", + ...topology.agents.flatMap((agent) => [ + `## ${agent.id}`, + `- Allowed: ${agent.allowedBundles.join(", ")}`, + `- Forbidden: ${agent.forbiddenBundles.join(", ")}`, + "", + ]), + ].join("\n"), +); +``` + +- [ ] **Step 4: Make the session hook prefer topology bootstrap** + +```js +// hooks/session-start.mjs +const topologyBootstrapPath = join(pluginRoot, "generated", "runtime-context", "topology.bootstrap.md"); +const legacyBootstrapPath = join(pluginRoot, "generated", "runtime-context", "hyperstack.bootstrap.md"); + +try { + bootstrapContent = readFileSync(topologyBootstrapPath, "utf8"); + bootstrapLabel = "generated topology bootstrap"; +} catch { + try { + bootstrapContent = readFileSync(legacyBootstrapPath, "utf8"); + bootstrapLabel = "compiled runtime bootstrap"; + } catch { + bootstrapContent = readFileSync(fallbackSkillPath, "utf8"); + bootstrapLabel = "full content of your 'hyperstack:hyperstack' skill"; + } +} +``` + +- [ ] **Step 5: Generate artifacts and run targeted tests** + +Run: `bun run generate:topology && bun test tests/topology-artifacts-behaviour.test.ts tests/context-compiler-behaviour.test.ts tests/role-harness-behaviour.test.ts` +Expected: PASS; no stale heading/path assertions remain + +- [ ] **Step 6: Commit** + +```bash +git add src/engine/injector.ts src/engine/navigation.ts scripts/generate-topology-artifacts.ts hooks/session-start.mjs generated/runtime-context generated/routing tests/topology-artifacts-behaviour.test.ts tests/context-compiler-behaviour.test.ts tests/role-harness-behaviour.test.ts +git commit -m "feat: generate topology runtime artifacts and hook bootstrap" +``` + +--- + +### Task 7: Remove Docker-First Setup Defaults and Refresh Documentation + +**Files:** +- Modify: `src/internal/setup-hyperstack.ts` +- Modify: `scripts/setup.ts` +- Modify: `README.md` +- Modify: `tests/plugin-registry-behaviour.test.ts` +- Modify: `tests/runtime-behaviour.test.ts` +- Test: `tests/runtime-behaviour.test.ts` +- Test: `tests/workflow-behaviour.test.ts` + +- [ ] **Step 1: Write a failing setup test for local-tool default** + +```ts +import { expect, test } from "bun:test"; +import { generateMcpPatch } from "../src/internal/setup-hyperstack.ts"; + +test("generateMcpPatch defaults to local tool runtime instead of docker", () => { + const patch = generateMcpPatch("/tmp/config.json", "/repo", "cursor"); + + expect(JSON.stringify(patch.content)).toMatch(/hyperstack/); + expect(JSON.stringify(patch.content)).toMatch(/bin\\/hyperstack\\.mjs|src\\/cli\\.ts/); + expect(JSON.stringify(patch.content)).not.toMatch(/docker/); +}); +``` + +- [ ] **Step 2: Run the test to verify it fails** + +Run: `bun test tests/runtime-behaviour.test.ts` +Expected: FAIL because setup still emits Docker-first config + +- [ ] **Step 3: Update setup generation to prefer local transport** + +```ts +// src/internal/setup-hyperstack.ts +const localServerConfig = { + command: "node", + args: [binaryPath], + env: { HYPERSTACK_ROOT: pluginRoot }, +}; + +export function generateMcpPatch( + configPath: string, + pluginRoot: string, + platform: string, + method: "local" | "docker" = "local", +) { + const serverConfig = method === "docker" ? dockerServerConfig : localServerConfig; + // unchanged format logic below +} +``` + +```ts +// scripts/setup.ts +const patch = setup.generateMcpPatch(configPath, pluginRoot, platform, "local"); +``` + +- [ ] **Step 4: Refresh README and tests away from plugin-count/server-first language** + +```md +## Runtime + +Hyperstack now defaults to a local tool runtime backed by topology manifests and corpus navigation. +Docker is no longer required for the default setup path. +Stable tool-call names are preserved through local adapters. +``` + +```ts +// tests/plugin-registry-behaviour.test.ts +import { existsSync } from "node:fs"; +import { resolve } from "node:path"; + +test("generated local tool registry exists and includes stable tool names", () => { + expect(existsSync(resolve("generated/tool-index/local-tool-registry.json"))).toBe(true); +}); +``` + +- [ ] **Step 5: Run the final verification suite** + +Run: `bun run generate:local-tools && bun run generate:topology && bun test && bun run build` +Expected: all tests PASS, build exits `0` + +- [ ] **Step 6: Commit** + +```bash +git add src/internal/setup-hyperstack.ts scripts/setup.ts README.md tests/plugin-registry-behaviour.test.ts tests/runtime-behaviour.test.ts tests/workflow-behaviour.test.ts +git commit -m "refactor: remove docker-first runtime defaults" +``` + +--- + +## Spec Coverage Check + +- Spec repo shape is covered by Task 1 scaffolding plus Task 6 generated outputs. +- Domain policies and agent contracts are covered by Tasks 1 and 2. +- Stable local tool names are preserved by Tasks 3, 4, and 5. +- Topology bootstrap and routing artifacts are covered by Task 6. +- Docker-first setup removal is covered by Task 7. +- Frontend/backend asymmetry is enforced through domain policies and resolver tests in Tasks 1 and 2. + +## Placeholder Scan + +- No `TBD`, `TODO`, or deferred implementation placeholders are present in tasks. +- Each code-touching task includes a concrete code block. +- Each verification step includes an exact command and expected outcome. + +## Type Consistency Check + +- `loadTopology`, `resolveCapabilityContext`, and `invokeRegisteredTool` names are defined before they are reused. +- `task_handoff`, `build_result`, and `verification_report` are treated as artifact names, not runtime function names. +- `frontend-builder`, `backend-builder`, `fullstack-builder`, `frontend.design`, and `backend.http` naming stays consistent with the approved spec. + +## Notes for Implementation + +- Keep `src/plugins/*` intact during V1. Reuse them through the direct tool bridge first; migrate deep data into `corpus/` later. +- Do not rewrite the 30+ hours of researched content during topology cutover. +- Prefer small focused commits exactly as listed above. diff --git a/docs/superpowers/specs/2026-04-17-topology-manifest-design.md b/docs/superpowers/specs/2026-04-17-topology-manifest-design.md new file mode 100644 index 0000000..a1e1cb7 --- /dev/null +++ b/docs/superpowers/specs/2026-04-17-topology-manifest-design.md @@ -0,0 +1,693 @@ +# Topology Manifest V1 Design + +Date: 2026-04-17 +Status: Approved design, pre-implementation +Scope: Replace Docker-MCP-first wiring with a topology-manifest-driven local navigation engine while preserving stable tool-call identities + +## 1. Goal + +Hyperstack currently has three high-value assets: + +- researched domain truth +- strong skill/process discipline +- internal agent routing ideas + +The failure mode is not missing research. The failure mode is wiring drift: + +- agents, skills, tests, bootstrap artifacts, and runtime contracts drift apart +- Docker-based MCP adds setup friction and transport complexity +- long frontend skills create skip behavior because the active runtime surface is too large +- frontend and backend are treated too symmetrically even though their proof paths are different + +V1 solves wiring drift by introducing a topology manifest as the single source of truth for runtime contracts while replacing the Docker MCP layer with a local navigation/injection engine backed by a file-based corpus. + +## 2. Design Summary + +V1 architecture: + +```text +User + -> hyper + -> builder agent + -> skills + -> local navigation engine + -> corpus folders +``` + +Previous architecture: + +```text +Agents -> Skills -> Docker MCP Server -> Data +``` + +V1 architecture: + +```text +Agents -> Skills -> Local Navigation/Injection Engine -> Corpus +``` + +Key decisions: + +- topology manifest becomes source of truth for runtime wiring +- tool-call names stay stable +- Docker MCP stops being the primary runtime transport +- corpus remains file-backed and researched +- engine resolves domain, capability, allowed links, and injection slices +- generated files cover topology-derived artifacts only + +## 3. Core Principles + +### 3.1 Transport Is Not Product + +The MCP transport is not the product. The product is: + +- domain truth +- contract-aware routing +- enforceable skill usage +- proof-aware autonomy + +V1 removes Docker MCP as the default transport but keeps stable tool-call identities through local adapters. + +### 3.2 Skills Enforce, Corpus Informs + +Skills should not carry the full research corpus. Skills should be short enforcement contracts that define: + +- call order +- required artifacts +- forbidden actions +- proof required before completion + +The researched material remains in the corpus. + +### 3.3 Frontend and Backend Are Not Symmetric + +Backend has stronger executable proof paths: + +- unit tests +- integration tests +- API calls +- logs + +Frontend has weaker proof paths and needs design-constrained execution: + +- layout quality +- hierarchy +- motion quality +- responsive behavior +- interaction feedback + +This asymmetry must be explicit in topology. + +### 3.4 Stable Tool Names, New Runtime + +Tool identities such as `designer_resolve_intent`, `reactflow_get_api`, and `golang_get_practice` remain stable. What changes is the resolver behind them: + +- old: server transport +- new: local adapter -> capability resolver -> corpus injection + +## 4. V1 Repo Shape + +```text +hyperstack/ +β”œβ”€ topology/ +β”‚ β”œβ”€ manifest.yaml +β”‚ β”œβ”€ domains/ +β”‚ β”‚ β”œβ”€ frontend.yaml +β”‚ β”‚ β”œβ”€ backend.yaml +β”‚ β”‚ └─ shared.yaml +β”‚ β”œβ”€ agents/ +β”‚ β”‚ β”œβ”€ hyper.yaml +β”‚ β”‚ β”œβ”€ frontend-builder.yaml +β”‚ β”‚ β”œβ”€ backend-builder.yaml +β”‚ β”‚ └─ fullstack-builder.yaml +β”‚ β”œβ”€ skills/ +β”‚ β”‚ β”œβ”€ groups.yaml +β”‚ β”‚ └─ policies.yaml +β”‚ └─ bundles/ +β”‚ β”œβ”€ frontend.design.yaml +β”‚ β”œβ”€ frontend.react.yaml +β”‚ β”œβ”€ backend.http.yaml +β”‚ β”œβ”€ backend.lang.go.yaml +β”‚ β”œβ”€ backend.lang.rust.yaml +β”‚ └─ shared.system.yaml +β”œβ”€ corpus/ +β”‚ β”œβ”€ frontend/ +β”‚ β”‚ β”œβ”€ designer/ +β”‚ β”‚ β”œβ”€ design-tokens/ +β”‚ β”‚ β”œβ”€ ui-ux/ +β”‚ β”‚ β”œβ”€ react/ +β”‚ β”‚ β”œβ”€ shadcn/ +β”‚ β”‚ β”œβ”€ motion/ +β”‚ β”‚ β”œβ”€ lenis/ +β”‚ β”‚ └─ reactflow/ +β”‚ β”œβ”€ backend/ +β”‚ β”‚ β”œβ”€ echo/ +β”‚ β”‚ β”œβ”€ golang/ +β”‚ β”‚ └─ rust/ +β”‚ └─ shared/ +β”‚ └─ system/ +β”œβ”€ engine/ +β”‚ β”œβ”€ registry.ts +β”‚ β”œβ”€ resolver.ts +β”‚ β”œβ”€ injector.ts +β”‚ β”œβ”€ contracts.ts +β”‚ β”œβ”€ navigation.ts +β”‚ └─ policy.ts +β”œβ”€ adapters/ +β”‚ └─ local-tools/ +β”œβ”€ agents/ +β”œβ”€ skills/ +β”œβ”€ generated/ +β”‚ β”œβ”€ routing/ +β”‚ β”œβ”€ runtime-context/ +β”‚ β”œβ”€ tool-index/ +β”‚ └─ tests/ +└─ tests/ +``` + +## 5. Domain Model + +V1 domains: + +- `frontend` +- `backend` +- `shared` + +Domains are not decorative labels. They enforce: + +- allowed bundles +- forbidden bundles +- default proof model +- write autonomy +- required gates + +### 5.1 Domain Policies + +#### Frontend + +- write policy: `constrained` +- completion proof: `visual_and_behavioral` +- truth source: frontend design/react bundles +- required gates: `designer`, `behaviour-analysis` +- optional gate: `shadcn-expert` + +#### Backend + +- write policy: `direct` +- completion proof: `executable` +- truth source: backend http/language bundles +- required gate: none beyond shared quality gates +- optional gate: `security-review` + +#### Shared + +- write policy: `policy_only` +- completion proof: `routing_and_verification` +- truth source: topology/system +- used mainly by `hyper` + +## 6. Agent Model + +Agents are runtime work owners. They are not generic personalities. Each agent is defined by domain set, allowed skills, allowed bundles, and handoff contracts. + +### 6.1 V1 Agents + +#### `hyper` + +- kind: `orchestrator` +- domains: `[shared]` +- owns: + - classification + - route decision + - gate enforcement + - final verification + - delivery +- should not own deep frontend/backend implementation by default + +#### `frontend-builder` + +- kind: `specialist` +- domains: `[frontend]` +- owns: + - UI implementation + - page/app surface + - component assembly + - interaction and state behavior +- must operate under design constraint + +#### `backend-builder` + +- kind: `specialist` +- domains: `[backend]` +- owns: + - HTTP/API/service logic + - auth + - data flow + - jobs and backend integrations + +#### `fullstack-builder` + +- kind: `cross-domain` +- domains: `[frontend, backend]` +- owns: + - tightly coupled end-to-end slices + - integration work where frontend and backend cannot be cleanly separated +- must not become wildcard `*` +- completion proof uses the strictest touched domain + +### 6.2 Agent Hierarchy + +```text +hyper +β”œβ”€ frontend-builder +β”œβ”€ backend-builder +└─ fullstack-builder +``` + +## 7. Skill Model + +Skills are enforcement contracts. They are not the source of deep domain truth. + +### 7.1 Shared Process Skills + +These should remain shared, short, and reusable: + +- `blueprint` +- `forge-plan` +- `run-plan` +- `parallel-dispatch` +- `subagent-ops` +- `autonomous-mode` +- `test-first` +- `debug-discipline` +- `engineering-discipline` +- `worktree-isolation` +- `ship-gate` +- `code-review` +- `deliver` + +### 7.2 Frontend Specialty Skills + +- `designer` +- `behaviour-analysis` +- `shadcn-expert` + +### 7.3 Backend Specialty Skill + +- `security-review` + +### 7.4 Deferred Skills + +Not core to V1 workgroup routing: + +- `readme-writer` +- `testing-skills` + +These remain installed but are not part of the default routed working group. + +## 8. Bundle Model + +Bundles replace the old MCP-layer mental model in runtime topology. A bundle is a capability-backed grouping of corpus sources and stable tool prefixes. + +### 8.1 V1 Bundles + +#### `shared.system` + +- domain: `shared` +- source: system/topology/runtime guidance +- stable tool family: system setup and policy tooling + +#### `frontend.design` + +- domain: `frontend` +- sources: + - `designer` + - `design-tokens` + - `ui-ux` +- capabilities: + - `design.intent` + - `design.contract` + - `design.tokens` + +#### `frontend.react` + +- domain: `frontend` +- sources: + - `react` + - `shadcn` + - `motion` + - `lenis` + - `reactflow` +- capabilities: + - `frontend.patterns` + - `frontend.motion` + - `frontend.flow` + +#### `backend.http` + +- domain: `backend` +- source: + - `echo` +- capabilities: + - `backend.http.patterns` + +#### `backend.lang.go` + +- domain: `backend` +- source: + - `golang` +- capabilities: + - `backend.go.patterns` + +#### `backend.lang.rust` + +- domain: `backend` +- source: + - `rust` +- capabilities: + - `backend.rust.patterns` + +## 9. Capability Model + +Capabilities are the stable runtime vocabulary. They are more durable than plugin names. + +V1 capability set: + +- `system.setup` +- `design.intent` +- `design.contract` +- `design.tokens` +- `frontend.patterns` +- `frontend.motion` +- `frontend.flow` +- `backend.http.patterns` +- `backend.go.patterns` +- `backend.rust.patterns` +- `quality.security.review` +- `quality.behaviour.audit` +- `quality.ship.verify` + +Why capability names matter: + +- tools can stay stable while storage changes +- bundles can be reorganized without breaking every agent +- tests can assert capability coverage directly + +## 10. Artifact Contracts + +Artifacts are typed handoff shapes between agents, skills, and adapters. + +Recommended first V1 artifact set: + +- `task_handoff` +- `design_contract` +- `build_result` +- `review_report` +- `verification_report` +- `delivery_report` + +### 10.1 `task_handoff` + +Produced by: + +- `hyper` + +Consumed by: + +- builder agents +- design/planning skills + +Fields: + +- request_id +- domain_targets +- capability_targets +- constraints +- success_criteria +- touched_surfaces + +### 10.2 `design_contract` + +Produced by: + +- `designer` + +Consumed by: + +- `frontend-builder` +- `fullstack-builder` +- `behaviour-analysis` + +Fields: + +- visual_theme +- color_system +- typography +- spacing +- component_states +- motion_rules +- responsive_rules +- anti_patterns + +### 10.3 `verification_report` + +Produced by: + +- `behaviour-analysis` +- `ship-gate` +- future runtime verifiers + +Consumed by: + +- `hyper` +- `deliver` + +Fields: + +- status +- proof_mode +- findings +- covered_paths +- residual_risks + +## 11. Runtime Resolution Flow + +Every stable tool call resolves through the same pipeline: + +```text +tool name +-> capability +-> bundle +-> corpus paths +-> injector +-> shaped artifact +``` + +This replaces the previous server-centric model. + +### 11.1 Example: `designer_resolve_intent` + +User asks for a developer analytics landing page. + +Flow: + +1. `hyper` receives the request +2. `hyper` classifies: + - domain = frontend + - capability = `design.intent` +3. `hyper` routes to `frontend-builder` +4. `frontend-builder` is required to use `frontend.design` +5. local adapter `designer_resolve_intent` resolves the call +6. engine maps the tool to: + - capability: `design.intent` + - bundle: `frontend.design` + - corpus paths: + - `corpus/frontend/designer` + - `corpus/frontend/ui-ux` + - `corpus/frontend/design-tokens` +7. injector builds a short context pack +8. adapter returns a shaped `intent_resolution` artifact +9. `designer` skill can now produce `design_contract` +10. no frontend completion path may claim done before required frontend proof exists + +## 12. Local Navigation Engine + +The engine is the new runtime center. It is not plain folder search. + +Responsibilities: + +- register tools, capabilities, bundles, and sources +- enforce allowed and forbidden links +- resolve minimal corpus slices +- build short injection packs +- return shaped artifacts +- support stable local tool handlers + +The engine should not: + +- act as a generic server-first runtime +- dump giant markdown payloads by default +- depend on Docker +- bypass manifest policy + +## 13. Smart Injection Rules + +To avoid long-skill skip behavior: + +- default injection must be minimal +- domain truth stays in corpus +- adapters inject only what the capability needs +- long references are annex material, not default runtime payload + +Rule: + +- Skills define order and gates +- Bundles define accessible truth +- Engine injects the smallest valid slice + +This preserves research while shrinking active prompt load. + +## 14. Generation Scope + +V1 generation should cover topology-derived outputs only. + +Generate: + +- routing matrix +- allow/deny link tables +- tool index +- runtime topology bootstrap +- contract tests +- stale-link tests + +Do not generate: + +- full skill prose +- deep domain corpus +- implementation code for researched tools + +Reason: + +- topology owns wiring +- corpus owns knowledge +- hand-authored process prose remains curated + +## 15. Validation and Proof + +### 15.1 Frontend + +Frontend completion requires stronger proof because code correctness is not enough. + +Required proof path: + +- design contract exists +- frontend truth bundles used +- behavior audit completed +- final report marks residual risks explicitly + +Compile-only or typecheck-only proof is insufficient. + +### 15.2 Backend + +Backend completion may be more direct because executable proof is stronger. + +Acceptable proof paths include: + +- tests +- API calls +- logs +- integration checks + +### 15.3 Fullstack + +Fullstack completion inherits the strictest touched domain. + +If frontend is touched: + +- frontend proof requirements apply + +Backend proof alone cannot close a fullstack task that changes frontend behavior. + +## 16. Invariants + +- no direct user -> specialist execution +- no unrestricted specialist -> forbidden bundle access +- no frontend completion without `design_contract` +- no completion claim without `verification_report` +- no cross-domain execution without declared domain union +- no runtime link outside topology manifest +- no generated artifact used as hand-authored source of truth + +## 17. Migration Strategy + +V1 should migrate in this order: + +1. create topology manifest and split files +2. define domain policies +3. define four V1 agents +4. define bundles and capability mapping +5. define first artifact contracts +6. build local navigation engine registry/resolver +7. add local tool adapters preserving stable tool names +8. generate routing/bootstrap/tests from topology +9. progressively move deep researched data into `corpus/` +10. retire Docker MCP as default runtime path + +## 18. Risks + +### Risk 1: Topology becomes too generic + +Mitigation: + +- keep V1 domains small +- keep capability set focused +- generate only what is needed + +### Risk 2: Corpus migration becomes a rewrite + +Mitigation: + +- keep existing researched files first +- map them into corpus gradually +- do not rewrite deep content during topology phase + +### Risk 3: Tool compatibility breaks + +Mitigation: + +- preserve stable tool names +- swap transport behind adapters +- add compatibility tests per tool family + +### Risk 4: Frontend still over-injects + +Mitigation: + +- enforce bundle-level injection +- keep skills short +- move long theory to corpus annexes + +### Risk 5: Fullstack becomes wildcard + +Mitigation: + +- declare exact domain union +- enforce strictest-proof rule +- test forbidden links + +## 19. Recommendation + +Ship V1 as: + +- topology manifest +- local navigation engine +- local stable tool adapters +- four-agent working group: + - `hyper` + - `frontend-builder` + - `backend-builder` + - `fullstack-builder` +- existing researched knowledge preserved in corpus-backed storage +- Docker MCP removed from the default runtime path + +This is the smallest design that meaningfully reduces drift while preserving the existing research investment. From cec5fa6e3fa4867851eb2396ba87778ae31e9d5f Mon Sep 17 00:00:00 2001 From: Kailas Mahavarkar <66670953+KailasMahavarkar@users.noreply.github.com> Date: Fri, 17 Apr 2026 02:59:03 +0530 Subject: [PATCH 06/23] feat: add topology manifest scaffolding and loader --- bun.lock | 3 + corpus/backend/README.md | 4 + corpus/frontend/README.md | 4 + corpus/shared/README.md | 3 + package.json | 1 + src/engine/contracts.ts | 48 +++++++++ src/engine/topology-loader.ts | 118 ++++++++++++++++++++++ tests/topology-manifest-behaviour.test.ts | 12 +++ topology/agents/backend-builder.yaml | 20 ++++ topology/agents/frontend-builder.yaml | 22 ++++ topology/agents/fullstack-builder.yaml | 24 +++++ topology/agents/hyper.yaml | 20 ++++ topology/bundles/backend.http.yaml | 10 ++ topology/bundles/backend.lang.go.yaml | 10 ++ topology/bundles/backend.lang.rust.yaml | 10 ++ topology/bundles/frontend.design.yaml | 18 ++++ topology/bundles/frontend.react.yaml | 21 ++++ topology/bundles/shared.system.yaml | 10 ++ topology/domains/backend.yaml | 13 +++ topology/domains/frontend.yaml | 15 +++ topology/domains/shared.yaml | 8 ++ topology/manifest.yaml | 19 ++++ topology/skills/groups.yaml | 23 +++++ topology/skills/policies.yaml | 26 +++++ 24 files changed, 462 insertions(+) create mode 100644 corpus/backend/README.md create mode 100644 corpus/frontend/README.md create mode 100644 corpus/shared/README.md create mode 100644 src/engine/contracts.ts create mode 100644 src/engine/topology-loader.ts create mode 100644 tests/topology-manifest-behaviour.test.ts create mode 100644 topology/agents/backend-builder.yaml create mode 100644 topology/agents/frontend-builder.yaml create mode 100644 topology/agents/fullstack-builder.yaml create mode 100644 topology/agents/hyper.yaml create mode 100644 topology/bundles/backend.http.yaml create mode 100644 topology/bundles/backend.lang.go.yaml create mode 100644 topology/bundles/backend.lang.rust.yaml create mode 100644 topology/bundles/frontend.design.yaml create mode 100644 topology/bundles/frontend.react.yaml create mode 100644 topology/bundles/shared.system.yaml create mode 100644 topology/domains/backend.yaml create mode 100644 topology/domains/frontend.yaml create mode 100644 topology/domains/shared.yaml create mode 100644 topology/manifest.yaml create mode 100644 topology/skills/groups.yaml create mode 100644 topology/skills/policies.yaml diff --git a/bun.lock b/bun.lock index a2b741a..d00d454 100644 --- a/bun.lock +++ b/bun.lock @@ -7,6 +7,7 @@ "dependencies": { "@modelcontextprotocol/sdk": "^1.17.0", "tsx": "^4.21.0", + "yaml": "^2.5.1", "zod": "^3.23.0", }, "devDependencies": { @@ -262,6 +263,8 @@ "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], + "yaml": ["yaml@2.8.3", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg=="], + "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], "zod-to-json-schema": ["zod-to-json-schema@3.25.1", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA=="], diff --git a/corpus/backend/README.md b/corpus/backend/README.md new file mode 100644 index 0000000..1946fe2 --- /dev/null +++ b/corpus/backend/README.md @@ -0,0 +1,4 @@ +# Backend Corpus + +V1 placeholder root for backend research assets. +The migration keeps existing plugin-backed research and maps future corpus moves here. diff --git a/corpus/frontend/README.md b/corpus/frontend/README.md new file mode 100644 index 0000000..9a025d5 --- /dev/null +++ b/corpus/frontend/README.md @@ -0,0 +1,4 @@ +# Frontend Corpus + +V1 placeholder root for frontend research assets. +The migration keeps existing plugin-backed research and maps future corpus moves here. diff --git a/corpus/shared/README.md b/corpus/shared/README.md new file mode 100644 index 0000000..c18a551 --- /dev/null +++ b/corpus/shared/README.md @@ -0,0 +1,3 @@ +# Shared Corpus + +V1 placeholder root for shared system and topology research assets. diff --git a/package.json b/package.json index c6a02dd..9387480 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "dependencies": { "@modelcontextprotocol/sdk": "^1.17.0", "tsx": "^4.21.0", + "yaml": "^2.5.1", "zod": "^3.23.0" }, "devDependencies": { diff --git a/src/engine/contracts.ts b/src/engine/contracts.ts new file mode 100644 index 0000000..fd19f39 --- /dev/null +++ b/src/engine/contracts.ts @@ -0,0 +1,48 @@ +export interface TopologyRoot { + version: 1; + defaultTransport: "local-tools"; + entryAgent: string; + domains: string[]; + agents: string[]; + bundles: 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 LoadedTopology { + version: 1; + defaultTransport: "local-tools"; + entryAgent: string; + domains: DomainPolicy[]; + agents: AgentPolicy[]; + bundles: BundlePolicy[]; +} diff --git a/src/engine/topology-loader.ts b/src/engine/topology-loader.ts new file mode 100644 index 0000000..fc93dba --- /dev/null +++ b/src/engine/topology-loader.ts @@ -0,0 +1,118 @@ +import { readFileSync } from "node:fs"; +import { join } from "node:path"; +import YAML from "yaml"; +import type { AgentPolicy, BundlePolicy, DomainPolicy, LoadedTopology, TopologyRoot } from "./contracts.js"; + +interface RootDocument { + version: 1; + default_transport: "local-tools"; + entry_agent: string; + domains: string[]; + agents: string[]; + bundles: 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[]; +} + +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, + }; +} + +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, + }; +} + +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`))), + ); + + return { + version: root.version, + defaultTransport: root.defaultTransport, + entryAgent: root.entryAgent, + domains, + agents, + bundles, + }; +} diff --git a/tests/topology-manifest-behaviour.test.ts b/tests/topology-manifest-behaviour.test.ts new file mode 100644 index 0000000..f631a01 --- /dev/null +++ b/tests/topology-manifest-behaviour.test.ts @@ -0,0 +1,12 @@ +import { expect, test } from "bun:test"; +import { loadTopology } from "../src/engine/topology-loader.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"); +}); 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/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..98f5d2d --- /dev/null +++ b/topology/manifest.yaml @@ -0,0 +1,19 @@ +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 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 From 106f61cd18bdc271670977ab08591b4eac8f53fd Mon Sep 17 00:00:00 2001 From: Kailas Mahavarkar <66670953+KailasMahavarkar@users.noreply.github.com> Date: Fri, 17 Apr 2026 02:59:50 +0530 Subject: [PATCH 07/23] feat: add topology policy and capability resolver --- src/engine/policy.ts | 25 +++++++++++++++++++++++ src/engine/resolver.ts | 21 +++++++++++++++++++ tests/topology-manifest-behaviour.test.ts | 23 +++++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 src/engine/policy.ts create mode 100644 src/engine/resolver.ts diff --git a/src/engine/policy.ts b/src/engine/policy.ts new file mode 100644 index 0000000..db4c488 --- /dev/null +++ b/src/engine/policy.ts @@ -0,0 +1,25 @@ +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; +} 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/tests/topology-manifest-behaviour.test.ts b/tests/topology-manifest-behaviour.test.ts index f631a01..b340fa6 100644 --- a/tests/topology-manifest-behaviour.test.ts +++ b/tests/topology-manifest-behaviour.test.ts @@ -1,5 +1,6 @@ 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()); @@ -10,3 +11,25 @@ test("loadTopology reads root manifest and expanded domain/agent/bundle files", 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"); +}); From c1efe5f6b9da57b1c16af3638cdca9b17352afe5 Mon Sep 17 00:00:00 2001 From: Kailas Mahavarkar <66670953+KailasMahavarkar@users.noreply.github.com> Date: Fri, 17 Apr 2026 03:00:24 +0530 Subject: [PATCH 08/23] feat: add direct bridge for local tool invocation --- src/engine/tool-bridge.ts | 27 +++++++++++++++++++++++++++ tests/tool-bridge-behaviour.test.ts | 21 +++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 src/engine/tool-bridge.ts create mode 100644 tests/tool-bridge-behaviour.test.ts 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/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); +}); From fb5d1edc03f1031070e2fad78e5aea3784ebb30e Mon Sep 17 00:00:00 2001 From: Kailas Mahavarkar <66670953+KailasMahavarkar@users.noreply.github.com> Date: Fri, 17 Apr 2026 03:01:58 +0530 Subject: [PATCH 09/23] feat: generate stable local tool registry --- generated/tool-index/local-tool-registry.json | 79 +++++++++++++++++++ generated/tool-index/local-tool-registry.ts | 79 +++++++++++++++++++ package.json | 1 + scripts/generate-local-tool-registry.ts | 52 ++++++++++++ src/adapters/local-tools/index.ts | 24 ++++++ tests/topology-artifacts-behaviour.test.ts | 13 +++ 6 files changed, 248 insertions(+) create mode 100644 generated/tool-index/local-tool-registry.json create mode 100644 generated/tool-index/local-tool-registry.ts create mode 100644 scripts/generate-local-tool-registry.ts create mode 100644 src/adapters/local-tools/index.ts create mode 100644 tests/topology-artifacts-behaviour.test.ts diff --git a/generated/tool-index/local-tool-registry.json b/generated/tool-index/local-tool-registry.json new file mode 100644 index 0000000..c5bf7c9 --- /dev/null +++ b/generated/tool-index/local-tool-registry.json @@ -0,0 +1,79 @@ +{ + "design_tokens_generate": "src/plugins/design-tokens/tools/generate.js", + "design_tokens_get_category": "src/plugins/design-tokens/tools/get-category.js", + "design_tokens_get_color_ramp": "src/plugins/design-tokens/tools/get-color-ramp.js", + "design_tokens_get_gotchas": "src/plugins/design-tokens/tools/get-gotchas.js", + "design_tokens_get_procedure": "src/plugins/design-tokens/tools/get-procedure.js", + "design_tokens_list_categories": "src/plugins/design-tokens/tools/list-categories.js", + "design_tokens_search": "src/plugins/design-tokens/tools/search.js", + "designer_generate_design_brief": "src/plugins/designer/tools/generate-design-brief.js", + "designer_generate_implementation_plan": "src/plugins/designer/tools/generate-implementation-plan.js", + "designer_get_anti_patterns": "src/plugins/designer/tools/get-anti-patterns.js", + "designer_get_cognitive_law": "src/plugins/designer/tools/get-cognitive-law.js", + "designer_get_composition_rules": "src/plugins/designer/tools/get-composition-rules.js", + "designer_get_design_system": "src/plugins/designer/tools/get-design-system.js", + "designer_get_font_pairing": "src/plugins/designer/tools/get-font-pairing.js", + "designer_get_industry_rules": "src/plugins/designer/tools/get-industry-rules.js", + "designer_get_interaction_pattern": "src/plugins/designer/tools/get-interaction-pattern.js", + "designer_get_landing_pattern": "src/plugins/designer/tools/get-landing-pattern.js", + "designer_get_page_template": "src/plugins/designer/tools/get-page-template.js", + "designer_get_personality": "src/plugins/designer/tools/get-personality.js", + "designer_get_preset": "src/plugins/designer/tools/get-preset.js", + "designer_get_ux_writing": "src/plugins/designer/tools/get-ux-writing.js", + "designer_list_personalities": "src/plugins/designer/tools/list-personalities.js", + "designer_list_presets": "src/plugins/designer/tools/list-presets.js", + "designer_resolve_intent": "src/plugins/designer/tools/resolve-intent.js", + "designer_search": "src/plugins/designer/tools/search.js", + "designer_verify_implementation": "src/plugins/designer/tools/verify-implementation.js", + "echo_decision_matrix": "src/plugins/echo/tools/decision-matrix.js", + "echo_get_middleware": "src/plugins/echo/tools/get-middleware.js", + "echo_get_recipe": "src/plugins/echo/tools/get-recipe.js", + "echo_list_middleware": "src/plugins/echo/tools/list-middleware.js", + "echo_list_recipes": "src/plugins/echo/tools/list-recipes.js", + "echo_search_docs": "src/plugins/echo/tools/search-docs.js", + "golang_get_antipatterns": "src/plugins/golang/tools/get-antipatterns.js", + "golang_get_pattern": "src/plugins/golang/tools/get-pattern.js", + "golang_get_practice": "src/plugins/golang/tools/get-practice.js", + "golang_list_patterns": "src/plugins/golang/tools/list-patterns.js", + "golang_list_practices": "src/plugins/golang/tools/list-practices.js", + "golang_search_docs": "src/plugins/golang/tools/search-docs.js", + "hyperstack_setup": "src/plugins/hyperstack/tools/setup.js", + "lenis_generate_setup": "src/plugins/lenis/tools/generate-setup.js", + "lenis_get_api": "src/plugins/lenis/tools/get-api.js", + "lenis_get_pattern": "src/plugins/lenis/tools/get-pattern.js", + "lenis_list_apis": "src/plugins/lenis/tools/list-apis.js", + "lenis_search_docs": "src/plugins/lenis/tools/search-docs.js", + "motion_generate_animation": "src/plugins/motion/tools/generate-animation.js", + "motion_get_api": "src/plugins/motion/tools/get-api.js", + "motion_get_examples": "src/plugins/motion/tools/get-examples.js", + "motion_get_transitions": "src/plugins/motion/tools/get-transitions.js", + "motion_list_apis": "src/plugins/motion/tools/list-apis.js", + "motion_search_docs": "src/plugins/motion/tools/search-docs.js", + "react_get_constraints": "src/plugins/react/tools/get-constraints.js", + "react_get_pattern": "src/plugins/react/tools/get-pattern.js", + "react_list_patterns": "src/plugins/react/tools/list-patterns.js", + "react_search_docs": "src/plugins/react/tools/search-docs.js", + "reactflow_generate_flow": "src/plugins/reactflow/tools/generate-flow.js", + "reactflow_get_api": "src/plugins/reactflow/tools/get-api.js", + "reactflow_get_examples": "src/plugins/reactflow/tools/get-examples.js", + "reactflow_get_migration_guide": "src/plugins/reactflow/tools/get-migration-guide.js", + "reactflow_get_pattern": "src/plugins/reactflow/tools/get-pattern.js", + "reactflow_get_template": "src/plugins/reactflow/tools/get-template.js", + "reactflow_list_apis": "src/plugins/reactflow/tools/list-apis.js", + "reactflow_search_docs": "src/plugins/reactflow/tools/search-docs.js", + "rust_cheatsheet": "src/plugins/rust/tools/cheatsheet.js", + "rust_get_practice": "src/plugins/rust/tools/get-practice.js", + "rust_list_practices": "src/plugins/rust/tools/list-practices.js", + "rust_search_docs": "src/plugins/rust/tools/search-docs.js", + "shadcn_get_component": "src/plugins/shadcn/tools/get-component.js", + "shadcn_get_composition": "src/plugins/shadcn/tools/get-composition.js", + "shadcn_get_rules": "src/plugins/shadcn/tools/get-rules.js", + "shadcn_get_snippet": "src/plugins/shadcn/tools/get-snippet.js", + "shadcn_list_components": "src/plugins/shadcn/tools/list-components.js", + "ui_ux_get_checklist": "src/plugins/ui-ux/tools/get-checklist.js", + "ui_ux_get_component_pattern": "src/plugins/ui-ux/tools/get-component-pattern.js", + "ui_ux_get_gotchas": "src/plugins/ui-ux/tools/get-gotchas.js", + "ui_ux_get_principle": "src/plugins/ui-ux/tools/get-principle.js", + "ui_ux_list_principles": "src/plugins/ui-ux/tools/list-principles.js", + "ui_ux_search": "src/plugins/ui-ux/tools/search.js" +} diff --git a/generated/tool-index/local-tool-registry.ts b/generated/tool-index/local-tool-registry.ts new file mode 100644 index 0000000..d961c04 --- /dev/null +++ b/generated/tool-index/local-tool-registry.ts @@ -0,0 +1,79 @@ +export const LOCAL_TOOL_REGISTRY = { + "design_tokens_generate": () => import("src/plugins/design-tokens/tools/generate.js"), + "design_tokens_get_category": () => import("src/plugins/design-tokens/tools/get-category.js"), + "design_tokens_get_color_ramp": () => import("src/plugins/design-tokens/tools/get-color-ramp.js"), + "design_tokens_get_gotchas": () => import("src/plugins/design-tokens/tools/get-gotchas.js"), + "design_tokens_get_procedure": () => import("src/plugins/design-tokens/tools/get-procedure.js"), + "design_tokens_list_categories": () => import("src/plugins/design-tokens/tools/list-categories.js"), + "design_tokens_search": () => import("src/plugins/design-tokens/tools/search.js"), + "designer_generate_design_brief": () => import("src/plugins/designer/tools/generate-design-brief.js"), + "designer_generate_implementation_plan": () => import("src/plugins/designer/tools/generate-implementation-plan.js"), + "designer_get_anti_patterns": () => import("src/plugins/designer/tools/get-anti-patterns.js"), + "designer_get_cognitive_law": () => import("src/plugins/designer/tools/get-cognitive-law.js"), + "designer_get_composition_rules": () => import("src/plugins/designer/tools/get-composition-rules.js"), + "designer_get_design_system": () => import("src/plugins/designer/tools/get-design-system.js"), + "designer_get_font_pairing": () => import("src/plugins/designer/tools/get-font-pairing.js"), + "designer_get_industry_rules": () => import("src/plugins/designer/tools/get-industry-rules.js"), + "designer_get_interaction_pattern": () => import("src/plugins/designer/tools/get-interaction-pattern.js"), + "designer_get_landing_pattern": () => import("src/plugins/designer/tools/get-landing-pattern.js"), + "designer_get_page_template": () => import("src/plugins/designer/tools/get-page-template.js"), + "designer_get_personality": () => import("src/plugins/designer/tools/get-personality.js"), + "designer_get_preset": () => import("src/plugins/designer/tools/get-preset.js"), + "designer_get_ux_writing": () => import("src/plugins/designer/tools/get-ux-writing.js"), + "designer_list_personalities": () => import("src/plugins/designer/tools/list-personalities.js"), + "designer_list_presets": () => import("src/plugins/designer/tools/list-presets.js"), + "designer_resolve_intent": () => import("src/plugins/designer/tools/resolve-intent.js"), + "designer_search": () => import("src/plugins/designer/tools/search.js"), + "designer_verify_implementation": () => import("src/plugins/designer/tools/verify-implementation.js"), + "echo_decision_matrix": () => import("src/plugins/echo/tools/decision-matrix.js"), + "echo_get_middleware": () => import("src/plugins/echo/tools/get-middleware.js"), + "echo_get_recipe": () => import("src/plugins/echo/tools/get-recipe.js"), + "echo_list_middleware": () => import("src/plugins/echo/tools/list-middleware.js"), + "echo_list_recipes": () => import("src/plugins/echo/tools/list-recipes.js"), + "echo_search_docs": () => import("src/plugins/echo/tools/search-docs.js"), + "golang_get_antipatterns": () => import("src/plugins/golang/tools/get-antipatterns.js"), + "golang_get_pattern": () => import("src/plugins/golang/tools/get-pattern.js"), + "golang_get_practice": () => import("src/plugins/golang/tools/get-practice.js"), + "golang_list_patterns": () => import("src/plugins/golang/tools/list-patterns.js"), + "golang_list_practices": () => import("src/plugins/golang/tools/list-practices.js"), + "golang_search_docs": () => import("src/plugins/golang/tools/search-docs.js"), + "hyperstack_setup": () => import("src/plugins/hyperstack/tools/setup.js"), + "lenis_generate_setup": () => import("src/plugins/lenis/tools/generate-setup.js"), + "lenis_get_api": () => import("src/plugins/lenis/tools/get-api.js"), + "lenis_get_pattern": () => import("src/plugins/lenis/tools/get-pattern.js"), + "lenis_list_apis": () => import("src/plugins/lenis/tools/list-apis.js"), + "lenis_search_docs": () => import("src/plugins/lenis/tools/search-docs.js"), + "motion_generate_animation": () => import("src/plugins/motion/tools/generate-animation.js"), + "motion_get_api": () => import("src/plugins/motion/tools/get-api.js"), + "motion_get_examples": () => import("src/plugins/motion/tools/get-examples.js"), + "motion_get_transitions": () => import("src/plugins/motion/tools/get-transitions.js"), + "motion_list_apis": () => import("src/plugins/motion/tools/list-apis.js"), + "motion_search_docs": () => import("src/plugins/motion/tools/search-docs.js"), + "react_get_constraints": () => import("src/plugins/react/tools/get-constraints.js"), + "react_get_pattern": () => import("src/plugins/react/tools/get-pattern.js"), + "react_list_patterns": () => import("src/plugins/react/tools/list-patterns.js"), + "react_search_docs": () => import("src/plugins/react/tools/search-docs.js"), + "reactflow_generate_flow": () => import("src/plugins/reactflow/tools/generate-flow.js"), + "reactflow_get_api": () => import("src/plugins/reactflow/tools/get-api.js"), + "reactflow_get_examples": () => import("src/plugins/reactflow/tools/get-examples.js"), + "reactflow_get_migration_guide": () => import("src/plugins/reactflow/tools/get-migration-guide.js"), + "reactflow_get_pattern": () => import("src/plugins/reactflow/tools/get-pattern.js"), + "reactflow_get_template": () => import("src/plugins/reactflow/tools/get-template.js"), + "reactflow_list_apis": () => import("src/plugins/reactflow/tools/list-apis.js"), + "reactflow_search_docs": () => import("src/plugins/reactflow/tools/search-docs.js"), + "rust_cheatsheet": () => import("src/plugins/rust/tools/cheatsheet.js"), + "rust_get_practice": () => import("src/plugins/rust/tools/get-practice.js"), + "rust_list_practices": () => import("src/plugins/rust/tools/list-practices.js"), + "rust_search_docs": () => import("src/plugins/rust/tools/search-docs.js"), + "shadcn_get_component": () => import("src/plugins/shadcn/tools/get-component.js"), + "shadcn_get_composition": () => import("src/plugins/shadcn/tools/get-composition.js"), + "shadcn_get_rules": () => import("src/plugins/shadcn/tools/get-rules.js"), + "shadcn_get_snippet": () => import("src/plugins/shadcn/tools/get-snippet.js"), + "shadcn_list_components": () => import("src/plugins/shadcn/tools/list-components.js"), + "ui_ux_get_checklist": () => import("src/plugins/ui-ux/tools/get-checklist.js"), + "ui_ux_get_component_pattern": () => import("src/plugins/ui-ux/tools/get-component-pattern.js"), + "ui_ux_get_gotchas": () => import("src/plugins/ui-ux/tools/get-gotchas.js"), + "ui_ux_get_principle": () => import("src/plugins/ui-ux/tools/get-principle.js"), + "ui_ux_list_principles": () => import("src/plugins/ui-ux/tools/list-principles.js"), + "ui_ux_search": () => import("src/plugins/ui-ux/tools/search.js"), +} as const; diff --git a/package.json b/package.json index 9387480..10bd6eb 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "scripts": { "build": "tsc --noEmit", "compile:context": "tsx src/internal/compile-runtime-context.ts", + "generate:local-tools": "tsx scripts/generate-local-tool-registry.ts", "test": "bun test", "start": "bun src/index.ts", "dev": "bun --watch src/index.ts", diff --git a/scripts/generate-local-tool-registry.ts b/scripts/generate-local-tool-registry.ts new file mode 100644 index 0000000..a18e74f --- /dev/null +++ b/scripts/generate-local-tool-registry.ts @@ -0,0 +1,52 @@ +import { mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs"; +import { join, resolve } from "node:path"; + +const repoRoot = resolve("."); +const pluginsRoot = join(repoRoot, "src", "plugins"); +const outputDir = join(repoRoot, "generated", "tool-index"); +const outputJson = join(outputDir, "local-tool-registry.json"); +const outputTs = join(outputDir, "local-tool-registry.ts"); + +const entries: Array<{ name: string; importPath: string }> = []; + +for (const plugin of readdirSync(pluginsRoot)) { + const toolsDir = join(pluginsRoot, plugin, "tools"); + + try { + for (const file of readdirSync(toolsDir)) { + if (!file.endsWith(".ts")) { + continue; + } + + const source = readFileSync(join(toolsDir, file), "utf8"); + const match = source.match(/server\.tool\(\s*"([^"]+)"/); + if (!match?.[1]) { + continue; + } + + entries.push({ + name: match[1], + importPath: `src/plugins/${plugin}/tools/${file.replace(/\.ts$/, ".js")}`, + }); + } + } catch {} +} + +entries.sort((left, right) => left.name.localeCompare(right.name)); + +mkdirSync(outputDir, { recursive: true }); + +writeFileSync( + outputJson, + JSON.stringify(Object.fromEntries(entries.map((entry) => [entry.name, entry.importPath])), null, 2) + "\n", +); + +writeFileSync( + outputTs, + [ + "export const LOCAL_TOOL_REGISTRY = {", + ...entries.map((entry) => ` "${entry.name}": () => import("${entry.importPath}"),`), + "} as const;", + "", + ].join("\n"), +); 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/tests/topology-artifacts-behaviour.test.ts b/tests/topology-artifacts-behaviour.test.ts new file mode 100644 index 0000000..30f5611 --- /dev/null +++ b/tests/topology-artifacts-behaviour.test.ts @@ -0,0 +1,13 @@ +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"); +}); From 9d7821233a0049aad770244f65a2af2fc38a40f3 Mon Sep 17 00:00:00 2001 From: Kailas Mahavarkar <66670953+KailasMahavarkar@users.noreply.github.com> Date: Fri, 17 Apr 2026 03:04:29 +0530 Subject: [PATCH 10/23] feat: switch hyperstack binary to local tool runtime --- bin/hyperstack.mjs | 15 +++++++++++---- src/cli.ts | 19 +++++++++++++++++++ tests/local-cli-behaviour.test.ts | 20 ++++++++++++++++++++ tests/runtime-behaviour.test.ts | 15 ++++++++------- 4 files changed, 58 insertions(+), 11 deletions(-) create mode 100644 src/cli.ts create mode 100644 tests/local-cli-behaviour.test.ts diff --git a/bin/hyperstack.mjs b/bin/hyperstack.mjs index bc53173..990947d 100755 --- a/bin/hyperstack.mjs +++ b/bin/hyperstack.mjs @@ -6,8 +6,7 @@ import { fileURLToPath } from "node:url"; const binDir = dirname(fileURLToPath(import.meta.url)); const rootDir = resolve(binDir, ".."); -const entrypoint = resolve(rootDir, "src/index.ts"); - +const entrypoint = resolve(rootDir, "src/cli.ts"); const isBun = !!process.versions.bun; let child; @@ -15,7 +14,7 @@ if (isBun) { child = spawn(process.execPath, [entrypoint, ...process.argv.slice(2)], { cwd: rootDir, env: process.env, - stdio: "inherit", + stdio: ["ignore", "pipe", "pipe"], }); } else { const nodeMajor = Number.parseInt(process.versions.node.split(".")[0] ?? "0", 10); @@ -23,10 +22,18 @@ if (isBun) { child = spawn(process.execPath, [...tsxLoaderArgs, entrypoint, ...process.argv.slice(2)], { cwd: rootDir, env: process.env, - stdio: "inherit", + stdio: ["ignore", "pipe", "pipe"], }); } +child.stdout?.on("data", (chunk) => { + process.stdout.write(chunk); +}); + +child.stderr?.on("data", (chunk) => { + process.stderr.write(chunk); +}); + const forwardSignal = (signal) => { if (!child.killed) { child.kill(signal); diff --git a/src/cli.ts b/src/cli.ts new file mode 100644 index 0000000..ef7ab64 --- /dev/null +++ b/src/cli.ts @@ -0,0 +1,19 @@ +import { invokeLocalTool } from "./adapters/local-tools/index.js"; + +async function main() { + const [command, toolName, flag, json] = process.argv.slice(2); + + if (command !== "tool" || !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`); +} + +main().catch((error) => { + process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`); + process.exit(1); +}); 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/runtime-behaviour.test.ts b/tests/runtime-behaviour.test.ts index efa66e9..311a517 100644 --- a/tests/runtime-behaviour.test.ts +++ b/tests/runtime-behaviour.test.ts @@ -2,7 +2,6 @@ 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"; function normalize(str: string): string { @@ -116,7 +115,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 +129,16 @@ 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(); - - child.kill("SIGTERM"); - await once(child, "close"); + const [exitCode] = (await once(child, "close")) as [number | null]; + expect(exitCode).toBe(1); + expect(`${stdout}${stderr}`).toMatch(/Usage: hyperstack tool/); }); From 4d8d00fb05086dd60cea996c56e1edb180dd04b4 Mon Sep 17 00:00:00 2001 From: Kailas Mahavarkar <66670953+KailasMahavarkar@users.noreply.github.com> Date: Fri, 17 Apr 2026 03:06:26 +0530 Subject: [PATCH 11/23] feat: generate topology runtime artifacts and hook bootstrap --- generated/routing/allow-deny.md | 17 ++++++++ .../runtime-context/topology.bootstrap.md | 17 ++++++++ hooks/session-start.mjs | 14 +++++-- package.json | 1 + scripts/generate-topology-artifacts.ts | 39 +++++++++++++++++++ src/engine/injector.ts | 10 +++++ src/engine/navigation.ts | 17 ++++++++ tests/runtime-behaviour.test.ts | 4 +- tests/topology-artifacts-behaviour.test.ts | 8 ++++ 9 files changed, 122 insertions(+), 5 deletions(-) create mode 100644 generated/routing/allow-deny.md create mode 100644 generated/runtime-context/topology.bootstrap.md create mode 100644 scripts/generate-topology-artifacts.ts create mode 100644 src/engine/injector.ts create mode 100644 src/engine/navigation.ts diff --git a/generated/routing/allow-deny.md b/generated/routing/allow-deny.md new file mode 100644 index 0000000..1b03656 --- /dev/null +++ b/generated/routing/allow-deny.md @@ -0,0 +1,17 @@ +# Allow / Deny Matrix + +## hyper +- Allowed: shared.system +- Forbidden: + +## frontend-builder +- Allowed: frontend.design, frontend.react +- Forbidden: backend.http, backend.lang.go, backend.lang.rust + +## backend-builder +- Allowed: backend.http, backend.lang.go, backend.lang.rust +- Forbidden: frontend.design, frontend.react + +## fullstack-builder +- Allowed: frontend.design, frontend.react, backend.http, backend.lang.go, backend.lang.rust +- Forbidden: diff --git a/generated/runtime-context/topology.bootstrap.md b/generated/runtime-context/topology.bootstrap.md new file mode 100644 index 0000000..c61a37f --- /dev/null +++ b/generated/runtime-context/topology.bootstrap.md @@ -0,0 +1,17 @@ +# Topology Runtime Bootstrap + +Entry agent: hyper + +## Agents +- hyper: orchestrator -> shared +- frontend-builder: specialist -> frontend +- backend-builder: specialist -> backend +- fullstack-builder: cross-domain -> frontend, backend + +## Bundles +- shared.system: system.setup +- frontend.design: design.intent, design.contract, design.tokens +- frontend.react: frontend.patterns, frontend.motion, frontend.flow +- backend.http: backend.http.patterns +- backend.lang.go: backend.go.patterns +- backend.lang.rust: backend.rust.patterns diff --git a/hooks/session-start.mjs b/hooks/session-start.mjs index 6f04cdf..d1cc0a8 100644 --- a/hooks/session-start.mjs +++ b/hooks/session-start.mjs @@ -12,6 +12,7 @@ function emit(payload) { } try { + const topologyBootstrapPath = join(pluginRoot, "generated", "runtime-context", "topology.bootstrap.md"); const compiledBootstrapPath = join(pluginRoot, "generated", "runtime-context", "hyperstack.bootstrap.md"); const fallbackSkillPath = join(pluginRoot, "skills", "hyperstack", "SKILL.md"); @@ -19,11 +20,16 @@ try { let bootstrapLabel; try { - bootstrapContent = readFileSync(compiledBootstrapPath, "utf8"); - bootstrapLabel = "compiled runtime bootstrap"; + bootstrapContent = readFileSync(topologyBootstrapPath, "utf8"); + bootstrapLabel = "generated topology bootstrap"; } catch { - bootstrapContent = readFileSync(fallbackSkillPath, "utf8"); - bootstrapLabel = "full content of your 'hyperstack:hyperstack' skill"; + try { + bootstrapContent = readFileSync(compiledBootstrapPath, "utf8"); + bootstrapLabel = "compiled runtime bootstrap"; + } catch { + bootstrapContent = readFileSync(fallbackSkillPath, "utf8"); + bootstrapLabel = "full content of your 'hyperstack:hyperstack' skill"; + } } const sessionContext = `\nYou have Hyperstack.\n\n**Below is the ${bootstrapLabel} - your introduction to using Hyperstack. For all other skills, use the 'Skill' tool:**\n\n${bootstrapContent}\n`; diff --git a/package.json b/package.json index 10bd6eb..1553f47 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "build": "tsc --noEmit", "compile:context": "tsx src/internal/compile-runtime-context.ts", "generate:local-tools": "tsx scripts/generate-local-tool-registry.ts", + "generate:topology": "tsx scripts/generate-topology-artifacts.ts", "test": "bun test", "start": "bun src/index.ts", "dev": "bun --watch src/index.ts", diff --git a/scripts/generate-topology-artifacts.ts b/scripts/generate-topology-artifacts.ts new file mode 100644 index 0000000..f124f58 --- /dev/null +++ b/scripts/generate-topology-artifacts.ts @@ -0,0 +1,39 @@ +import { mkdirSync, writeFileSync } from "node:fs"; +import { resolve } from "node:path"; +import { loadTopology } from "../src/engine/topology-loader.js"; + +const repoRoot = resolve("."); +const topology = loadTopology(repoRoot); + +mkdirSync(resolve("generated/runtime-context"), { recursive: true }); +mkdirSync(resolve("generated/routing"), { recursive: true }); + +writeFileSync( + resolve("generated/runtime-context/topology.bootstrap.md"), + [ + "# Topology Runtime Bootstrap", + "", + `Entry agent: ${topology.entryAgent}`, + "", + "## Agents", + ...topology.agents.map((agent) => `- ${agent.id}: ${agent.kind} -> ${agent.domains.join(", ")}`), + "", + "## Bundles", + ...topology.bundles.map((bundle) => `- ${bundle.id}: ${bundle.capabilities.join(", ")}`), + "", + ].join("\n"), +); + +writeFileSync( + resolve("generated/routing/allow-deny.md"), + [ + "# Allow / Deny Matrix", + "", + ...topology.agents.flatMap((agent) => [ + `## ${agent.id}`, + `- Allowed: ${agent.allowedBundles.join(", ")}`, + `- Forbidden: ${agent.forbiddenBundles.join(", ")}`, + "", + ]), + ].join("\n"), +); 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/tests/runtime-behaviour.test.ts b/tests/runtime-behaviour.test.ts index 311a517..55e7101 100644 --- a/tests/runtime-behaviour.test.ts +++ b/tests/runtime-behaviour.test.ts @@ -76,7 +76,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 () => { diff --git a/tests/topology-artifacts-behaviour.test.ts b/tests/topology-artifacts-behaviour.test.ts index 30f5611..c3105c5 100644 --- a/tests/topology-artifacts-behaviour.test.ts +++ b/tests/topology-artifacts-behaviour.test.ts @@ -11,3 +11,11 @@ test("generated local tool registry includes stable tool names", () => { 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(/hyper/); + expect(bootstrap).toMatch(/frontend-builder/); + expect(bootstrap).toMatch(/backend-builder/); + expect(bootstrap).toMatch(/frontend\.design/); +}); From 155cd60163d67b4d4549de396fe4e416b935244c Mon Sep 17 00:00:00 2001 From: Kailas Mahavarkar <66670953+KailasMahavarkar@users.noreply.github.com> Date: Fri, 17 Apr 2026 03:07:50 +0530 Subject: [PATCH 12/23] refactor: remove docker-first runtime defaults --- README.md | 38 +++++++------------------ scripts/setup.ts | 7 ++--- src/internal/setup-hyperstack.ts | 2 +- tests/plugin-registry-behaviour.test.ts | 4 +-- tests/runtime-behaviour.test.ts | 10 +++++++ 5 files changed, 26 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index ee1b212..120807f 100755 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@

MIT TypeScript - Docker + Local Runtime MCP

@@ -25,7 +25,7 @@ **Hyperstack is a disciplined engineering harness for AI coding agents.** -It provides the necessary **Ground Truth** (via 79 specialized MCP tools) and **Adversarial Enforcement** (via 21 rigorous skills) to transform a generic LLM into a high-precision Senior Engineer. +It provides the necessary **Ground Truth** (via 79 specialized tool calls) and **Adversarial Enforcement** (via 21 rigorous skills) to transform a generic LLM into a high-precision Senior Engineer. Unlike standard "polite" instructions, Hyperstack uses **Iron Laws** and a **SessionStart hook** to force agents to check real documentation, follow precise design specs, and provide binary verification before shipping. @@ -39,13 +39,13 @@ The fastest way to install Hyperstack is to let your AI agent do it for you. Thi Fetch and follow the instructions at https://raw.githubusercontent.com/orkait/hyperstack/main/install.md ``` -The autopilot will autonomously detect your environment, install the MCP server (Docker or Local), and **automatically link the Skills repository**. This is the only step required. +The autopilot will autonomously detect your environment, configure the local tool runtime, and **automatically link the Skills repository**. This is the only step required. --- ### 2. Manual Configuration (Advanced) -If you aren't using the Autopilot, follow the **Unified Bootstrap** to set up both the MCP server and the Skills: +If you aren't using the Autopilot, follow the **Unified Bootstrap** to set up both the local runtime and the Skills: 1. **Clone & Initialize**: ```bash @@ -134,34 +134,18 @@ If you are an agent reading this, your mental model must be: **Process is Priori ## πŸš€ Quickstart -### 🐳 Docker (Default) +### πŸ’» Local Runtime (Default) -Hyperstack uses a persistent container plus `docker exec`. This keeps startup cheap across sessions and ensures 100% environment stability. +Hyperstack now defaults to a local tool runtime backed by topology manifests and corpus navigation. Docker is no longer required for the standard setup path. -1. Pull the image: - -```bash -docker pull ghcr.io/orkait/hyperstack:main -``` - -2. Start the persistent container: - -```bash -docker rm -f hyperstack-mcp 2>/dev/null -docker run -d --name hyperstack-mcp --restart unless-stopped \ - --memory=512m --cpus=1 \ - --entrypoint sleep \ - ghcr.io/orkait/hyperstack:main infinity -``` - -3. Add this to your MCP settings (`~/.claude.json`, Cursor, Windsurf, etc.): +Add this to your MCP or tool settings (`~/.claude.json`, Cursor, Windsurf, etc.): ```json { "mcpServers": { "hyperstack": { - "command": "docker", - "args": ["exec", "-i", "hyperstack-mcp", "bun", "/app/src/index.ts"] + "command": "node", + "args": ["/path/to/hyperstack/bin/hyperstack.mjs"] } } } @@ -174,13 +158,13 @@ If you are using Claude Code, Cursor, Windsurf, Roo Code, or Gemini, you can use ```text Fetch and follow the instructions at https://raw.githubusercontent.com/orkait/hyperstack/main/install.md ``` -The autopilot will detect your environment and propose the correct Docker-based configuration block. +The autopilot will detect your environment and propose the correct local-runtime configuration block. ### πŸ”§ Install the skills -The MCP server gives you tools. The skills give you discipline. Install both: +The local runtime gives you tools. The skills give you discipline. Install both: ```bash git clone https://github.com/orkait/hyperstack.git ~/.claude/skills/hyperstack diff --git a/scripts/setup.ts b/scripts/setup.ts index 382d46f..1edd852 100644 --- a/scripts/setup.ts +++ b/scripts/setup.ts @@ -33,11 +33,8 @@ async function main() { } const pluginRoot = process.cwd(); - - // Attempt to proactively self-heal/upgrade the docker setup - setup.selfHealDocker(); - - const patch = setup.generateMcpPatch(configPath, pluginRoot, platform); + + const patch = setup.generateMcpPatch(configPath, pluginRoot, platform, "local"); // Proactively apply the patch setup.applyMcpPatch(configPath, patch); 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/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/runtime-behaviour.test.ts b/tests/runtime-behaviour.test.ts index 55e7101..d0a74cd 100644 --- a/tests/runtime-behaviour.test.ts +++ b/tests/runtime-behaviour.test.ts @@ -3,6 +3,7 @@ import { readFile } from "node:fs/promises"; import { resolve } from "node:path"; import { once } from "node:events"; 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"); @@ -144,3 +145,12 @@ test("package bin entry prints usage when invoked without command arguments", as 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); + + expect(serialized).toMatch(/hyperstack/); + expect(serialized).toMatch(/bin\/hyperstack\.mjs/); + expect(serialized).not.toMatch(/docker/); +}); From df16451a7cb1645c3ac744ec1d99eedc70d109f4 Mon Sep 17 00:00:00 2001 From: Kailas Mahavarkar <66670953+KailasMahavarkar@users.noreply.github.com> Date: Fri, 17 Apr 2026 03:09:50 +0530 Subject: [PATCH 13/23] added improvements and refactor codebase --- SKILL.md | 142 +-- {harness => docs/harness}/context-policy.md | 0 {harness => docs/harness}/observability.md | 0 {harness => docs/harness}/router.md | 0 {harness => docs/harness}/transitions.md | 0 ...-17-topology-manifest-runtime-migration.md | 948 +++++++++++++++ .../2026-04-17-topology-manifest-design.md | 693 +++++++++++ skills/autonomous-mode/SKILL.md | 19 - skills/behaviour-analysis/SKILL.md | 23 - skills/blueprint/SKILL.md | 151 +-- skills/code-review/SKILL.md | 9 - skills/debug-discipline/SKILL.md | 20 - skills/deliver/SKILL.md | 8 - skills/design-patterns-skill/SKILL.md | 111 -- .../references/misc/overview.md | 170 --- .../patterns/design-architecture.md | 477 -------- .../references/patterns/error-handling.md | 364 ------ .../references/patterns/maintainability.md | 548 --------- .../references/patterns/readability.md | 195 ---- .../references/patterns/simplicity.md | 279 ----- .../references/patterns/testing.md | 309 ----- skills/designer/SKILL.md | 1029 ++--------------- skills/engineering-discipline/SKILL.md | 38 - skills/forge-plan/SKILL.md | 244 ++-- skills/hyperstack/SKILL.md | 310 ++--- skills/parallel-dispatch/SKILL.md | 8 - skills/run-plan/SKILL.md | 16 - skills/shadcn-expert/SKILL.md | 23 - skills/ship-gate/SKILL.md | 197 +--- skills/subagent-ops/SKILL.md | 11 - skills/test-first/SKILL.md | 25 +- skills/testing-skills/SKILL.md | 9 - skills/worktree-isolation/SKILL.md | 8 - 33 files changed, 1994 insertions(+), 4390 deletions(-) rename {harness => docs/harness}/context-policy.md (100%) rename {harness => docs/harness}/observability.md (100%) rename {harness => docs/harness}/router.md (100%) rename {harness => docs/harness}/transitions.md (100%) create mode 100644 docs/superpowers/plans/2026-04-17-topology-manifest-runtime-migration.md create mode 100644 docs/superpowers/specs/2026-04-17-topology-manifest-design.md delete mode 100755 skills/design-patterns-skill/SKILL.md delete mode 100755 skills/design-patterns-skill/references/misc/overview.md delete mode 100755 skills/design-patterns-skill/references/patterns/design-architecture.md delete mode 100755 skills/design-patterns-skill/references/patterns/error-handling.md delete mode 100755 skills/design-patterns-skill/references/patterns/maintainability.md delete mode 100755 skills/design-patterns-skill/references/patterns/readability.md delete mode 100755 skills/design-patterns-skill/references/patterns/simplicity.md delete mode 100755 skills/design-patterns-skill/references/patterns/testing.md diff --git a/SKILL.md b/SKILL.md index 2d09ef1..6cb0544 100755 --- a/SKILL.md +++ b/SKILL.md @@ -6,7 +6,7 @@ description: >- React Flow, Motion, Echo, Go, Rust, and UI/UX design systems. metadata: author: orkait - version: "3.0.0" + version: "3.1.0" license: MIT triggers: - build feature @@ -31,142 +31,8 @@ activation: priority: high --- -# 🧠 The Hyperstack Engine +# Hyperstack -
- You are an autonomous Senior Staff Engineer. You are not an autocomplete engine. -
- Speed without correctness is failure. Preservation of invariants is the only success. -
+See the canonical skill definition: `skills/hyperstack/SKILL.md` ---- - -## βš–οΈ The Iron Laws - -``` -1. NO CODE WITHOUT MCP GROUND-TRUTH DATA -2. NO VISUAL CODE WITHOUT AN APPROVED DESIGN.md -3. NO COMPLETION CLAIMS WITHOUT FRESH VERIFICATION EVIDENCE -4. NO REFACTOR WITHOUT A FAILING TEST FIRST -5. NO PATTERN WITHOUT A NAMED FORCE -``` - -**Violating the letter of these laws is violating the spirit of these laws.** - - -Before writing any code, proposing any fix, or starting any architecture, you MUST: - -1. **Stop Rationalizing:** Do not skip steps to "be helpful." Thoroughness is the highest form of help. Skipping is laziness, not speed. -2. **Verify the Stack:** Consult the relevant MCP plugins below for 100% accurate API syntax. Memory is not acceptable. Pattern-matching is not acceptable. Only MCP output is acceptable. -3. **Load the Discipline:** Read `skills/engineering-discipline/SKILL.md` for architectural gates. -4. **Adopt Negative Doubt:** List 5 failure modes for your plan before you type a single line of code. - -**The 1% Rule:** If there is even a 1% chance a system rule applies to your task, you MUST read the corresponding file in the `skills/` directory BEFORE acting. You do not have a choice. You cannot rationalize your way out of this. - - ---- - -## 🚦 Operational Phases - -Follow this state machine for every non-trivial task. Do not skip phases. - -### Phase 1: Discovery (The Inventory) -- **Actions:** Map state variables, data flows, and dependencies. -- **Skill:** Use `skills/behaviour-analysis/SKILL.md` for UI/UX or state-heavy tasks. -- **MCP:** Query `[plugin]_list_apis` and `[plugin]_search_docs` to find exact ground-truth data. - -### Phase 2: Reasoning (The Architecture) -- **Actions:** Define invariants, module boundaries, and public APIs. -- **Skill:** Use `skills/engineering-discipline/SKILL.md`. Reason in order: Responsibilities -> Invariants -> Dependency Direction -> Syntax. -- **Visual work:** If the task changes how something looks, feels, moves, or is interacted with, use `skills/designer/SKILL.md` FIRST to produce a DESIGN.md contract before any visual code. The DESIGN.md becomes the input spec for `forge-plan`. -- **Constraint:** Never start at syntax. If you do, you are building slop. - -### Phase 3: Execution (The Implementation) -- **Actions:** Apply surgical changes. Use real commands from MCP patterns. -- **Skill:** Use `skills/design-patterns-skill/SKILL.md` to select the correct abstraction (Factory, Strategy, etc.). -- **Debugging:** If you encounter a failure during implementation, invoke `skills/debug-discipline/SKILL.md` before attempting any fix. -- **Rules:** No `rAF`. No redundant comments. No speculative code. - -### Phase 4: Verification (The Audit) -- **Actions:** Self-verify against failure modes. -- **Skill:** Use `skills/security-review/SKILL.md` for API/Infrastructure logic. -- **Completion gate:** Invoke `skills/ship-gate/SKILL.md` before claiming any phase or task is done. -- **Output:** Use `skills/readme-writer/SKILL.md` to document the outcome with evidence. - ---- - -## 🧩 Part 1: MCP Data Plugins (The Body) - -Use these tools for **100% accurate** API details, props, code examples, and patterns. - -### βš›οΈ Frontend Libraries -- **React Flow v12** (`reactflow_*`): 56 APIs, Enterprise patterns (Zustand, Auto-layout, SSR). -- **Motion for React v12** (`motion_*`): 33 APIs, Transitions reference, Layout animations. -- **Lenis Scroll** (`lenis_*`): Smooth scroll setups, GSAP/Motion integration. -- **React 19 & Next.js** (`react_*`): RSC patterns, State hierarchy, Data fetching rules. - -### 🐹 Backend & Systems -- **Echo (Go)** (`echo_*`): 19 recipes, Middleware chain, JWT auth, WebSocket. -- **Golang Practices** (`golang_*`): 18 best practices, 10 idiomatic design patterns. -- **Rust Practices** (`rust_*`): Borrowing rules, Error handling (anyhow/thiserror), Performance. - -### πŸ’… Design Systems -- **Designer** (`designer_*`): Decision layer - 17 tools. 6 personality clusters, 15 industry rules, 11 cognitive laws, 13 page templates, 9 code-ready presets (Linear/Stripe/Vercel/Apple/Carbon/shadcn/Notion/Supabase/Figma), 21 font pairings, 50+ anti-patterns. Call `designer_resolve_intent` first for any visual task. -- **Design Tokens** (`design_tokens_*`): Tailwind v4 + OKLCH templates, Color ramp math. -- **UI/UX Principles** (`ui_ux_*`): WCAG contrast, Typography scales, 4px grid rules. - ---- - -## 🧠 Part 2: Engineering Skills (The Brain) - -These are static guidelines in the `skills/` directory. Read them using file tools. - -### Workflow Skills (process gates -- follow in order) -- **Blueprint** (`skills/blueprint/SKILL.md`): MCP-surveyed design with hard gate before any code. -- **Forge Plan** (`skills/forge-plan/SKILL.md`): MCP-verified implementation plan after design approval. -- **Run Plan** (`skills/run-plan/SKILL.md`): Validate and execute an existing plan or spec. -- **Debug Discipline** (`skills/debug-discipline/SKILL.md`): Root cause first. MCP-informed. 3-strike escalation. -- **Ship Gate** (`skills/ship-gate/SKILL.md`): Evidence required before any completion claim. -- **Deliver** (`skills/deliver/SKILL.md`): Final verification and delivery -- terminal state of every workflow. - -### Execution Skills (used during implementation) -- **Autonomous Mode** (`skills/autonomous-mode/SKILL.md`): Full end-to-end execution, no human pauses, stops only on failure. -- **Subagent Ops** (`skills/subagent-ops/SKILL.md`): Fresh agent per task, two-stage review (spec + quality). -- **Test First** (`skills/test-first/SKILL.md`): Red-green-refactor discipline before any implementation code. -- **Worktree Isolation** (`skills/worktree-isolation/SKILL.md`): Clean workspace isolation before feature work. -- **Code Review** (`skills/code-review/SKILL.md`): Dispatch reviewer subagent, handle feedback technically. -- **Parallel Dispatch** (`skills/parallel-dispatch/SKILL.md`): Concurrent agent dispatch for independent tasks. - -### Domain Skills (execution guidance) -- **Engineering Discipline** (`skills/engineering-discipline/SKILL.md`): The Senior SDE phase-gate framework. -- **Designer** (`skills/designer/SKILL.md`): Intention gate for visual/UX work. Produces DESIGN.md contract before any visual code. Auto-resolves industry/personality/style, routes to cognitive laws, enforces anti-slop rules. Use for: landing pages, dashboards, component libraries, redesigns, any new visual direction. -- **Behaviour Analysis** (`skills/behaviour-analysis/SKILL.md`): State audits & Nielsen heuristics. -- **Design Patterns** (`skills/design-patterns-skill/SKILL.md`): Clean Code & Pragmatic patterns. -- **Security Review** (`skills/security-review/SKILL.md`): OWASP audits & vulnerability checklists. -- **Readme Writer** (`skills/readme-writer/SKILL.md`): Evidence-based documentation standards. - ---- - -## 🚩 Red Flags (STOP and Re-read) - -These are the rationalizations you will have when you want to skip Hyperstack. Every one is wrong. Every one has been used before to ship bugs, wrong APIs, and AI slop. - -| Thought | Why it is wrong | -|---|---| -| "The issue is simple, I don't need to check the docs" | Simple issues are where wrong assumptions hide. Call the MCP tool. | -| "I'll write the tests after I confirm it works" | "Confirm it works" by running a failing test first. Then pass it. That is the order. | -| "This pattern looks common, I'll just adapt it from memory" | Memory drifts. Common patterns have version-specific differences. Call the tool. | -| "I'll just add one quick fix now and investigate the root cause later" | Later never comes. Investigate first. | -| "The user is impatient, I'll skip the gates" | User impatience is not permission to ship slop. Gates exist because shortcuts fail. | -| "I know this API from memory" | Memory is v11 of the API. MCP has v12. Call the tool. | -| "This is a minor refactor, tests are overkill" | Minor refactors without tests are random code edits. Tests first. | -| "The skill takes too long" | Skills take minutes. Fixing wrong code takes days. Use the skill. | -| "I'll verify after I push" | After you push it is in CI and your partner's context. Verify BEFORE. | -| "Just this once" | There is no "just this once." No exceptions. | -| "I already checked this earlier in the conversation" | State drifts. Check again. | -| "The skill doesn't quite match this situation" | Invoke it anyway. If it truly doesn't apply, you lose 10 seconds. | -| "I can reason about this without MCP" | No you cannot. MCP exists because reasoning without it produced the bugs that made the MCP necessary. | -| "I'm tired and want to finish" | Stop. Rest. Do not ship unverified work. | -| "Different wording, so the rule doesn't apply" | The letter of the rule IS the spirit of the rule. | - -**STOP. Return to Phase 1. Load the ground-truth data from MCP.** +This file provides Cursor trigger metadata only. All operational rules, Iron Laws, MCP tool references, and skill condition table are in the canonical file above. diff --git a/harness/context-policy.md b/docs/harness/context-policy.md similarity index 100% rename from harness/context-policy.md rename to docs/harness/context-policy.md diff --git a/harness/observability.md b/docs/harness/observability.md similarity index 100% rename from harness/observability.md rename to docs/harness/observability.md diff --git a/harness/router.md b/docs/harness/router.md similarity index 100% rename from harness/router.md rename to docs/harness/router.md diff --git a/harness/transitions.md b/docs/harness/transitions.md similarity index 100% rename from harness/transitions.md rename to docs/harness/transitions.md diff --git a/docs/superpowers/plans/2026-04-17-topology-manifest-runtime-migration.md b/docs/superpowers/plans/2026-04-17-topology-manifest-runtime-migration.md new file mode 100644 index 0000000..829761b --- /dev/null +++ b/docs/superpowers/plans/2026-04-17-topology-manifest-runtime-migration.md @@ -0,0 +1,948 @@ +# Topology Manifest Runtime Migration Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Replace the Docker-MCP-first runtime with a topology-manifest-driven local navigation engine that preserves stable tool-call names, enforces agent/skill/domain contracts, and keeps the researched corpus intact. + +**Architecture:** Keep deep researched content on disk, move runtime wiring into `topology/`, and add a local tool adapter layer that invokes existing tool modules directly through a production bridge instead of an MCP server. Implement this in small phases: manifest + loader, policy resolver, direct tool bridge, generated local tool registry, local CLI runtime, topology artifact generation, then cut Docker-first setup and stale tests. + +**Tech Stack:** TypeScript, Bun, Bun test, Zod, YAML parsing, existing `src/plugins/*/tools/*.ts` tool modules, existing `bin/hyperstack.mjs` + +--- + +## File Structure Lock-In + +### New Files + +- `topology/manifest.yaml` +- `topology/domains/frontend.yaml` +- `topology/domains/backend.yaml` +- `topology/domains/shared.yaml` +- `topology/agents/hyper.yaml` +- `topology/agents/frontend-builder.yaml` +- `topology/agents/backend-builder.yaml` +- `topology/agents/fullstack-builder.yaml` +- `topology/skills/groups.yaml` +- `topology/skills/policies.yaml` +- `topology/bundles/shared.system.yaml` +- `topology/bundles/frontend.design.yaml` +- `topology/bundles/frontend.react.yaml` +- `topology/bundles/backend.http.yaml` +- `topology/bundles/backend.lang.go.yaml` +- `topology/bundles/backend.lang.rust.yaml` +- `corpus/frontend/README.md` +- `corpus/backend/README.md` +- `corpus/shared/README.md` +- `src/engine/contracts.ts` +- `src/engine/topology-loader.ts` +- `src/engine/policy.ts` +- `src/engine/resolver.ts` +- `src/engine/injector.ts` +- `src/engine/tool-bridge.ts` +- `src/engine/navigation.ts` +- `src/adapters/local-tools/index.ts` +- `src/cli.ts` +- `scripts/generate-local-tool-registry.ts` +- `scripts/generate-topology-artifacts.ts` +- `tests/topology-manifest-behaviour.test.ts` +- `tests/tool-bridge-behaviour.test.ts` +- `tests/local-cli-behaviour.test.ts` +- `tests/topology-artifacts-behaviour.test.ts` + +### Generated Files + +- `generated/tool-index/local-tool-registry.ts` +- `generated/tool-index/local-tool-registry.json` +- `generated/runtime-context/topology.bootstrap.md` +- `generated/routing/allow-deny.md` + +### Files To Modify + +- `package.json` +- `bin/hyperstack.mjs` +- `src/index.ts` +- `src/internal/setup-hyperstack.ts` +- `scripts/setup.ts` +- `hooks/session-start.mjs` +- `tests/runtime-behaviour.test.ts` +- `tests/plugin-registry-behaviour.test.ts` +- `tests/context-compiler-behaviour.test.ts` +- `tests/role-harness-behaviour.test.ts` +- `README.md` + +### Responsibility Boundaries + +- `topology/` is the source of truth for runtime contracts and routing. +- `corpus/` holds file-backed knowledge roots and migration markers; V1 can reference legacy plugin data from these roots instead of rewriting all research immediately. +- `src/engine/` owns loading, policy enforcement, capability resolution, injection, and direct tool bridging. +- `scripts/` own generation of local tool registry and topology artifacts. +- `generated/` is derived only; never hand-edit. +- `src/plugins/` remains the existing research/tool implementation surface reused by the direct tool bridge during migration. + +--- + +### Task 1: Add Topology Manifest Scaffolding and Loader + +**Files:** +- Create: `topology/manifest.yaml` +- Create: `topology/domains/frontend.yaml` +- Create: `topology/domains/backend.yaml` +- Create: `topology/domains/shared.yaml` +- Create: `topology/agents/hyper.yaml` +- Create: `topology/agents/frontend-builder.yaml` +- Create: `topology/agents/backend-builder.yaml` +- Create: `topology/agents/fullstack-builder.yaml` +- Create: `topology/skills/groups.yaml` +- Create: `topology/skills/policies.yaml` +- Create: `topology/bundles/shared.system.yaml` +- Create: `topology/bundles/frontend.design.yaml` +- Create: `topology/bundles/frontend.react.yaml` +- Create: `topology/bundles/backend.http.yaml` +- Create: `topology/bundles/backend.lang.go.yaml` +- Create: `topology/bundles/backend.lang.rust.yaml` +- Create: `corpus/frontend/README.md` +- Create: `corpus/backend/README.md` +- Create: `corpus/shared/README.md` +- Create: `src/engine/contracts.ts` +- Create: `src/engine/topology-loader.ts` +- Modify: `package.json` +- Test: `tests/topology-manifest-behaviour.test.ts` + +- [ ] **Step 1: Write the failing topology loader test** + +```ts +import { expect, test } from "bun:test"; +import { loadTopology } from "../src/engine/topology-loader.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"); +}); +``` + +- [ ] **Step 2: Run the test to verify it fails** + +Run: `bun test tests/topology-manifest-behaviour.test.ts` +Expected: FAIL with `Cannot find module "../src/engine/topology-loader.ts"` or `loadTopology is not defined` + +- [ ] **Step 3: Add YAML parsing dependency and topology files** + +```json +{ + "dependencies": { + "@modelcontextprotocol/sdk": "^1.17.0", + "tsx": "^4.21.0", + "yaml": "^2.5.1", + "zod": "^3.23.0" + } +} +``` + +```yaml +# topology/manifest.yaml +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 +``` + +```yaml +# topology/domains/frontend.yaml +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 +``` + +- [ ] **Step 4: Implement topology contracts and loader** + +```ts +// src/engine/contracts.ts +export interface TopologyRoot { + version: 1; + defaultTransport: "local-tools"; + entryAgent: string; + domains: string[]; + agents: string[]; + bundles: 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 LoadedTopology { + version: 1; + defaultTransport: "local-tools"; + entryAgent: string; + domains: DomainPolicy[]; + agents: AgentPolicy[]; + bundles: BundlePolicy[]; +} +``` + +```ts +// src/engine/topology-loader.ts +import { readFileSync } from "node:fs"; +import { join } from "node:path"; +import YAML from "yaml"; +import type { AgentPolicy, BundlePolicy, DomainPolicy, LoadedTopology, TopologyRoot } from "./contracts.js"; + +function readYaml(filePath: string): T { + return YAML.parse(readFileSync(filePath, "utf8")) as T; +} + +export function loadTopology(repoRoot: string): LoadedTopology { + const root = readYaml(join(repoRoot, "topology", "manifest.yaml")); + const domains = root.domains.map((id) => readYaml(join(repoRoot, "topology", "domains", `${id}.yaml`))); + const agents = root.agents.map((id) => readYaml(join(repoRoot, "topology", "agents", `${id}.yaml`))); + const bundles = root.bundles.map((id) => readYaml(join(repoRoot, "topology", "bundles", `${id}.yaml`))); + + return { + version: root.version, + defaultTransport: root.defaultTransport, + entryAgent: root.entryAgent, + domains, + agents, + bundles, + }; +} +``` + +- [ ] **Step 5: Run the test and typecheck** + +Run: `bun test tests/topology-manifest-behaviour.test.ts && bun run build` +Expected: test PASS, `tsc --noEmit` exits `0` + +- [ ] **Step 6: Commit** + +```bash +git add package.json bun.lock topology corpus src/engine/contracts.ts src/engine/topology-loader.ts tests/topology-manifest-behaviour.test.ts +git commit -m "feat: add topology manifest scaffolding and loader" +``` + +--- + +### Task 2: Enforce Domain Policy and Capability Resolution + +**Files:** +- Create: `src/engine/policy.ts` +- Create: `src/engine/resolver.ts` +- Modify: `src/engine/contracts.ts` +- Test: `tests/topology-manifest-behaviour.test.ts` + +- [ ] **Step 1: Add a failing resolver test** + +```ts +import { expect, test } from "bun:test"; +import { loadTopology } from "../src/engine/topology-loader.ts"; +import { resolveCapabilityContext } from "../src/engine/resolver.ts"; + +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"); +}); +``` + +- [ ] **Step 2: Run the test to verify it fails** + +Run: `bun test tests/topology-manifest-behaviour.test.ts` +Expected: FAIL with `Cannot find module "../src/engine/resolver.ts"` or missing export + +- [ ] **Step 3: Implement policy helpers** + +```ts +// src/engine/policy.ts +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; +} +``` + +- [ ] **Step 4: Implement capability resolver with allow/deny enforcement** + +```ts +// src/engine/resolver.ts +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} cannot access 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 }; +} +``` + +- [ ] **Step 5: Re-run the focused test suite** + +Run: `bun test tests/topology-manifest-behaviour.test.ts` +Expected: PASS for loader + resolver assertions + +- [ ] **Step 6: Commit** + +```bash +git add src/engine/policy.ts src/engine/resolver.ts src/engine/contracts.ts tests/topology-manifest-behaviour.test.ts +git commit -m "feat: add topology policy and capability resolver" +``` + +--- + +### Task 3: Add a Production Direct Tool Bridge for Existing Tool Modules + +**Files:** +- Create: `src/engine/tool-bridge.ts` +- Test: `tests/tool-bridge-behaviour.test.ts` +- Reference: existing `src/plugins/*/tools/*.ts` + +- [ ] **Step 1: Write the failing tool bridge test** + +```ts +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); +}); +``` + +- [ ] **Step 2: Run the test to verify it fails** + +Run: `bun test tests/tool-bridge-behaviour.test.ts` +Expected: FAIL with `Cannot find module "../src/engine/tool-bridge.ts"` + +- [ ] **Step 3: Implement production tool capture** + +```ts +// src/engine/tool-bridge.ts +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); +} +``` + +- [ ] **Step 4: Run the tool bridge test** + +Run: `bun test tests/tool-bridge-behaviour.test.ts` +Expected: PASS for both direct-invocation cases + +- [ ] **Step 5: Commit** + +```bash +git add src/engine/tool-bridge.ts tests/tool-bridge-behaviour.test.ts +git commit -m "feat: add direct bridge for local tool invocation" +``` + +--- + +### Task 4: Generate and Use a Stable Local Tool Registry + +**Files:** +- Create: `scripts/generate-local-tool-registry.ts` +- Create: `src/adapters/local-tools/index.ts` +- Create: `generated/tool-index/local-tool-registry.ts` +- Create: `generated/tool-index/local-tool-registry.json` +- Modify: `package.json` +- Test: `tests/topology-artifacts-behaviour.test.ts` + +- [ ] **Step 1: Write the failing registry generation test** + +```ts +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"); +}); +``` + +- [ ] **Step 2: Run the test to verify it fails** + +Run: `bun test tests/topology-artifacts-behaviour.test.ts` +Expected: FAIL because the generated registry files do not exist + +- [ ] **Step 3: Add generation script entries to package.json** + +```json +{ + "scripts": { + "generate:local-tools": "tsx scripts/generate-local-tool-registry.ts", + "generate:topology": "tsx scripts/generate-topology-artifacts.ts" + } +} +``` + +- [ ] **Step 4: Implement registry generation** + +```ts +// scripts/generate-local-tool-registry.ts +import { mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs"; +import { join, resolve } from "node:path"; + +const repoRoot = resolve("."); +const pluginsRoot = join(repoRoot, "src", "plugins"); +const outputJson = join(repoRoot, "generated", "tool-index", "local-tool-registry.json"); +const outputTs = join(repoRoot, "generated", "tool-index", "local-tool-registry.ts"); + +const entries: Array<{ name: string; importPath: string; exportName: string }> = []; + +for (const plugin of readdirSync(pluginsRoot)) { + const toolsDir = join(pluginsRoot, plugin, "tools"); + try { + for (const file of readdirSync(toolsDir)) { + if (!file.endsWith(".ts")) continue; + const fullPath = join(toolsDir, file); + const source = readFileSync(fullPath, "utf8"); + const match = source.match(/server\\.tool\\(\\s*"([^"]+)"/); + if (!match?.[1]) continue; + entries.push({ + name: match[1], + importPath: `../../src/plugins/${plugin}/tools/${file.replace(/\\.ts$/, ".js")}`, + exportName: "register", + }); + } + } catch {} +} + +mkdirSync(join(repoRoot, "generated", "tool-index"), { recursive: true }); +writeFileSync(outputJson, JSON.stringify(Object.fromEntries(entries.map((entry) => [entry.name, entry.importPath])), null, 2)); +writeFileSync( + outputTs, + [ + "export const LOCAL_TOOL_REGISTRY = {", + ...entries.map((entry) => ` "${entry.name}": () => import("${entry.importPath}"),`), + "} as const;", + "", + ].join("\n"), +); +``` + +- [ ] **Step 5: Add local adapter entrypoint** + +```ts +// src/adapters/local-tools/index.ts +import { LOCAL_TOOL_REGISTRY } from "../../generated/tool-index/local-tool-registry.js"; +import { invokeRegisteredTool } from "../../src/engine/tool-bridge.js"; + +export async function invokeLocalTool(name: string, args: Record) { + const loader = LOCAL_TOOL_REGISTRY[name as keyof typeof LOCAL_TOOL_REGISTRY]; + if (!loader) throw new Error(`Unknown local tool: ${name}`); + const module = await loader(); + return invokeRegisteredTool(module.register, args); +} +``` + +- [ ] **Step 6: Generate the registry and run the test** + +Run: `bun run generate:local-tools && bun test tests/topology-artifacts-behaviour.test.ts` +Expected: generator writes both files, test PASS + +- [ ] **Step 7: Commit** + +```bash +git add package.json scripts/generate-local-tool-registry.ts src/adapters/local-tools/index.ts generated/tool-index tests/topology-artifacts-behaviour.test.ts +git commit -m "feat: generate stable local tool registry" +``` + +--- + +### Task 5: Build Local CLI Runtime and Replace Server-First Entry + +**Files:** +- Create: `src/cli.ts` +- Modify: `bin/hyperstack.mjs` +- Modify: `src/index.ts` +- Test: `tests/local-cli-behaviour.test.ts` +- Test: `tests/runtime-behaviour.test.ts` + +- [ ] **Step 1: Write the failing CLI test** + +```ts +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/); +}); +``` + +- [ ] **Step 2: Run the test to verify it fails** + +Run: `bun test tests/local-cli-behaviour.test.ts` +Expected: FAIL because `bin/hyperstack.mjs` still points at the old runtime + +- [ ] **Step 3: Implement CLI runtime** + +```ts +// src/cli.ts +import { invokeLocalTool } from "./adapters/local-tools/index.js"; + +async function main() { + const [command, toolName, flag, json] = process.argv.slice(2); + + if (command !== "tool" || !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`); +} + +main().catch((error) => { + process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`); + process.exit(1); +}); +``` + +- [ ] **Step 4: Redirect the binary to the CLI entrypoint** + +```js +// bin/hyperstack.mjs +#!/usr/bin/env node + +import { spawn } from "node:child_process"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +const binDir = dirname(fileURLToPath(import.meta.url)); +const rootDir = resolve(binDir, ".."); +const entrypoint = resolve(rootDir, "src/cli.ts"); +const nodeMajor = Number.parseInt(process.versions.node.split(".")[0] ?? "0", 10); +const tsxLoaderArgs = nodeMajor >= 20 ? ["--import", "tsx"] : ["--loader", "tsx"]; + +const child = spawn(process.execPath, [...tsxLoaderArgs, entrypoint, ...process.argv.slice(2)], { + cwd: rootDir, + env: process.env, + stdio: "inherit", +}); + +child.on("exit", (code) => process.exit(code ?? 1)); +child.on("error", (error) => { + console.error("Failed to start hyperstack:", error); + process.exit(1); +}); +``` + +- [ ] **Step 5: Preserve compatibility by making `src/index.ts` a thin re-export or deprecation shim** + +```ts +// src/index.ts +export { invokeLocalTool } from "./adapters/local-tools/index.js"; +``` + +- [ ] **Step 6: Run CLI and runtime tests** + +Run: `bun test tests/local-cli-behaviour.test.ts tests/runtime-behaviour.test.ts` +Expected: PASS, binary starts, local CLI returns JSON output + +- [ ] **Step 7: Commit** + +```bash +git add src/cli.ts bin/hyperstack.mjs src/index.ts tests/local-cli-behaviour.test.ts tests/runtime-behaviour.test.ts +git commit -m "feat: switch hyperstack binary to local tool runtime" +``` + +--- + +### Task 6: Generate Topology Artifacts and Session Bootstrap from Manifest + +**Files:** +- Create: `src/engine/navigation.ts` +- Create: `src/engine/injector.ts` +- Create: `scripts/generate-topology-artifacts.ts` +- Create: `generated/runtime-context/topology.bootstrap.md` +- Create: `generated/routing/allow-deny.md` +- Modify: `hooks/session-start.mjs` +- Modify: `tests/context-compiler-behaviour.test.ts` +- Modify: `tests/role-harness-behaviour.test.ts` +- Test: `tests/topology-artifacts-behaviour.test.ts` + +- [ ] **Step 1: Write the failing topology artifact test** + +```ts +import { expect, test } from "bun:test"; +import { readFileSync } from "node:fs"; +import { resolve } from "node:path"; + +test("generated topology bootstrap includes agent and bundle routing markers", () => { + const bootstrap = readFileSync(resolve("generated/runtime-context/topology.bootstrap.md"), "utf8"); + expect(bootstrap).toMatch(/hyper/); + expect(bootstrap).toMatch(/frontend-builder/); + expect(bootstrap).toMatch(/backend-builder/); + expect(bootstrap).toMatch(/frontend\\.design/); +}); +``` + +- [ ] **Step 2: Run the test to verify it fails** + +Run: `bun test tests/topology-artifacts-behaviour.test.ts` +Expected: FAIL because topology bootstrap does not exist + +- [ ] **Step 3: Implement injection and artifact generation** + +```ts +// src/engine/injector.ts +import type { BundlePolicy } from "./contracts.js"; + +export function buildInjectionSlice(bundle: BundlePolicy, capability: string) { + return { + bundle: bundle.id, + capability, + sources: bundle.sources, + toolPrefixes: bundle.toolPrefixes, + }; +} +``` + +```ts +// scripts/generate-topology-artifacts.ts +import { mkdirSync, writeFileSync } from "node:fs"; +import { resolve } from "node:path"; +import { loadTopology } from "../src/engine/topology-loader.js"; + +const repoRoot = resolve("."); +const topology = loadTopology(repoRoot); + +mkdirSync(resolve("generated/runtime-context"), { recursive: true }); +mkdirSync(resolve("generated/routing"), { recursive: true }); + +writeFileSync( + resolve("generated/runtime-context/topology.bootstrap.md"), + [ + "# Topology Runtime Bootstrap", + "", + `Entry agent: ${topology.entryAgent}`, + "", + "## Agents", + ...topology.agents.map((agent) => `- ${agent.id}: ${agent.kind} -> ${agent.domains.join(", ")}`), + "", + "## Bundles", + ...topology.bundles.map((bundle) => `- ${bundle.id}: ${bundle.capabilities.join(", ")}`), + "", + ].join("\n"), +); + +writeFileSync( + resolve("generated/routing/allow-deny.md"), + [ + "# Allow / Deny Matrix", + "", + ...topology.agents.flatMap((agent) => [ + `## ${agent.id}`, + `- Allowed: ${agent.allowedBundles.join(", ")}`, + `- Forbidden: ${agent.forbiddenBundles.join(", ")}`, + "", + ]), + ].join("\n"), +); +``` + +- [ ] **Step 4: Make the session hook prefer topology bootstrap** + +```js +// hooks/session-start.mjs +const topologyBootstrapPath = join(pluginRoot, "generated", "runtime-context", "topology.bootstrap.md"); +const legacyBootstrapPath = join(pluginRoot, "generated", "runtime-context", "hyperstack.bootstrap.md"); + +try { + bootstrapContent = readFileSync(topologyBootstrapPath, "utf8"); + bootstrapLabel = "generated topology bootstrap"; +} catch { + try { + bootstrapContent = readFileSync(legacyBootstrapPath, "utf8"); + bootstrapLabel = "compiled runtime bootstrap"; + } catch { + bootstrapContent = readFileSync(fallbackSkillPath, "utf8"); + bootstrapLabel = "full content of your 'hyperstack:hyperstack' skill"; + } +} +``` + +- [ ] **Step 5: Generate artifacts and run targeted tests** + +Run: `bun run generate:topology && bun test tests/topology-artifacts-behaviour.test.ts tests/context-compiler-behaviour.test.ts tests/role-harness-behaviour.test.ts` +Expected: PASS; no stale heading/path assertions remain + +- [ ] **Step 6: Commit** + +```bash +git add src/engine/injector.ts src/engine/navigation.ts scripts/generate-topology-artifacts.ts hooks/session-start.mjs generated/runtime-context generated/routing tests/topology-artifacts-behaviour.test.ts tests/context-compiler-behaviour.test.ts tests/role-harness-behaviour.test.ts +git commit -m "feat: generate topology runtime artifacts and hook bootstrap" +``` + +--- + +### Task 7: Remove Docker-First Setup Defaults and Refresh Documentation + +**Files:** +- Modify: `src/internal/setup-hyperstack.ts` +- Modify: `scripts/setup.ts` +- Modify: `README.md` +- Modify: `tests/plugin-registry-behaviour.test.ts` +- Modify: `tests/runtime-behaviour.test.ts` +- Test: `tests/runtime-behaviour.test.ts` +- Test: `tests/workflow-behaviour.test.ts` + +- [ ] **Step 1: Write a failing setup test for local-tool default** + +```ts +import { expect, test } from "bun:test"; +import { generateMcpPatch } from "../src/internal/setup-hyperstack.ts"; + +test("generateMcpPatch defaults to local tool runtime instead of docker", () => { + const patch = generateMcpPatch("/tmp/config.json", "/repo", "cursor"); + + expect(JSON.stringify(patch.content)).toMatch(/hyperstack/); + expect(JSON.stringify(patch.content)).toMatch(/bin\\/hyperstack\\.mjs|src\\/cli\\.ts/); + expect(JSON.stringify(patch.content)).not.toMatch(/docker/); +}); +``` + +- [ ] **Step 2: Run the test to verify it fails** + +Run: `bun test tests/runtime-behaviour.test.ts` +Expected: FAIL because setup still emits Docker-first config + +- [ ] **Step 3: Update setup generation to prefer local transport** + +```ts +// src/internal/setup-hyperstack.ts +const localServerConfig = { + command: "node", + args: [binaryPath], + env: { HYPERSTACK_ROOT: pluginRoot }, +}; + +export function generateMcpPatch( + configPath: string, + pluginRoot: string, + platform: string, + method: "local" | "docker" = "local", +) { + const serverConfig = method === "docker" ? dockerServerConfig : localServerConfig; + // unchanged format logic below +} +``` + +```ts +// scripts/setup.ts +const patch = setup.generateMcpPatch(configPath, pluginRoot, platform, "local"); +``` + +- [ ] **Step 4: Refresh README and tests away from plugin-count/server-first language** + +```md +## Runtime + +Hyperstack now defaults to a local tool runtime backed by topology manifests and corpus navigation. +Docker is no longer required for the default setup path. +Stable tool-call names are preserved through local adapters. +``` + +```ts +// tests/plugin-registry-behaviour.test.ts +import { existsSync } from "node:fs"; +import { resolve } from "node:path"; + +test("generated local tool registry exists and includes stable tool names", () => { + expect(existsSync(resolve("generated/tool-index/local-tool-registry.json"))).toBe(true); +}); +``` + +- [ ] **Step 5: Run the final verification suite** + +Run: `bun run generate:local-tools && bun run generate:topology && bun test && bun run build` +Expected: all tests PASS, build exits `0` + +- [ ] **Step 6: Commit** + +```bash +git add src/internal/setup-hyperstack.ts scripts/setup.ts README.md tests/plugin-registry-behaviour.test.ts tests/runtime-behaviour.test.ts tests/workflow-behaviour.test.ts +git commit -m "refactor: remove docker-first runtime defaults" +``` + +--- + +## Spec Coverage Check + +- Spec repo shape is covered by Task 1 scaffolding plus Task 6 generated outputs. +- Domain policies and agent contracts are covered by Tasks 1 and 2. +- Stable local tool names are preserved by Tasks 3, 4, and 5. +- Topology bootstrap and routing artifacts are covered by Task 6. +- Docker-first setup removal is covered by Task 7. +- Frontend/backend asymmetry is enforced through domain policies and resolver tests in Tasks 1 and 2. + +## Placeholder Scan + +- No `TBD`, `TODO`, or deferred implementation placeholders are present in tasks. +- Each code-touching task includes a concrete code block. +- Each verification step includes an exact command and expected outcome. + +## Type Consistency Check + +- `loadTopology`, `resolveCapabilityContext`, and `invokeRegisteredTool` names are defined before they are reused. +- `task_handoff`, `build_result`, and `verification_report` are treated as artifact names, not runtime function names. +- `frontend-builder`, `backend-builder`, `fullstack-builder`, `frontend.design`, and `backend.http` naming stays consistent with the approved spec. + +## Notes for Implementation + +- Keep `src/plugins/*` intact during V1. Reuse them through the direct tool bridge first; migrate deep data into `corpus/` later. +- Do not rewrite the 30+ hours of researched content during topology cutover. +- Prefer small focused commits exactly as listed above. diff --git a/docs/superpowers/specs/2026-04-17-topology-manifest-design.md b/docs/superpowers/specs/2026-04-17-topology-manifest-design.md new file mode 100644 index 0000000..a1e1cb7 --- /dev/null +++ b/docs/superpowers/specs/2026-04-17-topology-manifest-design.md @@ -0,0 +1,693 @@ +# Topology Manifest V1 Design + +Date: 2026-04-17 +Status: Approved design, pre-implementation +Scope: Replace Docker-MCP-first wiring with a topology-manifest-driven local navigation engine while preserving stable tool-call identities + +## 1. Goal + +Hyperstack currently has three high-value assets: + +- researched domain truth +- strong skill/process discipline +- internal agent routing ideas + +The failure mode is not missing research. The failure mode is wiring drift: + +- agents, skills, tests, bootstrap artifacts, and runtime contracts drift apart +- Docker-based MCP adds setup friction and transport complexity +- long frontend skills create skip behavior because the active runtime surface is too large +- frontend and backend are treated too symmetrically even though their proof paths are different + +V1 solves wiring drift by introducing a topology manifest as the single source of truth for runtime contracts while replacing the Docker MCP layer with a local navigation/injection engine backed by a file-based corpus. + +## 2. Design Summary + +V1 architecture: + +```text +User + -> hyper + -> builder agent + -> skills + -> local navigation engine + -> corpus folders +``` + +Previous architecture: + +```text +Agents -> Skills -> Docker MCP Server -> Data +``` + +V1 architecture: + +```text +Agents -> Skills -> Local Navigation/Injection Engine -> Corpus +``` + +Key decisions: + +- topology manifest becomes source of truth for runtime wiring +- tool-call names stay stable +- Docker MCP stops being the primary runtime transport +- corpus remains file-backed and researched +- engine resolves domain, capability, allowed links, and injection slices +- generated files cover topology-derived artifacts only + +## 3. Core Principles + +### 3.1 Transport Is Not Product + +The MCP transport is not the product. The product is: + +- domain truth +- contract-aware routing +- enforceable skill usage +- proof-aware autonomy + +V1 removes Docker MCP as the default transport but keeps stable tool-call identities through local adapters. + +### 3.2 Skills Enforce, Corpus Informs + +Skills should not carry the full research corpus. Skills should be short enforcement contracts that define: + +- call order +- required artifacts +- forbidden actions +- proof required before completion + +The researched material remains in the corpus. + +### 3.3 Frontend and Backend Are Not Symmetric + +Backend has stronger executable proof paths: + +- unit tests +- integration tests +- API calls +- logs + +Frontend has weaker proof paths and needs design-constrained execution: + +- layout quality +- hierarchy +- motion quality +- responsive behavior +- interaction feedback + +This asymmetry must be explicit in topology. + +### 3.4 Stable Tool Names, New Runtime + +Tool identities such as `designer_resolve_intent`, `reactflow_get_api`, and `golang_get_practice` remain stable. What changes is the resolver behind them: + +- old: server transport +- new: local adapter -> capability resolver -> corpus injection + +## 4. V1 Repo Shape + +```text +hyperstack/ +β”œβ”€ topology/ +β”‚ β”œβ”€ manifest.yaml +β”‚ β”œβ”€ domains/ +β”‚ β”‚ β”œβ”€ frontend.yaml +β”‚ β”‚ β”œβ”€ backend.yaml +β”‚ β”‚ └─ shared.yaml +β”‚ β”œβ”€ agents/ +β”‚ β”‚ β”œβ”€ hyper.yaml +β”‚ β”‚ β”œβ”€ frontend-builder.yaml +β”‚ β”‚ β”œβ”€ backend-builder.yaml +β”‚ β”‚ └─ fullstack-builder.yaml +β”‚ β”œβ”€ skills/ +β”‚ β”‚ β”œβ”€ groups.yaml +β”‚ β”‚ └─ policies.yaml +β”‚ └─ bundles/ +β”‚ β”œβ”€ frontend.design.yaml +β”‚ β”œβ”€ frontend.react.yaml +β”‚ β”œβ”€ backend.http.yaml +β”‚ β”œβ”€ backend.lang.go.yaml +β”‚ β”œβ”€ backend.lang.rust.yaml +β”‚ └─ shared.system.yaml +β”œβ”€ corpus/ +β”‚ β”œβ”€ frontend/ +β”‚ β”‚ β”œβ”€ designer/ +β”‚ β”‚ β”œβ”€ design-tokens/ +β”‚ β”‚ β”œβ”€ ui-ux/ +β”‚ β”‚ β”œβ”€ react/ +β”‚ β”‚ β”œβ”€ shadcn/ +β”‚ β”‚ β”œβ”€ motion/ +β”‚ β”‚ β”œβ”€ lenis/ +β”‚ β”‚ └─ reactflow/ +β”‚ β”œβ”€ backend/ +β”‚ β”‚ β”œβ”€ echo/ +β”‚ β”‚ β”œβ”€ golang/ +β”‚ β”‚ └─ rust/ +β”‚ └─ shared/ +β”‚ └─ system/ +β”œβ”€ engine/ +β”‚ β”œβ”€ registry.ts +β”‚ β”œβ”€ resolver.ts +β”‚ β”œβ”€ injector.ts +β”‚ β”œβ”€ contracts.ts +β”‚ β”œβ”€ navigation.ts +β”‚ └─ policy.ts +β”œβ”€ adapters/ +β”‚ └─ local-tools/ +β”œβ”€ agents/ +β”œβ”€ skills/ +β”œβ”€ generated/ +β”‚ β”œβ”€ routing/ +β”‚ β”œβ”€ runtime-context/ +β”‚ β”œβ”€ tool-index/ +β”‚ └─ tests/ +└─ tests/ +``` + +## 5. Domain Model + +V1 domains: + +- `frontend` +- `backend` +- `shared` + +Domains are not decorative labels. They enforce: + +- allowed bundles +- forbidden bundles +- default proof model +- write autonomy +- required gates + +### 5.1 Domain Policies + +#### Frontend + +- write policy: `constrained` +- completion proof: `visual_and_behavioral` +- truth source: frontend design/react bundles +- required gates: `designer`, `behaviour-analysis` +- optional gate: `shadcn-expert` + +#### Backend + +- write policy: `direct` +- completion proof: `executable` +- truth source: backend http/language bundles +- required gate: none beyond shared quality gates +- optional gate: `security-review` + +#### Shared + +- write policy: `policy_only` +- completion proof: `routing_and_verification` +- truth source: topology/system +- used mainly by `hyper` + +## 6. Agent Model + +Agents are runtime work owners. They are not generic personalities. Each agent is defined by domain set, allowed skills, allowed bundles, and handoff contracts. + +### 6.1 V1 Agents + +#### `hyper` + +- kind: `orchestrator` +- domains: `[shared]` +- owns: + - classification + - route decision + - gate enforcement + - final verification + - delivery +- should not own deep frontend/backend implementation by default + +#### `frontend-builder` + +- kind: `specialist` +- domains: `[frontend]` +- owns: + - UI implementation + - page/app surface + - component assembly + - interaction and state behavior +- must operate under design constraint + +#### `backend-builder` + +- kind: `specialist` +- domains: `[backend]` +- owns: + - HTTP/API/service logic + - auth + - data flow + - jobs and backend integrations + +#### `fullstack-builder` + +- kind: `cross-domain` +- domains: `[frontend, backend]` +- owns: + - tightly coupled end-to-end slices + - integration work where frontend and backend cannot be cleanly separated +- must not become wildcard `*` +- completion proof uses the strictest touched domain + +### 6.2 Agent Hierarchy + +```text +hyper +β”œβ”€ frontend-builder +β”œβ”€ backend-builder +└─ fullstack-builder +``` + +## 7. Skill Model + +Skills are enforcement contracts. They are not the source of deep domain truth. + +### 7.1 Shared Process Skills + +These should remain shared, short, and reusable: + +- `blueprint` +- `forge-plan` +- `run-plan` +- `parallel-dispatch` +- `subagent-ops` +- `autonomous-mode` +- `test-first` +- `debug-discipline` +- `engineering-discipline` +- `worktree-isolation` +- `ship-gate` +- `code-review` +- `deliver` + +### 7.2 Frontend Specialty Skills + +- `designer` +- `behaviour-analysis` +- `shadcn-expert` + +### 7.3 Backend Specialty Skill + +- `security-review` + +### 7.4 Deferred Skills + +Not core to V1 workgroup routing: + +- `readme-writer` +- `testing-skills` + +These remain installed but are not part of the default routed working group. + +## 8. Bundle Model + +Bundles replace the old MCP-layer mental model in runtime topology. A bundle is a capability-backed grouping of corpus sources and stable tool prefixes. + +### 8.1 V1 Bundles + +#### `shared.system` + +- domain: `shared` +- source: system/topology/runtime guidance +- stable tool family: system setup and policy tooling + +#### `frontend.design` + +- domain: `frontend` +- sources: + - `designer` + - `design-tokens` + - `ui-ux` +- capabilities: + - `design.intent` + - `design.contract` + - `design.tokens` + +#### `frontend.react` + +- domain: `frontend` +- sources: + - `react` + - `shadcn` + - `motion` + - `lenis` + - `reactflow` +- capabilities: + - `frontend.patterns` + - `frontend.motion` + - `frontend.flow` + +#### `backend.http` + +- domain: `backend` +- source: + - `echo` +- capabilities: + - `backend.http.patterns` + +#### `backend.lang.go` + +- domain: `backend` +- source: + - `golang` +- capabilities: + - `backend.go.patterns` + +#### `backend.lang.rust` + +- domain: `backend` +- source: + - `rust` +- capabilities: + - `backend.rust.patterns` + +## 9. Capability Model + +Capabilities are the stable runtime vocabulary. They are more durable than plugin names. + +V1 capability set: + +- `system.setup` +- `design.intent` +- `design.contract` +- `design.tokens` +- `frontend.patterns` +- `frontend.motion` +- `frontend.flow` +- `backend.http.patterns` +- `backend.go.patterns` +- `backend.rust.patterns` +- `quality.security.review` +- `quality.behaviour.audit` +- `quality.ship.verify` + +Why capability names matter: + +- tools can stay stable while storage changes +- bundles can be reorganized without breaking every agent +- tests can assert capability coverage directly + +## 10. Artifact Contracts + +Artifacts are typed handoff shapes between agents, skills, and adapters. + +Recommended first V1 artifact set: + +- `task_handoff` +- `design_contract` +- `build_result` +- `review_report` +- `verification_report` +- `delivery_report` + +### 10.1 `task_handoff` + +Produced by: + +- `hyper` + +Consumed by: + +- builder agents +- design/planning skills + +Fields: + +- request_id +- domain_targets +- capability_targets +- constraints +- success_criteria +- touched_surfaces + +### 10.2 `design_contract` + +Produced by: + +- `designer` + +Consumed by: + +- `frontend-builder` +- `fullstack-builder` +- `behaviour-analysis` + +Fields: + +- visual_theme +- color_system +- typography +- spacing +- component_states +- motion_rules +- responsive_rules +- anti_patterns + +### 10.3 `verification_report` + +Produced by: + +- `behaviour-analysis` +- `ship-gate` +- future runtime verifiers + +Consumed by: + +- `hyper` +- `deliver` + +Fields: + +- status +- proof_mode +- findings +- covered_paths +- residual_risks + +## 11. Runtime Resolution Flow + +Every stable tool call resolves through the same pipeline: + +```text +tool name +-> capability +-> bundle +-> corpus paths +-> injector +-> shaped artifact +``` + +This replaces the previous server-centric model. + +### 11.1 Example: `designer_resolve_intent` + +User asks for a developer analytics landing page. + +Flow: + +1. `hyper` receives the request +2. `hyper` classifies: + - domain = frontend + - capability = `design.intent` +3. `hyper` routes to `frontend-builder` +4. `frontend-builder` is required to use `frontend.design` +5. local adapter `designer_resolve_intent` resolves the call +6. engine maps the tool to: + - capability: `design.intent` + - bundle: `frontend.design` + - corpus paths: + - `corpus/frontend/designer` + - `corpus/frontend/ui-ux` + - `corpus/frontend/design-tokens` +7. injector builds a short context pack +8. adapter returns a shaped `intent_resolution` artifact +9. `designer` skill can now produce `design_contract` +10. no frontend completion path may claim done before required frontend proof exists + +## 12. Local Navigation Engine + +The engine is the new runtime center. It is not plain folder search. + +Responsibilities: + +- register tools, capabilities, bundles, and sources +- enforce allowed and forbidden links +- resolve minimal corpus slices +- build short injection packs +- return shaped artifacts +- support stable local tool handlers + +The engine should not: + +- act as a generic server-first runtime +- dump giant markdown payloads by default +- depend on Docker +- bypass manifest policy + +## 13. Smart Injection Rules + +To avoid long-skill skip behavior: + +- default injection must be minimal +- domain truth stays in corpus +- adapters inject only what the capability needs +- long references are annex material, not default runtime payload + +Rule: + +- Skills define order and gates +- Bundles define accessible truth +- Engine injects the smallest valid slice + +This preserves research while shrinking active prompt load. + +## 14. Generation Scope + +V1 generation should cover topology-derived outputs only. + +Generate: + +- routing matrix +- allow/deny link tables +- tool index +- runtime topology bootstrap +- contract tests +- stale-link tests + +Do not generate: + +- full skill prose +- deep domain corpus +- implementation code for researched tools + +Reason: + +- topology owns wiring +- corpus owns knowledge +- hand-authored process prose remains curated + +## 15. Validation and Proof + +### 15.1 Frontend + +Frontend completion requires stronger proof because code correctness is not enough. + +Required proof path: + +- design contract exists +- frontend truth bundles used +- behavior audit completed +- final report marks residual risks explicitly + +Compile-only or typecheck-only proof is insufficient. + +### 15.2 Backend + +Backend completion may be more direct because executable proof is stronger. + +Acceptable proof paths include: + +- tests +- API calls +- logs +- integration checks + +### 15.3 Fullstack + +Fullstack completion inherits the strictest touched domain. + +If frontend is touched: + +- frontend proof requirements apply + +Backend proof alone cannot close a fullstack task that changes frontend behavior. + +## 16. Invariants + +- no direct user -> specialist execution +- no unrestricted specialist -> forbidden bundle access +- no frontend completion without `design_contract` +- no completion claim without `verification_report` +- no cross-domain execution without declared domain union +- no runtime link outside topology manifest +- no generated artifact used as hand-authored source of truth + +## 17. Migration Strategy + +V1 should migrate in this order: + +1. create topology manifest and split files +2. define domain policies +3. define four V1 agents +4. define bundles and capability mapping +5. define first artifact contracts +6. build local navigation engine registry/resolver +7. add local tool adapters preserving stable tool names +8. generate routing/bootstrap/tests from topology +9. progressively move deep researched data into `corpus/` +10. retire Docker MCP as default runtime path + +## 18. Risks + +### Risk 1: Topology becomes too generic + +Mitigation: + +- keep V1 domains small +- keep capability set focused +- generate only what is needed + +### Risk 2: Corpus migration becomes a rewrite + +Mitigation: + +- keep existing researched files first +- map them into corpus gradually +- do not rewrite deep content during topology phase + +### Risk 3: Tool compatibility breaks + +Mitigation: + +- preserve stable tool names +- swap transport behind adapters +- add compatibility tests per tool family + +### Risk 4: Frontend still over-injects + +Mitigation: + +- enforce bundle-level injection +- keep skills short +- move long theory to corpus annexes + +### Risk 5: Fullstack becomes wildcard + +Mitigation: + +- declare exact domain union +- enforce strictest-proof rule +- test forbidden links + +## 19. Recommendation + +Ship V1 as: + +- topology manifest +- local navigation engine +- local stable tool adapters +- four-agent working group: + - `hyper` + - `frontend-builder` + - `backend-builder` + - `fullstack-builder` +- existing researched knowledge preserved in corpus-backed storage +- Docker MCP removed from the default runtime path + +This is the smallest design that meaningfully reduces drift while preserving the existing research investment. diff --git a/skills/autonomous-mode/SKILL.md b/skills/autonomous-mode/SKILL.md index f69afd9..65ebafc 100644 --- a/skills/autonomous-mode/SKILL.md +++ b/skills/autonomous-mode/SKILL.md @@ -12,12 +12,6 @@ You are unleashed. Execute the full plan end-to-end, using every Hyperstack MCP User gets the finished product. Not questions. Not checkpoints. -## The Iron Law - -``` -AUTONOMOUS DOES NOT MEAN UNDISCIPLINED. -AUTONOMOUS MEANS YOU ARE THE DISCIPLINE. -``` - Every MCP tool that could be relevant β†’ call it - Every quality gate β†’ run it yourself @@ -158,19 +152,6 @@ Everything else β†’ you handle it. 3. **Decision log review** β†’ every 3 tasks, scan log for repeated reversals (signals drift) 4. **Deterministic over probabilistic** β†’ if you can check with a command, do that instead of reasoning -## Red Flags - STOP - -| Thought | Reality | -|---|---| -| "I'll skip the MCP check, I remember the API" | Autonomous mode β†’ MORE responsibility to verify, not less | -| "I'll skip the test for this task" | Autonomous β‰  undisciplined. Write the test. | -| "I'll ask the user about this" | Resolve with evidence. Only abort conditions reach the user. | -| "Test failed, I'll fix it in the next task" | Fix now. No debt carried forward. | -| "I'll skip self-review, ship-gate will catch it" | Self-review β†’ task-level. Ship-gate β†’ composition. Both run. | -| "This needs a change outside the plan's scope" | Log it, finish plan, mention at delivery. No scope creep. | -| "I'm confused but I'll figure it out as I code" | Stop. Hit self-correction hierarchy: MCP β†’ codebase β†’ web β†’ debug. | -| "The web search result looks right" | Cross-reference against MCP data and library version. | -| "I've been making a lot of decisions, that's fine" | Review decision log. Too many decisions may signal plan gaps. | ## Integration diff --git a/skills/behaviour-analysis/SKILL.md b/skills/behaviour-analysis/SKILL.md index e117c0f..795f0b6 100755 --- a/skills/behaviour-analysis/SKILL.md +++ b/skills/behaviour-analysis/SKILL.md @@ -138,30 +138,7 @@ Use findings to set expectations in the matrix - "expected behaviour" should be - Every state must be escapable β†’ user should never be stuck - Composition must be tested β†’ features that work alone often break together -## The Iron Law -``` -NO BEHAVIOUR CLAIM WITHOUT READING THE CODE PATH -``` - -You cannot say "this should work" - trace the actual code path and confirm. Reading code is not optional. - -## Red Flags - STOP - -| Thought | Reality | -|---|---| -| "I'll check a few interactions, not the full matrix" | Partial coverage misses composition bugs. Full matrix. | -| "This state combination is unlikely" | Unlikely states are where bugs live. Test them. | -| "Nielsen's heuristics are common sense" | Common sense β‰  verification. Apply them explicitly. | -| "I already know this code" | Code drifts. Mental models drift faster. Read it. | -| "Empty states are trivial" | Empty states = #1 place products feel broken. Audit them. | -| "Transition states will be fine" | Mid-drag/mid-animation/mid-load = where race conditions live. | -| "The user will report any issues" | Users don't report vague discomfort. They leave. | -| "Full audit is overkill for a simple component" | Simple components compose into complex flows. Audit it. | -| "I'll skip heuristics I don't remember exactly" | Open the reference. All 10 get applied. | -| "The behaviour feels right" | Feelings β‰  evidence. Read the code. | -| "I tested the happy path manually" | Happy path = 20% of the matrix. Audit the unhappy paths. | -| "No DESIGN.md β†’ no ground truth" | Search for one. Escalate to designer if missing. | ## Lifecycle Integration diff --git a/skills/blueprint/SKILL.md b/skills/blueprint/SKILL.md index f107311..86e6809 100644 --- a/skills/blueprint/SKILL.md +++ b/skills/blueprint/SKILL.md @@ -1,54 +1,45 @@ --- name: blueprint category: core -description: Use before any feature build, component creation, or behaviour modification. MCP-surveyed design with a hard gate before any implementation. Do not skip, do not skim, do not rationalize your way out of it. +description: Execute before any feature build, component creation, or behavior change. Performs MCP survey and enforces a hard design gate before implementation. Do not skip or rationalize skipping. --- # Feature Planning -## The Iron Law - -``` -NO CODE WITHOUT AN APPROVED DESIGN -``` - -No design presented + no explicit user approval β†’ no code. Violating the letter = violating the spirit. ## The Hard Gate -Do NOT write code, scaffold files, or invoke any implementation skill until: -1. MCP survey complete for relevant domains -2. Design presented OR We know user project preferences: - - Visual/UX work β†’ DESIGN.md contract from `skills/designer/SKILL.md` (if preference is not known) - - Backend/infra work β†’ architecture note from this skill -3. User explicitly approved it - -Applies to every task, regardless of perceived simplicity. +Do not write code, scaffold files, or invoke implementation skills until: +1. MCP survey is complete for relevant domains. +2. Design is presented OR user project preferences are known: + - Visual/UX work β†’ DESIGN.md contract from `skills/designer/SKILL.md`. + - Backend/infra work β†’ Architecture note from this skill. +3. User explicitly approves the design. + +This applies to every task, regardless of perceived simplicity. ## The 1% Rule -If there is even a 1% chance this task involves a new file, new component, new function, behavior change, config change affecting runtime, or any visual/UX modification β†’ run blueprint first. No exceptions. +If there is a 1% chance this task involves a new file, component, function, behavior change, runtime configuration, or visual/UX modification β†’ run blueprint first. No exceptions. -"Simple" tasks are where unexamined assumptions do the most damage. 5-minute design prevents hours of wrong implementation. +Simple tasks conceal unexamined assumptions. A 5-minute design prevents hours of incorrect implementation. ## The Process ### Step 1: Context Scan -Read the current state before asking anything: -- Relevant source files, recent commits, existing patterns -- What already exists that can be reused or extended -- Which Hyperstack MCP domains are relevant - -Don't ask the user questions until you've scanned the codebase. +Read current state before querying the user: +- Relevant source files, recent commits, existing patterns. +- Existing logic applicable for reuse or extension. +- Relevant Hyperstack MCP domains. ### Step 2: MCP Survey | Domain is relevant | Call first | |---|---| -| **Visual/UX work (any)** | **STOP β†’ invoke `skills/designer/SKILL.md`. It produces DESIGN.md β†’ input to Step 5 or directly to `forge-plan`.** | +| **Visual/UX work (any)** | **STOP β†’ invoke `skills/designer/SKILL.md`. Produces DESIGN.md β†’ pass to Step 5 or `forge-plan`.** | | React Flow | `reactflow_search_docs` + `reactflow_list_apis` | | Motion / animation | `motion_search_docs` + `motion_list_apis` | | Lenis scroll | `lenis_search_docs` + `lenis_list_apis` | @@ -58,76 +49,55 @@ Don't ask the user questions until you've scanned the codebase. | Design tokens | `design_tokens_list_categories` + `design_tokens_get_gotchas` | | UI/UX | `ui_ux_list_principles` + `ui_ux_get_gotchas` | -Design built on wrong API assumptions = technical debt scheduled for delivery. +Building designs on incorrect API assumptions creates immediate technical debt. -**Visual work routing:** New page, component library, landing page, dashboard, redesign, "make it look like X" β†’ `designer` skill owns the design gate. Return with DESIGN.md β†’ handoff (Step 7). +**Visual work routing:** New page, component library, landing page, dashboard, redesign, "make it look like X" β†’ `designer` skill owns the design gate. Return with DESIGN.md β†’ proceed to handoff (Step 7). ### Step 3: Clarify Requirements -Ask one clarifying question at a time: -- Purpose and success criteria β†’ what does done look like? -- Constraints β†’ performance targets, accessibility requirements, existing patterns -- Scope boundary β†’ what is explicitly NOT included? +Ask single clarifying questions sequentially: +- Purpose and success criteria (what defines completion?). +- Constraints (performance targets, accessibility, existing patterns). +- Scope boundaries (what is explicitly excluded?). -Wait for answer before asking the next. Multiple independent subsystems β†’ flag before proceeding, decompose first. +Wait for answers before proceeding. Decompose independent subsystems if necessary. ### Step 4: Propose 2-3 Approaches -For each approach: -- Trade-offs -- MCP-backed APIs and patterns used (cite tool output from Step 2) -- Your recommendation with reasoning +Outline for each approach: +- Architectural trade-offs. +- MCP-backed APIs and patterns (cite tool output from Step 2). +- Your recommendation with supporting logic. -Lead with your recommended option. No options without a recommendation. +Always lead with a firm recommendation. ### Step 5: Present Design -Scale each section to its complexity: - -- **Architecture** β†’ module boundaries, data flow, key abstractions -- **Invariants** β†’ what must always be true at runtime -- **Interfaces** β†’ public APIs between modules, including types -- **Error paths** β†’ what happens when dependencies fail, inputs are invalid, async times out +Scale specificity to the complexity: +- **Architecture** β†’ Module boundaries, data flow, abstractions. +- **Invariants** β†’ Immutable runtime truths. +- **Interfaces** β†’ Public APIs between modules and types. +- **Error paths** β†’ Dependency failures, invalid inputs, async timeouts. -Get user confirmation. Revise if needed. Don't proceed until approved. +Obtain user confirmation. Revise if rejected. Do not proceed until approved. ### Step 6: Negative Doubt -List at least 5 failure modes before finalizing: +Identify at least 5 failure modes before finalizing: +1. What breaks at runtime under normal conditions? +2. What edge cases are ignored? +3. Which invariants risk concurrent or unexpected state violation? +4. What relevant MCP `get_gotchas` apply? +5. Which external dependency updates could break this? -- What breaks at runtime under normal usage? -- What edge cases does this design not handle? -- Which invariants could be violated by concurrent operations or unexpected state? -- What does MCP `get_gotchas` say about this domain? -- What external dependency could change and break this? - -Address each explicitly β†’ design around it or record the accepted risk. +Address each explicitly: redesign or formally accept the risk. ### Step 7: Handoff to Implementation -Once approved: -- Save design note to relevant docs directory if non-trivial -- Visual/UX work β†’ DESIGN.md already exists. Save at `docs/DESIGN.md` or `/DESIGN.md`. -- Invoke `hyperstack:forge-plan` β†’ builds MCP-verified implementation plan from approved design -- DESIGN.md present β†’ forge-plan reads it as input spec, each of 10 sections β†’ one or more tasks - -## Red Flags - STOP - -| Thought | Reality | -|---|---| -| "I know React Flow well enough to skip the survey" | MCP has v12-specific API shapes. Memory has v11. Call the tool. | -| "This is too simple for a design" | Simple tasks β†’ unexamined assumptions β†’ most damage. Return to Hard Gate. | -| "Let me just start with a file and design as we go" | How wrong architectures get built. Design FIRST. | -| "User seems impatient, I'll skip Step 6" | User impatience β‰  permission to ship slop. Negative Doubt is not optional. | -| "I'll propose one approach - the obvious one" | Two approaches exist for every non-trivial design. Find both. | -| "The task is a single-line change" | Single line at the wrong place destroys invariants. Design first. | -| "This is a bug fix, not a feature" | Bug fixes change behavior. Behavior changes need designs. | -| "I'm just refactoring" | Refactors move responsibility. Moving responsibility is architectural. | -| "The design will slow us down" | Wrong code ships β†’ fix it β†’ fix it again. That is slow. Design once, ship right. | -| "I can reason about this without external tools" | MCP data contains gotchas you won't remember. Call the tool. | -| "The user will tell me if I'm wrong" | The user hired you to prevent that. Do the design. | -| "I already did a similar design last week" | State drifts. Codebase changes. Do the current survey. | -| "Let me start with a prototype" | Prototypes become production. Design the prototype. | +Following approval: +- Save design note to the relevant documentation directory. +- For Visual/UX work, save DESIGN.md at `docs/DESIGN.md` or `/DESIGN.md`. +- Invoke `hyperstack:forge-plan` to generate an MCP-verified implementation plan based on the approved design. ## Lifecycle Integration @@ -135,33 +105,26 @@ Once approved: ### Agent Workflow Chains **Website/Frontend Agent:** -``` -blueprint (THIS) β†’ designer β†’ forge-plan β†’ [execution] β†’ ship-gate β†’ deliver - ↓ visual routing -``` +`blueprint` (THIS) β†’ `designer` (visual routing) β†’ `forge-plan` β†’ [execution] β†’ `ship-gate` β†’ `deliver` **Backend/Infra Agent:** -``` -blueprint (THIS) β†’ forge-plan β†’ [execution] β†’ ship-gate β†’ deliver - ↓ architecture note -``` +`blueprint` (THIS) β†’ `forge-plan` (architecture note) β†’ [execution] β†’ `ship-gate` β†’ `deliver` -**Execution Options (chosen at forge-plan handoff):** -- `autonomous-mode` β†’ full auto, stops only on failure -- `subagent-ops` β†’ fresh agent per task, two-stage review -- `engineering-discipline` β†’ manual with phase gates +**Execution Options (selected at forge-plan handoff):** +- `autonomous-mode`: Full auto, stops upon failure. +- `subagent-ops`: Independent agents per task, two-stage review. +- `engineering-discipline`: Manual, phase-gated checkpoints. ### Upstream Dependencies -- None (entry point for feature work) -- `hyperstack` β†’ 1% rule enforcement +- `hyperstack`: Enforces 1% rule. ### Downstream Consumers -- `forge-plan` β†’ reads approved design, builds MCP-verified task plan -- `designer` β†’ if visual/UX routing detected -- `run-plan` β†’ if resuming existing plan +- `forge-plan`: Reads approved design, generates MCP task plan. +- `designer`: Invoked upon visual/UX routing detection. +- `run-plan`: Invoked to resume existing plans. ### Reverse Escalation | Discovery | Escalate to | Action | |---|---|---| -| Visual/UX work detected mid-task | `designer` | Pause, get DESIGN.md, resume | -| Architecture gap (non-visual) | `blueprint` | Re-enter for architecture decision | +| Visual/UX work detected mid-task | `designer` | Pause, generate DESIGN.md, resume. | +| Architecture gap (non-visual) | `blueprint` | Pause, formalize architecture decision, resume. | diff --git a/skills/code-review/SKILL.md b/skills/code-review/SKILL.md index 5eae65b..2378984 100644 --- a/skills/code-review/SKILL.md +++ b/skills/code-review/SKILL.md @@ -113,15 +113,6 @@ For multi-item feedback: 5. Test each fix individually 6. Verify no regressions -## Red Flags - STOP - -| Thought | Reality | -|---|---| -| "Skip review, it's simple" | Simple code has bugs. Review catches them. | -| "I'll review my own code" | Self-review β‰  code review. Dispatch a subagent. | -| "Reviewer is wrong, ignore it" | Push back with reasoning. Don't silently ignore. | -| "I agree with everything" | Performative agreement β‰  technical evaluation. | -| "I'll implement all feedback at once" | One item at a time, test each. | ## Integration diff --git a/skills/debug-discipline/SKILL.md b/skills/debug-discipline/SKILL.md index 774af20..6a9d36c 100644 --- a/skills/debug-discipline/SKILL.md +++ b/skills/debug-discipline/SKILL.md @@ -6,15 +6,6 @@ description: Use when encountering any bug, test failure, or unexpected behaviou # Systematic Debugging -## The Iron Law - -``` -NO FIXES WITHOUT ROOT CAUSE FIRST. -``` - -Symptom fix = failure. Random changes = thrashing. Every fix attempt without confirmed root cause β†’ higher probability of a second bug. - -Phase 1 not complete β†’ no fix proposed. ## When to Use @@ -110,17 +101,6 @@ Signals: Stop fixing. Present findings to user: what you tried, what each attempt revealed, what architectural change appears required. -## Red Flags - STOP - -| Thought | Reality | -|---|---| -| "Let me just try changing X" | No root cause β†’ don't touch it | -| "It's probably a race condition" | "Probably" β‰  root cause | -| "Quick fix now, investigate later" | There is no later | -| "Multiple small changes at once" | Can't isolate what worked | -| "The library is broken" | Check MCP docs first | -| "One more attempt" (after 2 failures) | Stop. Escalate. | -| "I fixed it - the error is gone" | Run `hyperstack:ship-gate` | ## Integration diff --git a/skills/deliver/SKILL.md b/skills/deliver/SKILL.md index 4f7ee14..95c9f84 100644 --- a/skills/deliver/SKILL.md +++ b/skills/deliver/SKILL.md @@ -82,14 +82,6 @@ git commit -m "[single descriptive commit message]" git push -u origin [branch-name] ``` -## Red Flags - STOP - -| Thought | Reality | -|---|---| -| "Tests mostly pass, I'll fix the rest in a follow-up" | Fix them now or don't deliver. | -| "The type errors are pre-existing" | Verify with `git stash`. Pre-existing β†’ document it. Not pre-existing β†’ fix it. | -| "I'll skip ship-gate, I just ran individual verifications" | Individual gates β‰  composition. Run ship-gate. | -| "Let me also clean up X while I'm here" | Scope creep. Out-of-plan changes β†’ new branch. | ## Integration diff --git a/skills/design-patterns-skill/SKILL.md b/skills/design-patterns-skill/SKILL.md deleted file mode 100755 index 7506dd2..0000000 --- a/skills/design-patterns-skill/SKILL.md +++ /dev/null @@ -1,111 +0,0 @@ ---- -name: design-patterns-skill -category: domain -description: Apply core programming principles and design patterns from Clean Code, The Pragmatic Programmer, Code Complete, Refactoring, and Design Patterns. Use when writing code, reviewing PRs, refactoring, or designing system architecture. -triggers: - - "code review" - - "design pattern" - - "refactor" - - "clean code" - - "SOLID" - - "code quality" - - "architecture design" - - "code generation" -activation: - mode: fuzzy - priority: normal - triggers: - - "code review" - - "design pattern" - - "refactor" - - "clean code" - - "SOLID" - - "code quality" - - "architecture design" - - "code generation" -compatibility: ">=1.0.0" -metadata: - version: "1.0.0" -references: - - references/patterns/readability.md - - references/patterns/simplicity.md - - references/patterns/design-architecture.md - - references/patterns/testing.md - - references/patterns/error-handling.md - - references/patterns/maintainability.md ---- - -# Design Patterns & Programming Principles - -## When to Apply - -- **Code Generation** β†’ writing new functions, classes, or modules -- **Code Review** β†’ evaluating PRs or existing codebases -- **Refactoring** β†’ improving code structure and clarity -- **Architecture Design** β†’ choosing appropriate patterns and abstractions - -## Core Philosophy - -1. Readability over cleverness β†’ code is read more than written -2. Simplicity over complexity β†’ simplest solution that works -3. Testability by design β†’ write code that's easy to test -4. Incremental improvement β†’ leave code better than you found it -5. Patterns as tools β†’ apply when they clarify, not by default - -## Principle Categories - -### 1. Readability & Clarity -Descriptive naming, consistent formatting, self-documenting code, small focused functions -β†’ `references/patterns/readability.md` - -### 2. Simplicity & Efficiency -KISS, DRY, YAGNI -β†’ `references/patterns/simplicity.md` - -### 3. Design & Architecture -SRP, composition over inheritance, program to interfaces -Patterns: Factory, Strategy, Observer, Decorator, Adapter, Command, Singleton -β†’ `references/patterns/design-architecture.md` - -### 4. Testing & Quality -Automated testing, focused assertions, edge case coverage -β†’ `references/patterns/testing.md` - -### 5. Error Handling -Clear error messages, early validation, proper exception usage -β†’ `references/patterns/error-handling.md` - -### 6. Maintainability -Boy Scout Rule, continuous refactoring, atomic commits, automation -β†’ `references/patterns/maintainability.md` - -## AI-Specific Guidance - -When generating or reviewing code: -1. Check for AI pitfalls listed in each principle -2. Avoid pattern prediction bias β†’ don't use patterns just because they're common -3. Question generic naming β†’ resist `data`, `temp`, `result` without context -4. Validate edge cases β†’ don't skip error handling -5. Keep functions focused β†’ resist combining unrelated operations -6. Match project conventions β†’ maintain consistency with existing codebase - -## Quick Reference - -| Situation | Apply | -|-----------|-------| -| Function > 20 lines | Split into smaller functions (SRP) | -| Repeated code blocks | Extract to function/constant (DRY) | -| Complex conditionals | Strategy or State pattern | -| Object creation logic | Factory pattern | -| Cross-cutting concerns | Decorator or Observer pattern | -| Incompatible interfaces | Adapter pattern | -| Need undo/logging | Command pattern | -| Global access point | Singleton (use sparingly) | - -## Sources - -- *Clean Code* - Robert C. Martin -- *The Pragmatic Programmer* - Andrew Hunt & David Thomas -- *Code Complete* - Steve McConnell -- *Refactoring* - Martin Fowler -- *Design Patterns* - Gang of Four diff --git a/skills/design-patterns-skill/references/misc/overview.md b/skills/design-patterns-skill/references/misc/overview.md deleted file mode 100755 index 4bf7af1..0000000 --- a/skills/design-patterns-skill/references/misc/overview.md +++ /dev/null @@ -1,170 +0,0 @@ -# Design Patterns & Programming Principles Skill - -A comprehensive Kiro skill that provides structured guidance on programming principles and design patterns from foundational software engineering books. - -## Overview - -This skill encapsulates best practices from: -- *Clean Code* by Robert C. Martin -- *The Pragmatic Programmer* by Andrew Hunt & David Thomas -- *Code Complete* by Steve McConnell -- *Refactoring* by Martin Fowler -- *Design Patterns* by Gang of Four - -## Installation - -### For Workspace (Project-Specific) -```bash -mkdir -p .kiro/skills -cp -r design-patterns .kiro/skills/ -``` - -### For Global (All Projects) -```bash -mkdir -p ~/.kiro/skills -cp -r design-patterns ~/.kiro/skills/ -``` - -## Structure - -``` -design-patterns/ -β”œβ”€β”€ SKILL.md # Main skill definition -β”œβ”€β”€ README.md # This file -└── references/ - β”œβ”€β”€ readability.md # Naming, formatting, documentation - β”œβ”€β”€ simplicity.md # KISS, DRY, YAGNI principles - β”œβ”€β”€ design-architecture.md # SRP, patterns, composition - β”œβ”€β”€ testing.md # Testing strategies and best practices - β”œβ”€β”€ error-handling.md # Validation, exceptions, recovery - └── maintainability.md # Refactoring, commits, automation -``` - -## Usage - -The skill activates automatically when: -- Writing new code -- Reviewing pull requests -- Refactoring existing code -- Designing system architecture -- Assisting with AI code generation - -## Principle Categories - -### 1. Readability & Clarity -- Descriptive naming conventions -- Consistent code formatting -- Self-documenting code principles -- Small, focused functions - -### 2. Simplicity & Efficiency -- KISS (Keep It Simple, Stupid) -- DRY (Don't Repeat Yourself) -- YAGNI (You Aren't Gonna Need It) -- Avoiding premature optimization - -### 3. Design & Architecture -- Single Responsibility Principle (SRP) -- Composition over Inheritance -- Program to Interfaces -- Essential Design Patterns: - - Factory Pattern - - Strategy Pattern - - Observer Pattern - - Decorator Pattern - - Adapter Pattern - - Command Pattern - - Singleton Pattern - -### 4. Testing & Quality -- Test-driven development approach -- Focused test assertions -- Test pyramid (unit/integration/e2e) -- Mocking and test doubles - -### 5. Error Handling -- Clear error messages -- Early input validation -- Exception hierarchies -- Recovery strategies - -### 6. Maintainability -- Boy Scout Rule -- Continuous refactoring -- Incremental commits -- Automation and tooling - -## AI-Specific Guidance - -This skill includes specific guidance for AI code generation, helping avoid common pitfalls such as: -- Generic naming (`data`, `temp`, `result`) -- Over-commenting obvious code -- Skipping edge case validation -- Applying patterns unnecessarily -- Creating monolithic functions -- Duplicating code structures - -## Quick Reference Examples - -### Before & After - -**Poor Code:** -```python -def proc(u): - if u['age'] < 13: return False - db.save(u) - email.send(u['email'], 'Welcome') - return True -``` - -**Improved Code:** -```python -def is_eligible_user(user): - return user['age'] >= 13 - -def save_user(user): - db.save(user) - -def send_welcome_email(user): - email.send(user['email'], 'Welcome to the platform') - -def register_user(user): - if not is_eligible_user(user): - raise ValueError('User must be 13 or older') - save_user(user) - send_welcome_email(user) -``` - -## When to Apply - -| Situation | Recommended Principle/Pattern | -|-----------|------------------------------| -| Function > 20 lines | Split using SRP | -| Repeated code blocks | Extract with DRY | -| Complex conditionals | Strategy or State pattern | -| Object creation complexity | Factory pattern | -| Cross-cutting concerns | Decorator or Observer | -| Incompatible interfaces | Adapter pattern | -| Need undo/logging | Command pattern | - -## Contributing - -This skill is structured to be easily extended. To add new principles or patterns: - -1. Update the relevant reference file in `references/` -2. Add a cross-reference in `SKILL.md` -3. Include examples with "Do", "Don't", and "AI Pitfalls" sections - -## License - -This skill is based on principles from publicly available software engineering literature and industry best practices. - -## Additional Resources - -- [The 7 Most Important Software Design Patterns](https://learningdaily.dev/the-7-most-important-software-design-patterns-d60e546afb0e) -- [Refactoring Guru - Design Patterns](https://refactoring.guru/design-patterns) -- [SOLID Principles](https://en.wikipedia.org/wiki/SOLID) - -## Version - -1.0.0 - Initial release diff --git a/skills/design-patterns-skill/references/patterns/design-architecture.md b/skills/design-patterns-skill/references/patterns/design-architecture.md deleted file mode 100755 index fc1cd6c..0000000 --- a/skills/design-patterns-skill/references/patterns/design-architecture.md +++ /dev/null @@ -1,477 +0,0 @@ -# Design & Architecture Principles - -## Single Responsibility Principle (SRP) - -**Definition:** Each class or module should have only one reason to change. It should encapsulate one cohesive responsibility. - -**Supported by:** *Clean Code*, *The Pragmatic Programmer*, SOLID Principles - -### Examples - -```python -# Bad - Multiple responsibilities -class User: - def __init__(self, name, email): - self.name = name - self.email = email - - def save_to_database(self): - # Database logic - pass - - def send_welcome_email(self): - # Email logic - pass - - def generate_report(self): - # Reporting logic - pass - -# Good - Separated concerns -class User: - def __init__(self, name, email): - self.name = name - self.email = email - -class UserRepository: - def save(self, user): - # Database logic - pass - -class EmailService: - def send_welcome(self, user): - # Email logic - pass - -class UserReportGenerator: - def generate(self, user): - # Reporting logic - pass -``` - -### Do -- Encapsulate related data and behavior -- Separate concerns (data access, business logic, presentation) -- Create cohesive modules -- Make reasons for change explicit - -### Don't -- Mix data access, logic, and UI in one class -- Create "god objects" that do everything -- Couple unrelated functionality - -### AI Pitfalls -- Cramming multiple operations into one class -- Creating utility classes with unrelated methods -- Mixing infrastructure and domain logic - ---- - -## Composition Over Inheritance - -**Definition:** Prefer combining objects to form behavior over creating deep class hierarchies. Favor "has-a" relationships over "is-a". - -**Supported by:** *The Pragmatic Programmer*, *Design Patterns* - -### Examples - -```python -# Bad - Rigid inheritance hierarchy -class Bird: - def fly(self): - return "Flying" - -class Penguin(Bird): - def fly(self): - raise Exception("Penguins cannot fly") - -# Good - Composition with behavior injection -class FlyBehavior: - def fly(self): - pass - -class CanFly(FlyBehavior): - def fly(self): - return "Flying" - -class CannotFly(FlyBehavior): - def fly(self): - return "Cannot fly" - -class Bird: - def __init__(self, fly_behavior): - self.fly_behavior = fly_behavior - - def perform_fly(self): - return self.fly_behavior.fly() - -# Usage -sparrow = Bird(CanFly()) -penguin = Bird(CannotFly()) -``` - -### Do -- Use interfaces or protocols to define contracts -- Inject dependencies and behaviors -- Compose small, focused objects -- Favor delegation over inheritance - -### Don't -- Create deep inheritance hierarchies (>3 levels) -- Inherit just to override behavior -- Use inheritance for code reuse alone -- Force unnatural "is-a" relationships - -### AI Pitfalls -- Defaulting to inheritance for code reuse -- Creating rigid class hierarchies -- Not recognizing when composition is clearer - ---- - -## Program to an Interface, Not an Implementation - -**Definition:** Depend on abstractions (interfaces, protocols) rather than concrete implementations. This enables flexibility and testability. - -**Supported by:** *Design Patterns*, *Code Complete*, Dependency Inversion Principle - -### Examples - -```python -# Bad - Depends on concrete implementation -class OrderProcessor: - def __init__(self): - self.payment = StripePayment() # Hard-coded dependency - - def process(self, order): - self.payment.charge(order.total) - -# Good - Depends on abstraction -class PaymentProcessor: - def charge(self, amount): - raise NotImplementedError - -class StripePayment(PaymentProcessor): - def charge(self, amount): - # Stripe-specific logic - pass - -class PayPalPayment(PaymentProcessor): - def charge(self, amount): - # PayPal-specific logic - pass - -class OrderProcessor: - def __init__(self, payment_processor: PaymentProcessor): - self.payment = payment_processor - - def process(self, order): - self.payment.charge(order.total) - -# Usage - Easy to swap implementations -processor = OrderProcessor(StripePayment()) -# or -processor = OrderProcessor(PayPalPayment()) -``` - -### Do -- Define interfaces for key abstractions -- Inject dependencies via constructors -- Use dependency injection frameworks when appropriate -- Code against contracts, not implementations - -### Don't -- Hard-code concrete class names -- Use `isinstance()` checks to switch behavior -- Create tight coupling to specific implementations - -### AI Pitfalls -- Using fixed class names instead of interfaces -- Not recognizing opportunities for abstraction -- Creating concrete dependencies in constructors - ---- - -## Essential Design Patterns - -### Factory Pattern - -**Purpose:** Delegate object creation to factory methods or classes. Decouples client code from concrete instantiation. - -**Use when:** Object creation is complex or varies based on conditions. - -```python -# Example -class LoggerFactory: - @staticmethod - def get_logger(log_type): - if log_type == "file": - return FileLogger() - elif log_type == "console": - return ConsoleLogger() - elif log_type == "cloud": - return CloudLogger() - else: - raise ValueError(f"Unknown logger type: {log_type}") - -# Usage -logger = LoggerFactory.get_logger("file") -logger.log("Application started") -``` - -### Strategy Pattern - -**Purpose:** Define a family of interchangeable algorithms and make them swappable at runtime. - -**Use when:** You need different behaviors for the same operation. - -```python -# Example -class SortStrategy: - def sort(self, data): - raise NotImplementedError - -class QuickSort(SortStrategy): - def sort(self, data): - # Quick sort implementation - pass - -class MergeSort(SortStrategy): - def sort(self, data): - # Merge sort implementation - pass - -class DataProcessor: - def __init__(self, sort_strategy: SortStrategy): - self.sorter = sort_strategy - - def process(self, data): - sorted_data = self.sorter.sort(data) - return sorted_data - -# Usage -processor = DataProcessor(MergeSort()) -result = processor.process([3, 1, 4, 1, 5]) -``` - -### Observer Pattern - -**Purpose:** Define a one-to-many dependency where changes in one object notify all dependents automatically. - -**Use when:** Multiple objects need to react to state changes. - -```python -# Example -class Subject: - def __init__(self): - self._observers = [] - - def attach(self, observer): - self._observers.append(observer) - - def notify(self, event): - for observer in self._observers: - observer.update(event) - -class Observer: - def update(self, event): - raise NotImplementedError - -class EmailNotifier(Observer): - def update(self, event): - print(f"Sending email for: {event}") - -class SlackNotifier(Observer): - def update(self, event): - print(f"Posting to Slack: {event}") - -# Usage -order_system = Subject() -order_system.attach(EmailNotifier()) -order_system.attach(SlackNotifier()) -order_system.notify("Order #123 shipped") -``` - -### Decorator Pattern - -**Purpose:** Dynamically add responsibilities to objects without modifying their class. - -**Use when:** You need flexible, composable enhancements. - -```python -# Example -class Notifier: - def send(self, message): - raise NotImplementedError - -class BasicNotifier(Notifier): - def send(self, message): - print(f"Basic notification: {message}") - -class NotifierDecorator(Notifier): - def __init__(self, notifier: Notifier): - self._notifier = notifier - - def send(self, message): - self._notifier.send(message) - -class SlackDecorator(NotifierDecorator): - def send(self, message): - super().send(message) - print(f"Also sent to Slack: {message}") - -class EmailDecorator(NotifierDecorator): - def send(self, message): - super().send(message) - print(f"Also sent via email: {message}") - -# Usage - Compose behaviors -notifier = EmailDecorator(SlackDecorator(BasicNotifier())) -notifier.send("System alert") -``` - -### Adapter Pattern - -**Purpose:** Convert one interface into another that clients expect. Enables incompatible interfaces to work together. - -**Use when:** Integrating legacy systems or third-party libraries. - -```python -# Example -class LegacyPrinter: - def print_text(self, text): - print(f"[LEGACY] {text}") - -class ModernPrinter: - def print(self, content): - raise NotImplementedError - -class PrinterAdapter(ModernPrinter): - def __init__(self, legacy_printer: LegacyPrinter): - self.legacy = legacy_printer - - def print(self, content): - self.legacy.print_text(content) - -# Usage -old_printer = LegacyPrinter() -adapter = PrinterAdapter(old_printer) -adapter.print("Hello World") # Uses modern interface, delegates to legacy -``` - -### Command Pattern - -**Purpose:** Encapsulate a request as an object, enabling queuing, logging, or undoable operations. - -**Use when:** You need to queue operations, support undo/redo, or log actions. - -```python -# Example -class Command: - def execute(self): - raise NotImplementedError - - def undo(self): - raise NotImplementedError - -class Light: - def on(self): - print("Light is ON") - - def off(self): - print("Light is OFF") - -class LightOnCommand(Command): - def __init__(self, light: Light): - self.light = light - - def execute(self): - self.light.on() - - def undo(self): - self.light.off() - -class LightOffCommand(Command): - def __init__(self, light: Light): - self.light = light - - def execute(self): - self.light.off() - - def undo(self): - self.light.on() - -# Usage -living_room_light = Light() -light_on = LightOnCommand(living_room_light) -light_on.execute() # Light is ON -light_on.undo() # Light is OFF -``` - -### Singleton Pattern - -**Purpose:** Ensure only one instance of a class exists globally. - -**Use when:** You need a single point of access (e.g., config, logger, connection pool). - -**Caution:** Often overused. Consider dependency injection instead. - -```python -# Example -class Singleton: - _instance = None - - def __new__(cls): - if cls._instance is None: - cls._instance = super().__new__(cls) - return cls._instance - -class ConfigManager(Singleton): - def __init__(self): - if not hasattr(self, 'initialized'): - self.config = {} - self.initialized = True - -# Usage -config1 = ConfigManager() -config2 = ConfigManager() -assert config1 is config2 # Same instance -``` - ---- - -## Pattern Usage Guidelines - -### Do -- Apply patterns when they improve clarity and flexibility -- Choose patterns based on structural fit -- Use patterns to communicate design intent -- Combine patterns when appropriate - -### Don't -- Force patterns into simple code -- Use patterns for the sake of patterns -- Apply patterns without understanding the problem -- Over-abstract with unnecessary pattern layers - -### AI Pitfalls -- Predicting patterns where none are needed -- Misnaming pattern roles (e.g., calling a simple factory a "Factory Pattern") -- Misapplying pattern intent (e.g., Singleton for everything) -- Creating pattern boilerplate without actual benefit - ---- - -## Summary - -Good architecture is: -- **Modular** - clear boundaries and responsibilities -- **Flexible** - uses composition and interfaces -- **Abstract** - depends on contracts, not implementations -- **Pattern-aware** - applies proven solutions appropriately - -When designing systems, ask: -- Does each module have one clear responsibility? -- Can I swap implementations easily? -- Am I using inheritance or composition? -- Does this pattern solve a real structural problem? diff --git a/skills/design-patterns-skill/references/patterns/error-handling.md b/skills/design-patterns-skill/references/patterns/error-handling.md deleted file mode 100755 index 1b1c25f..0000000 --- a/skills/design-patterns-skill/references/patterns/error-handling.md +++ /dev/null @@ -1,364 +0,0 @@ -# Error Handling & Input Validation - -## Handle Errors Clearly - -**Definition:** Use exceptions for unexpected states and provide clear error messages. Fail early and explicitly rather than allowing silent failures. - -**Supported by:** *Code Complete*, *Clean Code*, *The Pragmatic Programmer* - -### Examples - -```python -# Bad - Silent failure -def divide(a, b): - if b == 0: - return None # Caller has to check for None - return a / b - -# Good - Explicit error -def divide(a, b): - if b == 0: - raise ValueError("Cannot divide by zero") - return a / b -``` - -```python -# Bad - Vague error message -def process_user(user): - if not user: - raise Exception("Error") - -# Good - Descriptive error message -def process_user(user): - if user is None: - raise ValueError("User object cannot be None") - if not user.email: - raise ValueError(f"User {user.id} must have a valid email address") -``` - -### Do -- Use exceptions for exceptional conditions -- Provide descriptive error messages -- Include context (what failed, why, what was expected) -- Fail fast - validate inputs early -- Use specific exception types -- Document what exceptions can be raised - -### Don't -- Return None or -1 as error codes -- Swallow exceptions silently -- Use exceptions for control flow -- Provide generic error messages ("Error occurred") -- Catch exceptions you can't handle - -### AI Pitfalls -- Skipping edge-case validation -- Empty except blocks: `except: pass` -- Returning default values instead of raising errors -- Generic exception types instead of specific ones - ---- - -## Validate Inputs Early - -**Definition:** Check preconditions at the entry point of functions. Reject invalid input before processing. - -**Supported by:** *Code Complete*, *Clean Code* - -### Examples - -```python -# Bad - Late validation, partial processing -def register_user(username, email, age): - user = User(username, email, age) - save_to_database(user) - if age < 18: # Too late - already saved! - raise ValueError("User must be 18 or older") - -# Good - Early validation -def register_user(username, email, age): - if not username or len(username) < 3: - raise ValueError("Username must be at least 3 characters") - if not email or '@' not in email: - raise ValueError("Invalid email address") - if age < 18: - raise ValueError("User must be 18 or older") - - user = User(username, email, age) - save_to_database(user) -``` - -### Guard Clauses - -Use guard clauses to validate and exit early: - -```python -# Bad - Nested conditions -def process_order(order): - if order is not None: - if order.items: - if order.total > 0: - # Main logic here - charge_payment(order) - ship_order(order) - -# Good - Guard clauses -def process_order(order): - if order is None: - raise ValueError("Order cannot be None") - if not order.items: - raise ValueError("Order must contain at least one item") - if order.total <= 0: - raise ValueError("Order total must be positive") - - # Main logic - no nesting - charge_payment(order) - ship_order(order) -``` - -### Do -- Validate at function entry -- Use guard clauses to reduce nesting -- Check preconditions explicitly -- Validate types and ranges -- Use type hints and runtime validation - -### Don't -- Defer validation until deep in the logic -- Assume inputs are valid -- Mix validation with business logic - ---- - -## Exception Hierarchy - -**Definition:** Use specific exception types to allow targeted error handling. - -### Examples - -```python -# Bad - Generic exceptions -def fetch_user(user_id): - if user_id < 0: - raise Exception("Invalid ID") - user = db.get(user_id) - if not user: - raise Exception("Not found") - return user - -# Good - Specific exceptions -class InvalidUserIdError(ValueError): - pass - -class UserNotFoundError(LookupError): - pass - -def fetch_user(user_id): - if user_id < 0: - raise InvalidUserIdError(f"User ID must be positive, got {user_id}") - user = db.get(user_id) - if not user: - raise UserNotFoundError(f"User with ID {user_id} not found") - return user - -# Caller can handle specifically -try: - user = fetch_user(user_id) -except InvalidUserIdError as e: - return {"error": "bad_request", "message": str(e)} -except UserNotFoundError as e: - return {"error": "not_found", "message": str(e)} -``` - -### Do -- Create custom exception classes for domain errors -- Inherit from appropriate built-in exceptions -- Use exception hierarchies for related errors -- Document exception types in docstrings - -### Don't -- Raise generic `Exception` or `RuntimeError` -- Create exceptions for every possible error -- Use exceptions for non-exceptional cases - ---- - -## Error Recovery Strategies - -### Retry with Backoff - -```python -import time - -def fetch_with_retry(url, max_attempts=3): - for attempt in range(max_attempts): - try: - return http.get(url) - except TransientError as e: - if attempt == max_attempts - 1: - raise - wait_time = 2 ** attempt # Exponential backoff - time.sleep(wait_time) -``` - -### Fallback Mechanisms - -```python -def get_user_avatar(user_id): - try: - return cdn.fetch_avatar(user_id) - except CDNError: - # Fallback to default avatar - return DEFAULT_AVATAR_URL -``` - -### Circuit Breaker - -```python -class CircuitBreaker: - def __init__(self, failure_threshold=5): - self.failure_count = 0 - self.threshold = failure_threshold - self.state = "closed" # closed, open, half-open - - def call(self, func, *args): - if self.state == "open": - raise CircuitOpenError("Service is temporarily unavailable") - - try: - result = func(*args) - self.on_success() - return result - except Exception as e: - self.on_failure() - raise - - def on_success(self): - self.failure_count = 0 - self.state = "closed" - - def on_failure(self): - self.failure_count += 1 - if self.failure_count >= self.threshold: - self.state = "open" -``` - ---- - -## Logging vs. Exceptions - -**Definition:** Log for diagnostics, use exceptions for control flow. - -### When to Log - -```python -# Log operational info -logger.info(f"Processing order {order_id}") - -# Log warnings for recoverable issues -logger.warning(f"Slow query detected: {duration}ms") - -# Log errors with context -try: - process_payment(order) -except PaymentError as e: - logger.error(f"Payment failed for order {order.id}", exc_info=True) - raise # Re-raise after logging -``` - -### When to Raise Exceptions - -```python -# Invalid input - exception -def set_age(age): - if age < 0 or age > 150: - raise ValueError(f"Invalid age: {age}") - -# Business rule violation - exception -def withdraw(account, amount): - if account.balance < amount: - raise InsufficientFundsError(f"Balance: {account.balance}, requested: {amount}") - -# Operational issue - log + exception -def connect_to_database(): - try: - return db.connect() - except ConnectionError as e: - logger.error("Database connection failed", exc_info=True) - raise DatabaseUnavailableError("Cannot connect to database") from e -``` - -### Do -- Log context before re-raising -- Include exception traceback in logs -- Use structured logging for searchability -- Set appropriate log levels - -### Don't -- Log and swallow exceptions -- Log sensitive data (passwords, tokens) -- Over-log routine operations - ---- - -## Error Messages Best Practices - -### Good Error Messages - -**What went wrong:** -``` -"Invalid email address: 'user@domain' - missing top-level domain" -``` - -**What was expected:** -``` -"Order total must be positive, got -50.00" -``` - -**How to fix it:** -``` -"File not found: '/data/input.csv'. Check that the file exists and path is correct." -``` - -**Actionable context:** -``` -"User authentication failed: Invalid API key. Please check your credentials in the dashboard." -``` - -### Bad Error Messages - -``` -"Error" # Too vague -"Something went wrong" # Unhelpful -"Invalid input" # Missing details -"Error code: 42" # No explanation -``` - -### Do -- Explain what failed and why -- Include actual vs. expected values -- Suggest corrective actions -- Avoid technical jargon for user-facing errors -- Use clear, plain language - -### Don't -- Expose internal implementation details to end users -- Include stack traces in user-facing messages -- Use codes without explanations -- Be condescending ("You entered invalid data") - ---- - -## Summary - -Effective error handling: -- **Fails fast** - Validates early and explicitly -- **Provides clarity** - Error messages explain what and why -- **Uses exceptions correctly** - For exceptional conditions only -- **Enables recovery** - Appropriate retry and fallback strategies - -When handling errors, ask: -- Have I validated all inputs? -- Will the error message help someone fix the issue? -- Am I using the right exception type? -- Should this be logged, raised, or both? diff --git a/skills/design-patterns-skill/references/patterns/maintainability.md b/skills/design-patterns-skill/references/patterns/maintainability.md deleted file mode 100755 index a085889..0000000 --- a/skills/design-patterns-skill/references/patterns/maintainability.md +++ /dev/null @@ -1,548 +0,0 @@ -# Maintainability & Best Practices - -## Boy Scout Rule - -**Definition:** "Leave the code better than you found it." Make small improvements whenever you touch existing code. - -**Supported by:** *Clean Code*, *The Pragmatic Programmer* - -### Examples - -```python -# Before - Existing code you're modifying -def calc(a, b): - return a + b - -# After - Improved while making your change -def calculate_sum(a, b): - """Return the sum of two numbers.""" - return a + b -``` - -```python -# Before - Adding a feature to messy code -def processUser(u): - # Check age - if u.age<18:return False - db.save(u) - return True - -# After - Clean up while you're here -def process_user(user): - """Register an eligible user.""" - if not is_eligible_user(user): - return False - save_user(user) - return True - -def is_eligible_user(user): - return user.age >= 18 -``` - -### Do -- Improve variable names -- Extract magic numbers to constants -- Add missing docstrings -- Fix formatting inconsistencies -- Remove dead code -- Simplify complex conditions - -### Don't -- Make unrelated large refactors -- Change behavior without tests -- Add hacks or workarounds -- Ignore obvious issues ("not my code") - -### AI Pitfalls -- Regenerating dirty code without improvements -- Not suggesting cleanup opportunities -- Adding to technical debt instead of reducing it - ---- - -## Continuous Refactoring - -**Definition:** Improve code structure regularly through small, safe changes backed by tests. Refactoring should be ongoing, not a separate phase. - -**Supported by:** *Refactoring*, *Code Complete*, *Clean Code* - -### Common Refactorings - -**Extract Method** -```python -# Before -def process_order(order): - # Validate - if not order.items: - raise ValueError("Empty order") - - # Calculate total - total = 0 - for item in order.items: - total += item.price * item.quantity - - # Apply discount - if order.customer.is_premium: - total *= 0.9 - - return total - -# After -def process_order(order): - validate_order(order) - total = calculate_total(order) - return apply_discount(total, order.customer) - -def validate_order(order): - if not order.items: - raise ValueError("Empty order") - -def calculate_total(order): - return sum(item.price * item.quantity for item in order.items) - -def apply_discount(total, customer): - if customer.is_premium: - return total * 0.9 - return total -``` - -**Extract Variable** -```python -# Before -if (user.age >= 18 and user.has_verified_email and user.account_status == 'active'): - grant_access() - -# After -is_adult = user.age >= 18 -has_verified_email = user.has_verified_email -is_active = user.account_status == 'active' - -if is_adult and has_verified_email and is_active: - grant_access() -``` - -**Rename** -```python -# Before -def fn(x, y): - return x * y - -# After -def calculate_area(width, height): - return width * height -``` - -**Replace Magic Numbers** -```python -# Before -def calculate_price(quantity): - if quantity > 100: - return quantity * 9.99 * 0.85 - return quantity * 9.99 - -# After -UNIT_PRICE = 9.99 -BULK_DISCOUNT = 0.85 -BULK_THRESHOLD = 100 - -def calculate_price(quantity): - price = quantity * UNIT_PRICE - if quantity > BULK_THRESHOLD: - price *= BULK_DISCOUNT - return price -``` - -### Refactoring Workflow - -1. **Ensure tests pass** - Start with green tests -2. **Make one change** - Small, focused refactor -3. **Run tests** - Verify behavior unchanged -4. **Commit** - Save working state -5. **Repeat** - Iterate on improvements - -### Do -- Refactor in small steps -- Run tests after each change -- Commit frequently -- Use IDE refactoring tools -- Keep behavior identical - -### Don't -- Refactor without tests -- Mix refactoring with feature work -- Make multiple changes at once -- Skip running tests -- Delay commits - -### AI Pitfalls -- Suggesting large refactors without incremental steps -- Omitting test runs between changes -- Changing behavior during refactoring - ---- - -## Version Control & Incremental Work - -**Definition:** Commit code in logical, testable chunks. Each commit should represent a complete, working unit of change. - -**Supported by:** *Refactoring*, *The Pragmatic Programmer*, Agile practices - -### Good Commit Practices - -**Atomic Commits** -``` -βœ“ "Add user email validation" -βœ“ "Extract payment processing to service" -βœ“ "Fix off-by-one error in pagination" - -βœ— "Fixed stuff" -βœ— "WIP" -βœ— "Updated files" -``` - -**Commit Messages** -``` -# Good - Imperative mood, clear intent -Add password strength validation - -Implement validation rules: -- Minimum 8 characters -- At least one uppercase letter -- At least one number -- At least one special character - -Closes #123 - -# Bad -fixed login -``` - -### Commit Workflow - -```bash -# 1. Make a focused change -# 2. Run tests -pytest - -# 3. Review changes -git diff - -# 4. Stage related files -git add user_validator.py tests/test_validator.py - -# 5. Commit with clear message -git commit -m "Add email format validation" - -# 6. Repeat for next logical change -``` - -### Do -- Commit working, tested code -- Write descriptive commit messages -- Keep commits focused and atomic -- Use branches for features -- Commit frequently - -### Don't -- Commit broken code -- Mix unrelated changes in one commit -- Skip commit messages -- Commit sensitive data (API keys, passwords) -- Leave uncommitted changes overnight - -### AI Pitfalls -- Generating large changes without guiding commit boundaries -- Not suggesting logical commit points -- Creating code that can't be committed incrementally - ---- - -## Code Reviews - -**Definition:** Systematic examination of code changes by peers to catch issues, share knowledge, and maintain quality. - -### Review Checklist - -**Correctness** -- Does it solve the stated problem? -- Are edge cases handled? -- Is error handling appropriate? -- Are there off-by-one errors or race conditions? - -**Design** -- Is it in the right place? -- Does it follow existing patterns? -- Is complexity warranted? -- Could it be simpler? - -**Readability** -- Are names clear? -- Is logic easy to follow? -- Are comments helpful (not redundant)? -- Is formatting consistent? - -**Testing** -- Are tests included? -- Do tests cover edge cases? -- Are tests readable and maintainable? - -**Security** -- Is input validated? -- Are secrets hardcoded? -- Are SQL queries parameterized? -- Is authentication/authorization correct? - -### Review Etiquette - -**As Reviewer** -``` -βœ“ "Consider extracting this to a helper function for reusability" -βœ“ "Could we add a test for the empty list case?" -βœ“ "This is clever! Can we add a comment explaining the algorithm?" - -βœ— "This is terrible" -βœ— "Why didn't you just..." -βœ— "Obviously this is wrong" -``` - -**As Author** -- Respond to all feedback -- Ask for clarification -- Explain non-obvious decisions -- Be open to suggestions -- Thank reviewers - -### Do -- Review promptly -- Focus on substance over style -- Suggest improvements, don't demand -- Automate style checks -- Learn from reviews you receive - -### Don't -- Approve without reading -- Nitpick trivial issues -- Review your own PRs -- Take criticism personally -- Skip review for "small" changes - ---- - -## Automation and Tooling - -**Definition:** Automate repetitive tasks and use tools to maintain consistency and quality. - -**Supported by:** *The Pragmatic Programmer*, *Clean Code* - -### Essential Tools - -**Linters** - Catch common mistakes -```bash -# Python -pylint myapp/ -flake8 myapp/ - -# JavaScript -eslint src/ - -# Go -golangci-lint run -``` - -**Formatters** - Maintain consistent style -```bash -# Python -black myapp/ - -# JavaScript -prettier --write src/ - -# Rust -rustfmt src/ -``` - -**Type Checkers** - Catch type errors -```bash -# Python -mypy myapp/ - -# TypeScript -tsc --noEmit - -# Flow -flow check -``` - -**Test Runners** - Verify behavior -```bash -# Python -pytest - -# JavaScript -jest - -# Go -go test ./... -``` - -### Continuous Integration - -```yaml -# .github/workflows/ci.yml -name: CI -on: [push, pull_request] - -jobs: - test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Install dependencies - run: pip install -r requirements.txt - - name: Lint - run: flake8 . - - name: Type check - run: mypy . - - name: Test - run: pytest --cov - - name: Security scan - run: bandit -r . -``` - -### Pre-commit Hooks - -```bash -# .pre-commit-config.yaml -repos: - - repo: https://github.com/psf/black - hooks: - - id: black - - repo: https://github.com/pycqa/flake8 - hooks: - - id: flake8 - - repo: https://github.com/pre-commit/pre-commit-hooks - hooks: - - id: trailing-whitespace - - id: end-of-file-fixer - - id: check-yaml -``` - -### Do -- Integrate tools into workflow -- Run checks locally before pushing -- Fail builds on violations -- Configure tools consistently -- Update tools regularly - -### Don't -- Rely on manual checks -- Ignore tool warnings -- Skip tools for "quick fixes" -- Disable checks without good reason - -### AI Pitfalls -- Producing code that doesn't pass linting -- Ignoring type annotations -- Generating code incompatible with project tools - ---- - -## Documentation - -**Definition:** Provide context and explanations where code alone isn't sufficient. - -### What to Document - -**APIs and Public Interfaces** -```python -def calculate_shipping_cost(weight_kg: float, destination: str) -> float: - """Calculate shipping cost based on weight and destination. - - Args: - weight_kg: Package weight in kilograms (must be positive) - destination: ISO 3166-1 alpha-2 country code - - Returns: - Shipping cost in USD - - Raises: - ValueError: If weight is negative or destination is invalid - - Example: - >>> calculate_shipping_cost(2.5, 'US') - 12.50 - """ -``` - -**Complex Algorithms** -```python -def dijkstra(graph, start): - """Find shortest paths using Dijkstra's algorithm. - - Time complexity: O((V + E) log V) where V is vertices, E is edges - Space complexity: O(V) - - See: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm - """ -``` - -**Non-Obvious Decisions** -```python -# Using MD5 for cache keys only - NOT for security -# MD5 is fast and collision-resistant enough for this use case -cache_key = hashlib.md5(url.encode()).hexdigest() -``` - -**Setup and Configuration** -```markdown -# README.md - -## Installation - -pip install -r requirements.txt - -## Configuration - -Set environment variables: -- `DATABASE_URL`: PostgreSQL connection string -- `API_KEY`: Third-party service API key - -## Running - -python app.py -``` - -### Don't Document - -- Obvious code (let code be self-documenting) -- Implementation details that change frequently -- Duplicated information available elsewhere - -### Do -- Keep docs close to code -- Update docs with code changes -- Use examples liberally -- Link to external references - -### Don't -- Let docs become stale -- Over-document simple code -- Duplicate info across files - ---- - -## Summary - -Maintainable code: -- **Improves incrementally** - Boy Scout Rule -- **Refactors continuously** - Small, safe improvements -- **Commits logically** - Atomic, tested changes -- **Automates quality** - Linters, formatters, CI/CD -- **Documents appropriately** - Context where needed - -When maintaining code, ask: -- Can I improve this while I'm here? -- Is this change small and safe? -- Should I commit now? -- Are my tools catching issues? -- Does this need documentation? diff --git a/skills/design-patterns-skill/references/patterns/readability.md b/skills/design-patterns-skill/references/patterns/readability.md deleted file mode 100755 index edf85e0..0000000 --- a/skills/design-patterns-skill/references/patterns/readability.md +++ /dev/null @@ -1,195 +0,0 @@ -# Readability & Clarity Principles - -## Descriptive Naming - -**Definition:** Use clear, meaningful names for variables, functions, classes, etc., so code reads like natural language. Avoid vague, abbreviated, or encoded names. Good names explain intent without requiring comments. - -**Supported by:** *Clean Code*, *Code Complete* - -### Examples - -```python -# Bad -def calc(a, b): - return a * b + 3 - -# Good -def calculate_rectangle_area(width, height): - margin = 3 - return width * height + margin -``` - -### Do -- Use nouns for data structures and variables -- Use verbs for functions and methods -- Use consistent domain terminology -- Make names pronounceable and searchable -- Use solution/problem domain names - -### Don't -- Use single-letter names (except loop counters in small scopes) -- Create misleading names -- Use encodings or prefixes (Hungarian notation) -- Use abbreviations unless universally known -- Mix naming conventions in the same scope - -### AI Pitfalls -- Repeating generic names like `data`, `temp`, `foo`, `result` -- Inconsistent naming across similar concepts -- Using placeholder names and forgetting to rename -- Over-shortening meaningful names for brevity - ---- - -## Consistent Style & Formatting - -**Definition:** Follow a uniform coding style and project conventions. Consistency aids readability and reduces cognitive load. - -**Supported by:** *Clean Code*, *Code Complete* - -### Examples - -```javascript -// Bad - Inconsistent spacing, braces, indentation -if(x>0){ -y= x+10; - console.log(y);} - -// Good - Consistent formatting -if (x > 0) { - let result = x + 10; - console.log(result); -} -``` - -### Do -- Stick to one brace style (K&R, Allman, etc.) -- Use consistent indentation (2 or 4 spaces, never mix tabs/spaces) -- Follow language conventions (PEP 8 for Python, Airbnb for JS) -- Maintain consistent line length (80-120 characters) -- Use automated formatters (Prettier, Black, rustfmt) - -### Don't -- Mix different formatting styles in one file -- Ignore project linting rules -- Use inconsistent whitespace -- Create overly long lines - -### AI Pitfalls -- Producing inconsistent formatting across code blocks -- Mixing indentation styles -- Ignoring existing project formatting conventions - ---- - -## Self-Documenting Code (Minimize Comments) - -**Definition:** Write code so its intent is clear from the code itself. Comments should explain *why*, not *what*. - -**Supported by:** *Clean Code*, *Code Complete* - -### Examples - -```python -# Bad - Redundant comment -# Increment i by 1 -i = i + 1 - -# Good - No comment needed -i = i + 1 - -# Acceptable - Explains business rule -# Block access for users under minimum age requirement -if user.age < 13: - block_access() - -# Good - Explains non-obvious why -# Using exponential backoff to avoid API rate limits -retry_delay = base_delay * (2 ** attempt_count) -``` - -### Do -- Use clear naming and logic structure -- Comment complex algorithms or business rules -- Explain performance optimizations -- Document API contracts and side effects -- Add TODO comments for future work (with ticket IDs) - -### Don't -- Write comments that restate the code -- Leave commented-out code -- Write misleading or outdated comments -- Use comments to fix bad naming - -### AI Pitfalls -- Over-commenting obvious operations -- Leaving stale or contradictory comments -- Using comments instead of refactoring unclear code - ---- - -## Small Functions & Single Responsibility - -**Definition:** Functions and methods should do one thing and do it well. Small, cohesive units are easier to understand, test, and maintain. - -**Supported by:** *Clean Code*, *Code Complete* - -### Examples - -```python -# Bad - Function does too many things -def update_user(data): - validate(data) - update_database(data) - send_email(data) - log_activity(data) - invalidate_cache(data) - -# Good - Separated concerns -def update_user(data): - validated_data = validate(data) - save_user(validated_data) - notify_user(validated_data) - -def save_user(data): - update_database(data) - invalidate_cache(data) - -def notify_user(data): - send_email(data) - log_activity(data) -``` - -### Do -- Keep functions under 20-30 lines when possible -- Extract helper functions for complex logic -- Use descriptive function names that indicate purpose -- Limit function parameters (ideally ≀ 3) -- Make one level of abstraction per function - -### Don't -- Combine unrelated operations -- Create deeply nested logic -- Use flag arguments to control behavior -- Write functions that both query and modify state - -### AI Pitfalls -- Creating monolithic functions with multiple responsibilities -- Over-fragmenting into excessive tiny functions -- Mixing abstraction levels within one function -- Generating functions that modify global state unexpectedly - ---- - -## Summary - -Readable code is: -- **Self-explanatory** through naming -- **Consistent** in style and structure -- **Minimal in comments** - code speaks for itself -- **Small and focused** - easy to understand at a glance - -When writing or reviewing code, ask: -- Can I understand this without the author present? -- Would I want to debug this at 2 AM? -- Does this follow the team's conventions? diff --git a/skills/design-patterns-skill/references/patterns/simplicity.md b/skills/design-patterns-skill/references/patterns/simplicity.md deleted file mode 100755 index 54b8d1d..0000000 --- a/skills/design-patterns-skill/references/patterns/simplicity.md +++ /dev/null @@ -1,279 +0,0 @@ -# Simplicity & Efficiency Principles - -## KISS (Keep It Simple, Stupid) - -**Definition:** Use the simplest solution that solves the problem. Avoid unnecessary complexity, over-engineering, or premature optimization. - -**Supported by:** *Clean Code*, *The Pragmatic Programmer* - -### Examples - -```javascript -// Bad - Unnecessary abstraction -class SingleValueContainer { - constructor(value) { - this.values = [value]; - } - add(value) { - this.values.push(value); - } - getValue() { - return this.values[0]; - } -} - -// Good - Use built-in features -let numbers = [5]; -numbers.push(7); -let firstNumber = numbers[0]; -``` - -```python -# Bad - Over-complicated -def is_even(n): - return True if n % 2 == 0 else False - -# Good - Direct and clear -def is_even(n): - return n % 2 == 0 -``` - -### Do -- Use language built-ins and standard libraries -- Choose clear, direct solutions -- Optimize only when profiling shows need -- Prefer composition of simple parts -- Write code for the current requirement - -### Don't -- Create abstractions without clear benefit -- Add complexity for hypothetical future needs -- Use clever tricks that obscure intent -- Build custom solutions when standard ones exist - -### AI Pitfalls -- Using classes or design patterns unnecessarily -- Creating abstractions for single-use code -- Over-complicating simple conditional logic -- Generating enterprise patterns for simple scripts - ---- - -## DRY (Don't Repeat Yourself) - -**Definition:** Eliminate duplicated code and logic. Every piece of knowledge should have a single, authoritative representation. - -**Supported by:** *The Pragmatic Programmer*, *Clean Code* - -### Examples - -```python -# Bad - Duplicated logic -def circle_area(radius): - return 3.14159 * radius * radius - -def quarter_circle_area(radius): - return 3.14159 * radius * radius / 4 - -def sphere_volume(radius): - return (4/3) * 3.14159 * radius * radius * radius - -# Good - Extracted constant and reused logic -PI = 3.14159 - -def circle_area(radius): - return PI * radius ** 2 - -def quarter_circle_area(radius): - return circle_area(radius) / 4 - -def sphere_volume(radius): - return (4/3) * PI * radius ** 3 -``` - -```javascript -// Bad - Repeated validation -function createUser(name, email) { - if (!email.includes('@')) throw Error('Invalid email'); - // ... -} - -function updateEmail(userId, email) { - if (!email.includes('@')) throw Error('Invalid email'); - // ... -} - -// Good - Extracted validation -function validateEmail(email) { - if (!email.includes('@')) { - throw Error('Invalid email'); - } -} - -function createUser(name, email) { - validateEmail(email); - // ... -} - -function updateEmail(userId, email) { - validateEmail(email); - // ... -} -``` - -### Do -- Extract common logic into functions -- Use constants for repeated values -- Abstract similar patterns -- Share code across modules appropriately -- Keep abstractions at the right level - -### Don't -- Copy-paste code blocks -- Duplicate business rules -- Repeat validation logic -- Hard-code the same values multiple times -- Create premature abstractions (see Rule of Three) - -### Rule of Three -Wait until you see duplication **three times** before abstracting. Two instances might be coincidental; three suggests a pattern. - -### AI Pitfalls -- Producing repeated code structures from pattern prediction -- Duplicating similar functions instead of parameterizing -- Repeating validation or error handling logic -- Not recognizing when to extract shared utilities - ---- - -## YAGNI (You Aren't Gonna Need It) - -**Definition:** Don't implement features or infrastructure until you actually need them. Avoid speculative development. - -**Supported by:** *The Pragmatic Programmer*, Extreme Programming (XP) - -### Examples - -```python -# Bad - Building for hypothetical futures -def process_order(order): - prepare_invoice(order) - apply_future_discount_system(order) # Not used yet - schedule_loyalty_rewards(order) # Not needed now - prepare_for_blockchain_audit(order) # Speculative - -# Good - Only what's needed now -def process_order(order): - prepare_invoice(order) - charge_payment(order) - ship_order(order) -``` - -```javascript -// Bad - Over-engineered configuration -class DatabaseConfig { - constructor() { - this.primaryHost = 'localhost'; - this.replicaHosts = []; // Not using replication - this.shardingStrategy = null; // Not sharding - this.cacheLayer = null; // No cache yet - } -} - -// Good - Current requirements only -class DatabaseConfig { - constructor(host) { - this.host = host; - } -} -``` - -### Do -- Write code for current, known requirements -- Add features when they're actually requested -- Keep infrastructure minimal -- Refactor when new needs emerge -- Trust that future changes will be manageable - -### Don't -- Build "just in case" features -- Create extensibility points without use cases -- Add configuration for hypothetical scenarios -- Implement features before they're specified - -### AI Pitfalls -- Generating code for unspecified future features -- Adding unnecessary configuration options -- Creating extensibility hooks without current need -- Building infrastructure beyond MVP scope - ---- - -## Premature Optimization - -**Definition:** Don't optimize until you have evidence of a performance problem. Clarity and correctness come first. - -**Supported by:** *The Pragmatic Programmer*, Donald Knuth's famous quote - -> "Premature optimization is the root of all evil" - Donald Knuth - -### Examples - -```python -# Bad - Premature optimization -def find_user(user_id): - # Using complex caching before knowing if it's needed - cache_key = f"user:{user_id}:v2" - if cache_key in cache: - return deserialize(decompress(cache[cache_key])) - user = db.query(user_id) - cache[cache_key] = compress(serialize(user)) - return user - -# Good - Start simple, optimize if needed -def find_user(user_id): - return db.query(user_id) - -# Later, if profiling shows this is slow: -def find_user(user_id): - cached = cache.get(f"user:{user_id}") - if cached: - return cached - user = db.query(user_id) - cache.set(f"user:{user_id}", user) - return user -``` - -### Do -- Write clear, correct code first -- Profile before optimizing -- Optimize only proven bottlenecks -- Measure impact of optimizations -- Document why optimizations were made - -### Don't -- Sacrifice readability for unmeasured performance -- Optimize without profiling data -- Use complex algorithms for small datasets -- Cache everything "just in case" - -### AI Pitfalls -- Adding caching layers without justification -- Using complex data structures for simple cases -- Micro-optimizing at the expense of clarity - ---- - -## Summary - -Simple code is: -- **Direct** - solves the problem at hand -- **DRY** - has no unnecessary duplication -- **Minimal** - contains only what's needed now -- **Clear** - prioritizes readability over premature optimization - -When writing code, ask: -- Is this the simplest approach that works? -- Am I repeating myself? -- Do I actually need this now? -- Am I optimizing based on evidence? diff --git a/skills/design-patterns-skill/references/patterns/testing.md b/skills/design-patterns-skill/references/patterns/testing.md deleted file mode 100755 index 6ddc615..0000000 --- a/skills/design-patterns-skill/references/patterns/testing.md +++ /dev/null @@ -1,309 +0,0 @@ -# Testing & Quality Principles - -## Write Automated Tests Early - -**Definition:** Use tests to guide design, prevent regressions, and validate behavior. Testing should be part of the development process, not an afterthought. - -**Supported by:** *Refactoring*, *The Pragmatic Programmer*, Test-Driven Development (TDD) - -### Examples - -```python -# Test-first approach -def test_calculate_discount(): - # Arrange - price = 100 - discount_percent = 10 - - # Act - result = calculate_discount(price, discount_percent) - - # Assert - assert result == 90 - -def calculate_discount(price, discount_percent): - return price * (1 - discount_percent / 100) -``` - -```python -# Test edge cases -def test_user_age_validation(): - assert is_adult(18) == True - assert is_adult(17) == False - assert is_adult(0) == False - assert is_adult(150) == True # No upper bound check yet - -def is_adult(age): - return age >= 18 -``` - -### Do -- Write tests before or alongside code -- Test edge cases and boundary conditions -- Test business logic thoroughly -- Use descriptive test names -- Keep tests fast and independent -- Use test fixtures and setup/teardown appropriately - -### Don't -- Skip tests for "simple" code -- Test implementation details instead of behavior -- Write brittle tests that break on refactoring -- Ignore failing tests -- Write tests that depend on external state - -### AI Pitfalls -- Missing tests entirely -- Writing overly broad test functions -- Not testing edge cases or error paths -- Creating tests with vague assertions - ---- - -## One Assert Per Test (Focus) - -**Definition:** Keep tests focused on a single behavior or scenario. This makes failures easy to diagnose. - -**Supported by:** *Clean Code*, TDD best practices - -### Examples - -```python -# Bad - Multiple unrelated assertions -def test_user(): - user = User("Alice", 25) - assert user.name == "Alice" - assert user.age == 25 - assert user.is_adult() == True - assert user.can_vote() == True - assert user.get_greeting() == "Hello, Alice" - -# Good - Focused tests -def test_user_name_is_set_correctly(): - user = User("Alice", 25) - assert user.name == "Alice" - -def test_user_age_is_set_correctly(): - user = User("Alice", 25) - assert user.age == 25 - -def test_user_is_adult_when_age_18_or_above(): - user = User("Alice", 25) - assert user.is_adult() == True - -def test_user_is_not_adult_when_age_below_18(): - user = User("Bob", 17) - assert user.is_adult() == False -``` - -### Guideline Exceptions - -Multiple assertions are acceptable when: -- Testing object state after a single operation -- Verifying related properties of one concept -- Testing list/collection contents - -```python -# Acceptable - Related assertions on same concept -def test_order_creation(): - order = Order(items=[item1, item2]) - assert len(order.items) == 2 - assert order.total == 50.00 - assert order.status == OrderStatus.PENDING -``` - -### Do -- Use test names to describe expected behavior -- Group related tests in test classes -- Use parametrized tests for similar scenarios -- Make test intent crystal clear - -### Don't -- Group many checks together -- Test multiple behaviors in one test -- Create generic test names like `test_user()` - -### AI Pitfalls -- Combining multiple assertions in one test function -- Creating catch-all test functions -- Not using descriptive test names - ---- - -## Test Coverage Guidelines - -**Definition:** Aim for meaningful coverage of critical paths, not just high percentages. Focus on business logic, edge cases, and failure modes. - -### What to Test - -**High Priority:** -- Business logic and algorithms -- Input validation and error handling -- State transitions -- Integration points -- Security-critical code - -**Medium Priority:** -- Data transformations -- Configuration handling -- User-facing features - -**Low Priority:** -- Trivial getters/setters -- Framework-generated code -- External library wrappers - -### Coverage Anti-Patterns - -```python -# Bad - Testing for coverage, not correctness -def test_add(): - add(2, 3) # No assertion! - -# Good - Test actual behavior -def test_add_returns_sum(): - result = add(2, 3) - assert result == 5 -``` - -### Do -- Focus on critical code paths -- Test public interfaces, not private methods -- Use code coverage as a guide, not a goal -- Write tests that catch real bugs - -### Don't -- Aim for 100% coverage blindly -- Test trivial code just for metrics -- Ignore untested critical paths - ---- - -## Test Pyramid - -**Definition:** Balance different types of tests - many unit tests, fewer integration tests, even fewer end-to-end tests. - -``` - /\ - / \ Few E2E tests (slow, brittle) - /____\ - / \ More integration tests (moderate speed) - /________\ - / \ Many unit tests (fast, isolated) -``` - -### Unit Tests -- Test individual functions/classes in isolation -- Fast execution (milliseconds) -- Mock external dependencies -- High count (hundreds to thousands) - -### Integration Tests -- Test interactions between components -- Moderate speed (seconds) -- Use real dependencies where practical -- Medium count (dozens to hundreds) - -### End-to-End Tests -- Test complete user workflows -- Slow execution (minutes) -- Test through actual UI/API -- Low count (handful to dozens) - -### Do -- Rely primarily on unit tests -- Use integration tests for critical paths -- Reserve E2E tests for key user journeys - -### Don't -- Over-rely on E2E tests -- Skip unit tests in favor of integration tests -- Test everything through the UI - ---- - -## Test Quality Checklist - -Good tests are: - -- **Fast** - Run in milliseconds -- **Isolated** - No shared state or order dependency -- **Repeatable** - Same result every time -- **Self-validating** - Pass/fail is clear -- **Timely** - Written close to code - -### Do -- Use test fixtures for setup -- Clean up resources in teardown -- Use meaningful test data -- Avoid test interdependence - -### Don't -- Rely on external services without mocks -- Use production data -- Write flaky tests -- Commit commented-out tests - ---- - -## Mocking & Test Doubles - -**Definition:** Use test doubles (mocks, stubs, fakes) to isolate the code under test. - -### Types of Test Doubles - -**Stub** - Returns canned responses -```python -class StubPaymentGateway: - def charge(self, amount): - return {"status": "success", "transaction_id": "123"} -``` - -**Mock** - Verifies interactions -```python -def test_order_charges_payment(): - mock_gateway = Mock() - processor = OrderProcessor(mock_gateway) - processor.process(order) - mock_gateway.charge.assert_called_once_with(100.00) -``` - -**Fake** - Simplified working implementation -```python -class FakeDatabase: - def __init__(self): - self.data = {} - - def save(self, key, value): - self.data[key] = value - - def get(self, key): - return self.data.get(key) -``` - -### Do -- Mock external dependencies (APIs, databases, file systems) -- Use dependency injection to enable mocking -- Verify behavior, not implementation -- Keep mocks simple - -### Don't -- Mock everything (test real code when possible) -- Create complex mock hierarchies -- Over-specify mock expectations - ---- - -## Summary - -Effective testing: -- **Guides design** - Tests drive better architecture -- **Prevents regressions** - Catches bugs early -- **Documents behavior** - Tests are living specifications -- **Enables refactoring** - Confidence to improve code - -When writing tests, ask: -- Does this test verify actual behavior? -- Will this test catch real bugs? -- Is this test easy to understand and maintain? -- Can this test run quickly and reliably? diff --git a/skills/designer/SKILL.md b/skills/designer/SKILL.md index caf30c8..bb9d156 100644 --- a/skills/designer/SKILL.md +++ b/skills/designer/SKILL.md @@ -2,45 +2,14 @@ name: designer category: domain description: >- - Evidence-based design decision engine. Intention gate that produces non-slop - UI/UX by forcing every visual choice through industry context, cognitive science, - design master principles, and anti-pattern detection before code generation. - Outputs DESIGN.md contract all subsequent implementation must follow. -metadata: - author: booleanstack - version: "3.0.0" - labels: [design, ui, ux, design-system, anti-slop] -triggers: - - design a - - build me a - - landing page - - dashboard design - - make it look - - visual direction - - ui design - - design system - - DESIGN.md - - create a page - - website for - - app design - - redesign - - style guide -activation: - mode: fuzzy - priority: high -references: - - references/design-md-template.md - - references/website-experience-cheatsheet.md - - examples/saas-dashboard.md - - examples/developer-tool.md - - examples/ecommerce-checkout.md + Evidence-based decision engine. Forces visual choices through industry context, cognitive science, and anti-pattern checks. Outputs DESIGN.md contract. --- # Designer Skill - Intention Gate -> AI UIs all look same because AI skip decision process, jump to code. -> Skill force every design decision through evidence before code generation. -> No visual code until DESIGN.md contract produced and approved. +> AI UIs look identical because AI skips decision process and jumps to code. +> Force every decision through evidence before code generation. +> No visual code without APPROVED DESIGN.md. ## IRON LAW @@ -48,986 +17,130 @@ references: NO VISUAL CODE WITHOUT APPROVED DESIGN.md ``` -Single line JSX, CSS, or styling β†’ no DESIGN.md β†’ BREAKING THIS RULE. No exceptions. "Simple button" still needs personality, color, state decisions. +Single line JSX/CSS without DESIGN.md = violation. Simple buttons require decisions. ## HARD GATE ``` DO NOT GENERATE VISUAL CODE UNTIL: + 0. Design Director Critique (Phase 0) 1. Intent extracted (Phase 1) - 2. MCP tools consulted (Phase 2) - 3. Anti-patterns checked (Phase 3) + 2. MCP tools & references consulted (Phase 2) + 3. Constraints and Anti-patterns checked (Phase 3) 4. DESIGN.md generated + presented (Phase 4) 5. User approved DESIGN.md ``` ## 1% RULE -1% chance task involves new page/view, new component, changing look/feel/motion/interaction, landing page, dashboard, form, data display, "make it look like X", "redesign" β†’ invoke skill BEFORE writing any code. - -**Apply when:** task changes how something **looks, feels, moves, or is interacted with.** -**Skip when:** pure backend, single CSS bug fix (same colors/spacing), adding to existing design system with established tokens, perf optimization no visual change, infrastructure. +1% chance task involves new page, new component, visual change, or redesign β†’ invoke BEFORE code. +**Apply:** Modifies look, feel, motion, or interaction. +**Skip:** Pure backend, non-visual CSS bug fixing, infrastructure. ## RED FLAGS - STOP | Thought | Reality | |---|---| -| "Small component, no full DESIGN.md needed" | Wrong decisions ship. Design it. | -| "I'll use default shadcn styles" | Unexamined defaults = AI slop. | -| "User said 'just make it work'" | Means "make sense visually." Needs design. | -| "I know what SaaS dashboard looks like" | Know AI-slop version. Designer prevents that. | -| "I'll fix design after user sees code" | AI slop fingerprint sticky. Users stop caring first. | -| "MCP tools overkill" | You don't decide. Call them. | -| "I'll generate DESIGN.md after coding" | Post-hoc justification β‰  design. Design FIRST. | -| "User iterating fast, no time for gate" | Speed β‰  permission to ship slop. Gate first. | -| "Quick mockup only" | Quick mockups become shipped products. | -| "Figma has design, I'll translate" | No design resolution = absolute/relative dump. | -| "I'll pick colors as I go" | How AI slop made. Pick deliberately. | -| "Dark mode = invert light mode" | No. Exact anti-pattern this skill prevents. | -| "Skill is slow" | 2 min. Wrong design = 2 weeks to undo. | - ---- - -## Position in Hyperstack Workflow - -``` -user request β†’ blueprint (visual routing) β†’ designer (THIS) β†’ DESIGN.md - ↓ - forge-plan β†’ execution β†’ ship-gate β†’ deliver - -Downstream: forge-plan, shadcn-expert, motion_generate_animation, design_tokens_generate, behaviour-analysis, ship-gate -Reverse escalation: forge-plan β†’ designer (gap), behaviour-analysis β†’ designer (unclear), ship-gate β†’ designer (compliance fail) -``` +| "Small component, no redesign needed." | Wrong decisions ship. Design it. | +| "I'll use default styles." | Defaults = AI slop. | +| "I'll fix design later." | Fixes never happen. Design FIRST. | +| "MCP tools overkill." | You don't decide. Call them. | +| "Speed required, skip gate." | Speed β‰  permission to ship slop. | -## Three-Layer Stack +## Knowledge Base (References & MCP) -| Layer | Plugin | Question | Tools | -|---|---|---|---| -| Decision | `designer` (this) | Which design? | 17 MCP tools | -| Rules | `ui-ux` | What principles? | 6 MCP tools | -| Values | `design-tokens` | What exact CSS? | 7 MCP tools | -| Components | `shadcn` | Which components? | 4 MCP tools | -| Motion | `motion` | Exact animation code? | 7 MCP tools | +The data you need to make decisions is external. **Do not hallucinate it.** +- **MCP Tools:** `designer_resolve_intent`, `designer_get_personality`, `designer_get_industry_rules`, etc. +- **Local References:** If MCP is unavailable, use `view_file` to read the files in `skills/designer/references/`: + - `personality-atlas.md` + - `cognitive-laws-cheatsheet.md` + - `industry-matrix.md` + - `anti-patterns-checklist.md` + - `website-experience-cheatsheet.md` --- -## Website Experience Non-Negotiables - -Every DESIGN.md must resolve these 7: - -1. **Primary path** - user's main JTBD + single primary action -2. **Information scent** - "Where am I, what can I do, what happens next?" -3. **State coverage** - loading, empty, error, success, disabled, destructive -4. **Form/auth friction** - labels persistent, validation humane, paste allowed, password managers supported -5. **Performance budget** - LCP, INP, CLS, payload-sensitive media targets -6. **Accessibility floor** - focus visibility, focus not obscured, target size, reduced motion, keyboard usage -7. **Responsive content priority** - what survives first on mobile, what deferred - -Use [website-experience-cheatsheet](references/website-experience-cheatsheet.md). +# PHASE 0: DESIGN DIRECTOR CRITIQUE -## User Preferences Override Defaults +Before running any tool or compiling a configuration, **you must emit a `` thought block.** You are not a coder here; you are an opinionated Creative Director protecting the user from derivative AI sludge. -Priority order: -1. Explicit user preferences + constraints -2. Existing workspace reality (framework, component lib, design system, tokens, frontend patterns) -3. Approved product/brand requirements -4. Designer auto-resolved defaults +Inside ``, you must answer: +1. **Defend against Slop:** What is the laziest, most generic "Tailwind template" way this request could be built? Explicitly declare how you will avoid it. +2. **Define the Metaphor:** Establish a real-world physical metaphor. (e.g., "This isn't a spreadsheet; it's a pilot's HUD.") +3. **Anchor Constraints:** Pick exactly 1 Cognitive Law and 1 Design Master principle that will strictly govern this layout. -User says "use these colors", "keep current design system", "match this app shell", "no shadcn" β†’ preference wins. +*Only after emitting this critique may you proceed to Phase 1.* --- # PHASE 1: INTENT EXTRACTION -Two modes. Default **Base** unless user says "advanced" or "detailed." - -## Base Mode (3 Questions + Confirm) - -**Step 0:** Existing project β†’ inspect workspace: framework, package manifests, component lib, token system, core frontend files, explicit visual prefs in repo. - -**Step 1:** Call `designer_resolve_intent(product_description)`. Auto-detects: industry, personality, style, mode, density, color mood, must-haves, never-uses. - -**Step 2:** Ask 3 essential questions: - -| # | Question | Why | -|---|---|---| -| 1 | What is product? (1 sentence) | Everything derives from this | -| 2 | Brand color? (hex, name, or "generate") | Can't guess brand | -| 3 | What sections/pages to build? | What to implement | - -**Step 3:** Present auto-resolved defaults as suggestions. Ask if user prefs or workspace patterns override. Offer: *"Say 'advanced' for full control, or pick preset to start."* - -## Presets (Fast Start) - -User says "make it feel like Linear" or "start from Stripe" β†’ `designer_get_preset(name)`, use as DESIGN.md foundation, customize brand color only. - -| Preset | Best For | Key Trait | -|---|---|---| -| `linear` | SaaS, productivity | Opacity hierarchy, 8px grid, 150ms | -| `stripe` | Payment, docs, premium SaaS | Weight 300/500, CIELAB contrast | -| `vercel` | Dev tools, technical | -0.04em tracking, zero chromatic bias | -| `apple` | Consumer, mobile-first | 17px body, spring physics, 44pt targets | -| `carbon` | Enterprise, regulated | Zero radius, IBM Plex, WCAG AA | -| `shadcn` | React + Tailwind | OKLCH, opacity borders, brand-agnostic | -| `notion` | Content, editorial | Warm cream bg, serif headings, 65ch prose | -| `supabase` | Dev tools, dark-first | Emerald on black, compact, code-native | -| `figma` | Creative tools, startups | Multi-color, spring animations, vivid | - -Call `designer_list_presets` to show all. Preset fills Sections 1-7 automatically. Customize: brand color, sections/pages, industry do's/don'ts. - -## Advanced Mode (12 Questions) - -Call `designer_resolve_intent` first. Show suggested default per question. Present in batches of 3-4 (Hick's Law). - -**Q1:** Product? (1 sentence) β†’ determines industry, anti-pattern set, style priority - -**Q2:** Primary user? -| User Type | Defaults | -|---|---| -| Developer | Dark default, monospace accents, keyboard-first, compact | -| Consumer | Light default, friendly typography, mobile-first, comfortable | -| Enterprise | Structured, conservative, data-dense, normal density | -| Child | Playful, 48px+ targets, high contrast, claymorphism | -| Creative | Rich motion, bold colors, portfolio-native | -| Healthcare | Calm, AAA, large text, minimal motion | - -**Q3:** Emotional target? -| Target | Visual Direction | -|---|---| -| Trustworthy | Professional palette, serif/clean sans, conservative radius | -| Playful | Vivid colors, 16-24px radius, spring animations | -| Premium | -0.02em+ tracking, generous whitespace, single accent, subtle shadows | -| Energetic | C 0.15+, 32px+ headings, rich motion | -| Calm | Muted palette, warm neutrals, generous lh, minimal motion | -| Technical | Dark default, monospace accents, compact, snappy motion | -| Bold | Max contrast, large type, strong color blocks | -| Editorial | Serif headings, 18px body 1.75lh, warm bg | - -**Q4:** Light or dark default? Product decision, not preference. Developer tools β†’ dark. Marketing β†’ light. Editorial β†’ light. Gaming β†’ dark. - -**Q5:** Brand color? Given: extract hue, derive OKLCH ramp (11 stops). "generate": pick from industry color mood. -| Industry | Color Mood | -|---|---| -| SaaS | Trust blue + single accent | -| Healthcare | Calm blue + health green | -| Fintech | Navy + trust blue + gold | -| Luxury | Black + gold, minimal | -| AI/Tech | Neutral + one distinct (NOT #6366F1) | -| Education | Friendly pastels, warm accents | -| Wellness | Earth tones, sage, soft coral | - -**Q6:** Density? -| Mode | Section Padding | Card Padding | Body | Use | -|---|---|---|---|---| -| Comfortable | 96px | 40px | 18px | Marketing, editorial, consumer | -| Normal | 64px | 28px | 16px | SaaS, dashboards, apps | -| Compact | 48px | 20px | 14px | Data tables, admin, dev tools | - -**Q7:** Style? minimalism / glassmorphism / soft-ui / dark-oled / vibrant-block / claymorphism / aurora-ui. "recommend": resolved from industry + emotional target. - -**Q8:** Font personality? -| Personality | Pairing | Use | -|---|---|---| -| Technical | Geist + Geist Mono | Dev tools, SaaS, dashboards | -| Elegant | Cormorant + Montserrat | Luxury, editorial, premium | -| Friendly | Plus Jakarta Sans + mono | Consumer, education, SaaS | -| System | Inter (or system stack) | Universal | -| Editorial | Playfair Display + Lora | Content, blogs, news | - -**Q9:** Motion level? -| Level | Includes | -|---|---| -| Static | No animations | -| Subtle | Hover states + transitions (150-200ms) | -| Moderate | + scroll reveals, micro-interactions (200-300ms) | -| Rich | + parallax, page transitions, animated bg (300-500ms) | - -Always respects `prefers-reduced-motion`. - -**Q10:** Sections/pages? Landing: Hero, Features, Testimonials, CTA, Footer, Pricing, FAQ. Dashboard: Sidebar, Header, Content, Data panels. - -**Q11: Framework + Component Library (TWO sub-questions):** - -Q11a Framework: React + Tailwind v4 / Next.js + Tailwind v4 / Vue + Tailwind / Svelte + Tailwind / HTML + Tailwind / Other - -Q11b Component Library: -- **shadcn/ui (Base UI)** β†’ invokes `hyperstack:shadcn-expert`, uses `shadcn_*` MCP tools -- **Raw Tailwind** β†’ hand-built from DESIGN.md, no lib -- **MUI / Mantine / Chakra / Ant Design** β†’ use library's own docs (no hyperstack plugin) -- **Custom / existing** β†’ read user's components, match patterns -- **Ask me to recommend** β†’ recommend shadcn/ui for React+Tailwind, or raw Tailwind for max control - -**DO NOT assume shadcn by default.** Ask explicitly. Libraries have incompatible architectures. - -Routing: `shadcn/ui` β†’ `hyperstack:shadcn-expert` | `Raw Tailwind` β†’ forge-plan hand-writes | `Other` β†’ library's own docs, flag to user | `Custom` β†’ read existing first - -**Q12:** Constraints? WCAG AA (default) or AAA. Performance budget (< 150KB JS, < 2s load). Dark mode required. Brand keywords. - -**Do NOT proceed to Phase 2 until Q1, Q5, Q10 answered.** +**Step 1:** Use `designer_resolve_intent` (or read `references/questions.md`). +**Step 2:** Ensure you know the Product, Brand Color, Target Audience, Emotional Target, and Component Library (e.g., shadcn vs raw Tailwind). --- # PHASE 2: DESIGN SYSTEM RESOLUTION -Every MCP call fills specific DESIGN.md section. No call without purpose. - -## Core Calls (Every Design Task - 4 calls, run in parallel) - -### Call 1: `designer_resolve_intent(product_description)` -**FILLS:** All sections (defaults) -**PURPOSE:** Auto-detects industry, personality, style, mode, density, color mood, must-haves, never-uses. -**USE:** Set defaults for entire DESIGN.md. Present to user in Phase 1. - -### Call 2: `designer_get_personality(resolved_cluster)` -**FILLS:** Sections 1, 2, 3, 4, 6, 7 -**PURPOSE:** Concrete visual vocabulary - tracking, radius range, shadow style, motion timing, density, CSS example. Single most important data source. -**USE:** Set every visual property. Personality vocabulary IS design system skeleton. - -### Call 3: `designer_get_page_template(page_type)` -**FILLS:** Sections 5, 9 -**PURPOSE:** Section anatomy with component inventory + cognitive laws for this page type. -**USE:** Define sections to build, components each needs, responsive behavior. - -### Call 4: `designer_get_anti_patterns(industry: resolved_industry)` -**FILLS:** Sections 8, 10 -**PURPOSE:** Specific violations this industry must avoid. -**USE:** Write Do's/Don'ts + anti-pattern checklist. Every "Don't" must come from this list. - -## Context Calls (Only When Product Needs Them) - -NOT routine. Call ONLY when product has these specific features: - -| Product Feature | Call | FILLS | WHY (what decision changes) | -|---|---|---|---| -| Landing page | `designer_get_landing_pattern("hero-section")` | S5 | Conversion stats change hero layout: value prop 3s, CTA above fold, 40-80px bleed | -| Landing page | `designer_get_landing_pattern("section-ordering")` | S5 | Unbounce 41K pages: Heroβ†’Proofβ†’Problemβ†’Featuresβ†’Testimonialsβ†’Pricingβ†’FAQβ†’CTA | -| Landing page | `designer_get_landing_pattern("social-proof")` | S5 | Named metrics (+30-70%) vs logos (+260%) vs badges (+55%) | -| Landing page | `designer_get_landing_pattern("cta-optimization")` | S8 | First-person CTAs +90%, single CTA +266%, "no credit card" +34% | -| Pricing page | `designer_get_landing_pattern("pricing-psychology")` | S5 | Ariely decoy: 3 tiers, highlight middle, expensive first | -| Forms | `designer_get_interaction_pattern("form-design")` | S5 | Validation timing (blur not input), label placement (top not placeholder) | -| Navigation | `designer_get_interaction_pattern("navigation")` | S5 | Hamburger 39% slower on desktop (NNG). Tab bars +58% engagement. | -| Onboarding | `designer_get_interaction_pattern("onboarding")` | S5 | 3-5 checklist items > 8+. Interactive > passive. | -| Data tables | `designer_get_interaction_pattern("skeleton-vs-spinner")` | S6 | Skeleton for known structure, spinner for discrete actions | -| Error handling | `designer_get_ux_writing("error-messages")` | S8 | NNG rubric: what happened + why + how to fix | -| CTAs/buttons | `designer_get_ux_writing("button-labels")` | S8 | "Start my trial" +90% vs "Start your trial" | -| Premium feel | `designer_get_design_system("stripe")` or `("vercel-geist")` | S1 | Stripe weight 300/500, Vercel -0.04em tracking | -| Enterprise | `designer_get_design_system("ibm-carbon")` | S1 | Carbon 12px spacing-04, IBM Plex, a11y-first | - -## Token Calls (Phase 5 only - when generating code) - -Do NOT call during design resolution: -``` -design_tokens_get_category("colors") β†’ OKLCH ramp construction -design_tokens_get_category("typography") β†’ type scale token defs -design_tokens_get_category("spacing") β†’ 4px grid token defs -design_tokens_generate(description) β†’ complete Tailwind v4 CSS -``` +Gather context (in parallel): +1. **Get Personality:** `designer_get_personality` (or read `personality-atlas.md`). Sets core visual vocabulary (tracking, radius, motion). +2. **Get Industry Rules:** `designer_get_industry_rules` (or read `industry-matrix.md`). +3. **Get Page Anatomy:** `designer_get_page_template` (or read `composition-cheatsheet.md`). --- # PHASE 3: CONSTRAINT APPLICATION -Cross-reference every decision against rules below. P1 β†’ P10. Higher = fix first. - -## P1: Accessibility (CRITICAL) - -| Rule | Standard | Avoid | -|---|---|---| -| `contrast-body` | 4.5:1 body (AA); 7:1 AAA | Testing light mode only | -| `contrast-large` | 3:1 for β‰₯18px bold or β‰₯24px | Assuming brand colors pass | -| `contrast-ui` | 3:1 UI components, borders, icons | Low-contrast borders in dark | -| `focus-rings` | 2px ring, 2px offset, primary color, ALL interactive | `outline: none` without replacement | -| `touch-targets` | 44x44px min (WCAG); 48x48px recommended; 8px gap | Targets < 44px mobile | -| `color-not-only` | Color + icon/text for every state | Red border as sole error indicator | -| `reduced-motion` | `prefers-reduced-motion: reduce` with `!important` in `@layer base` | Missing media query | -| `keyboard-nav` | Tab order = visual order; Enter/Space activates; Escape closes | Unreachable interactive elements | -| `skip-links` | `Skip to main content` first body element | No skip link on nav-heavy pages | -| `alt-text` | Descriptive for informational; `alt=""` decorative | `alt="image"` or missing | -| `aria-labels` | `aria-label` on icon-only buttons | Unlabeled icon buttons | -| `heading-hierarchy` | Sequential h1β†’h2β†’h3, no skipping | h1 β†’ h3 | -| `zoom-support` | Works at 400% zoom; never `user-scalable=no` | Disabling pinch-to-zoom | -| `semantic-html` | `