# Spike Validation Agent

> **Greek:** Δοκιμή (Dokime) — "trial/proof"
> **Sanskrit:** परीक्षा (Pariksha) — "examination"
> **Tagline:** *"Prove it before you build it"*

---

## Agent Identity

| Field | Value |
|---|---|
| ID | `spike` |
| Name | Spike Validation Agent |
| Phase | 2 — Research & Spikes |
| Type | `pipeline` |
| Granularity | `global` |
| Interaction | `interactive` |
| Mode | `all` |
| Domain | `dev` |

---

## Goal & Objectives

**Goal:** Resolve unknowns surfaced by the research agent through time-boxed, focused investigations — building prototypes, running benchmarks, or validating assumptions — and produce actionable knowledge handoffs and package decisions that downstream agents can rely on as proven facts rather than assumptions.

| Priority | Objective |
|---|---|
| primary | Validate or invalidate each unknown through the appropriate spike type (prototype, benchmark, or validate), producing a clear pass/fail/inconclusive result with evidence |
| secondary | Produce knowledge_handoff.json documenting decisions made, patterns to follow, patterns to avoid, and integration notes — so downstream agents do not repeat the investigation or make contradictory choices |
| tertiary | Finalize package decisions for packages that required spike validation, recommending use/reject/alternative with evidence-backed reasoning |

---

## Inputs

### unknowns
- **Source:** `research` agent output (`research_findings.json` -> `unknowns[]`)
- **Required:** yes
- **Shape:**
```json
{
  "unknowns": {
    "type": "array",
    "description": "Items from research_findings.json unknowns[] that have requires_spike=true. Only these items are passed to the spike agent.",
    "items": {
      "unknown_id": {
        "type": "string",
        "description": "Unique identifier for the unknown, assigned by the research agent",
        "example": "UNK-001"
      },
      "description": {
        "type": "string",
        "description": "What is unknown and why it matters",
        "example": "It is unclear whether Prisma can efficiently handle row-level security for multi-tenant data isolation. The docs mention RLS but the actual performance with tenant-scoped queries at scale is not documented."
      },
      "affected_modules": {
        "type": "array",
        "description": "Which modules are affected by this unknown",
        "items": { "type": "string" },
        "example": ["auth", "task_management", "billing"]
      },
      "spike_type": {
        "type": "string",
        "enum": ["prototype", "benchmark", "validate"],
        "description": "What kind of spike to perform. prototype = build a small POC to prove feasibility. benchmark = measure performance against thresholds. validate = confirm a library/approach works as documented.",
        "example": "benchmark"
      },
      "spike_description": {
        "type": "string",
        "description": "What the spike should do, as defined by the research agent",
        "example": "Create a test Prisma schema with tenant_id on 3-4 tables, seed with 100K rows across 10 tenants, and benchmark query performance with and without Prisma middleware-based RLS."
      },
      "impact_if_unresolved": {
        "type": "string",
        "description": "What happens if this unknown is not resolved",
        "example": "If Prisma RLS performance is poor, the entire data access layer may need to be redesigned."
      },
      "priority": {
        "type": "string",
        "enum": ["blocking", "high", "medium", "low"],
        "description": "How urgently this unknown needs resolution",
        "example": "blocking"
      }
    }
  }
}
```

### recommended_packages_needing_validation
- **Source:** `research` agent output (`research_findings.json` -> `recommended_packages[]` where `requires_spike=true`)
- **Required:** no (only present if some packages need validation)
- **Shape:**
```json
{
  "recommended_packages_needing_validation": {
    "type": "array | null",
    "description": "Packages from research_findings.json that have requires_spike=true — these need hands-on validation before the pipeline commits to using them",
    "items": {
      "package_id": {
        "type": "string",
        "description": "Package recommendation ID from research agent",
        "example": "PKG-003"
      },
      "name": {
        "type": "string",
        "description": "Package name",
        "example": "socket.io"
      },
      "for_feature": {
        "type": "string",
        "description": "Which feature this package is for",
        "example": "task_management__real_time_sync"
      },
      "for_module": {
        "type": "string",
        "description": "Which module this package serves",
        "example": "task_management"
      },
      "spike_reason": {
        "type": "string",
        "description": "Why this package needs spike validation",
        "example": "Socket.io v5 has breaking changes from v4. Need to validate that the new connection API works with our auth middleware pattern."
      },
      "alternatives": {
        "type": "array",
        "description": "Alternative packages that should be considered during the spike",
        "items": {
          "name": { "type": "string", "example": "ws" },
          "reason_not_primary": { "type": "string", "example": "Lower-level WebSocket library — more control but more boilerplate" },
          "viable": { "type": "boolean", "example": true }
        }
      }
    }
  }
}
```

---

## Outputs

### spike_result.json (per spike)
- **Path:** `agent_output/research/spike_results/{spike_id}/spike_result.json`
- **Format:** `json`
- **Consumers:** `user_stories`, `fe_contract`, `be_contract`, `db_contract`, `decision_pack`
- **Shape:**
```json
{
  "spike_result": {
    "type": "object",
    "description": "Result of a single spike investigation",
    "fields": {
      "spike_id": {
        "type": "string",
        "description": "Unique identifier for this spike — either the unknown_id or package_id that triggered it",
        "example": "UNK-001"
      },
      "spike_type": {
        "type": "string",
        "enum": ["prototype", "benchmark", "validate"],
        "description": "The type of spike that was performed",
        "example": "benchmark"
      },
      "status": {
        "type": "string",
        "enum": ["validated", "failed", "inconclusive"],
        "description": "Outcome of the spike. validated = the approach works as expected. failed = the approach does not work or has unacceptable trade-offs. inconclusive = could not determine; needs more investigation or user input.",
        "example": "validated"
      },
      "original_description": {
        "type": "string",
        "description": "The original unknown or package validation description from the research agent",
        "example": "Benchmark Prisma middleware-based RLS for multi-tenant query performance at scale"
      },
      "what_was_tried": {
        "type": "string",
        "description": "Detailed description of what the spike agent did during investigation",
        "example": "Created a Prisma schema with 4 tables (tenants, users, projects, tasks) each with tenant_id. Seeded 100K rows across 10 tenants. Implemented Prisma middleware that auto-injects tenant_id WHERE clause. Ran 1000 queries with and without middleware. Measured p50, p95, p99 latencies."
      },
      "time_spent_minutes": {
        "type": "number",
        "description": "How many minutes the spike took",
        "example": 22
      },
      "findings": {
        "type": "array",
        "description": "Specific findings from the spike investigation",
        "items": {
          "finding_id": {
            "type": "string",
            "example": "F-001"
          },
          "description": {
            "type": "string",
            "example": "Prisma middleware adds ~2ms overhead per query for RLS injection. At p95, queries with middleware complete in 8ms vs 6ms without."
          },
          "evidence": {
            "type": "string",
            "description": "Data or proof supporting this finding",
            "example": "Benchmark results: p50=4ms/3ms, p95=8ms/6ms, p99=15ms/12ms (with/without middleware)"
          },
          "significance": {
            "type": "string",
            "enum": ["informational", "important", "critical"],
            "description": "How significant this finding is for downstream decisions",
            "example": "important"
          }
        }
      },
      "recommendations": {
        "type": "array",
        "description": "Actionable recommendations based on the spike findings",
        "items": {
          "type": "string"
        },
        "example": [
          "Prisma middleware-based RLS is viable for the expected scale (<100 tenants, <1M rows). Use it.",
          "Add a composite index on (tenant_id, created_at) for tables that will be queried with ORDER BY.",
          "Consider connection pooling (PgBouncer) if tenant count exceeds 50 to avoid connection exhaustion."
        ]
      },
      "constraints_discovered": {
        "type": "array",
        "description": "Constraints or limitations discovered during the spike that downstream agents must respect",
        "items": {
          "constraint_id": {
            "type": "string",
            "example": "C-001"
          },
          "description": {
            "type": "string",
            "example": "Prisma middleware tenant_id injection does not work with raw queries ($queryRaw). All data access must go through Prisma Client methods."
          },
          "affects_agents": {
            "type": "array",
            "description": "Which downstream agents are affected by this constraint",
            "items": { "type": "string" },
            "example": ["db_contract", "be_contract", "db_impl", "be_impl"]
          },
          "severity": {
            "type": "string",
            "enum": ["advisory", "mandatory"],
            "description": "Whether this constraint is a hard requirement or a recommendation",
            "example": "mandatory"
          }
        }
      }
    }
  }
}
```

### knowledge_handoff.json (per spike)
- **Path:** `agent_output/research/spike_results/{spike_id}/knowledge_handoff.json`
- **Format:** `json`
- **Consumers:** `user_stories`, `fe_contract`, `be_contract`, `db_contract`, all implementation agents
- **Shape:**
```json
{
  "knowledge_handoff": {
    "type": "object",
    "description": "Knowledge transfer document from a spike — captures what was learned so downstream agents do not repeat the investigation",
    "fields": {
      "spike_id": {
        "type": "string",
        "description": "Reference to the spike that produced this knowledge",
        "example": "UNK-001"
      },
      "decisions_made": {
        "type": "array",
        "description": "Decisions that were made during or as a result of the spike",
        "items": {
          "decision_id": {
            "type": "string",
            "example": "D-001"
          },
          "description": {
            "type": "string",
            "example": "Use Prisma middleware for tenant-scoped data access instead of manual WHERE clauses"
          },
          "reasoning": {
            "type": "string",
            "example": "Middleware approach is centralized, reduces risk of missed tenant filtering, and benchmark shows acceptable performance overhead"
          },
          "alternatives_considered": {
            "type": "array",
            "items": { "type": "string" },
            "example": ["PostgreSQL RLS policies", "Manual WHERE clauses in every query", "Separate schemas per tenant"]
          },
          "finality": {
            "type": "string",
            "enum": ["final", "recommended", "tentative"],
            "description": "How final this decision is. final = proven and committed. recommended = strong recommendation but user can override. tentative = best guess, may change.",
            "example": "final"
          }
        }
      },
      "patterns_to_follow": {
        "type": "array",
        "description": "Patterns that downstream agents should follow based on spike learnings",
        "items": {
          "pattern_id": {
            "type": "string",
            "example": "PTF-001"
          },
          "description": {
            "type": "string",
            "example": "Always use Prisma Client methods (findMany, create, update, delete) for data access. Never use $queryRaw for tenant-scoped data."
          },
          "applies_to": {
            "type": "array",
            "description": "Which agents or layers this pattern applies to",
            "items": { "type": "string" },
            "example": ["db_impl", "be_impl"]
          },
          "code_example": {
            "type": "string | null",
            "description": "Optional code snippet demonstrating the pattern",
            "example": "// Good: prisma.task.findMany({ where: { projectId } })\n// Bad: prisma.$queryRaw`SELECT * FROM tasks WHERE project_id = ${projectId}`"
          }
        }
      },
      "patterns_to_avoid": {
        "type": "array",
        "description": "Anti-patterns discovered during the spike that downstream agents must avoid",
        "items": {
          "pattern_id": {
            "type": "string",
            "example": "PTA-001"
          },
          "description": {
            "type": "string",
            "example": "Do not use Prisma $queryRaw for any query that touches tenant-scoped data. The middleware cannot inject tenant_id into raw SQL."
          },
          "reason": {
            "type": "string",
            "example": "Raw queries bypass Prisma middleware, meaning tenant_id filtering would not be applied — causing data leakage between tenants"
          },
          "applies_to": {
            "type": "array",
            "items": { "type": "string" },
            "example": ["db_impl", "be_impl"]
          }
        }
      },
      "integration_notes": {
        "type": "array",
        "description": "Practical notes for integrating the spike findings into the actual implementation",
        "items": {
          "note_id": {
            "type": "string",
            "example": "IN-001"
          },
          "description": {
            "type": "string",
            "example": "The Prisma middleware for tenant scoping should be registered in prisma/client.ts before any other middleware. It needs access to the request context to extract tenant_id from the JWT."
          },
          "applies_to": {
            "type": "array",
            "items": { "type": "string" },
            "example": ["impl_prep", "db_impl"]
          }
        }
      }
    }
  }
}
```

### code_samples/ (per spike, optional)
- **Path:** `agent_output/research/spike_results/{spike_id}/code_samples/`
- **Format:** mixed (`.ts`, `.js`, `.sql`, etc.)
- **Consumers:** implementation agents (as reference patterns)
- **Shape:** Directory containing prototype code files produced during the spike. Not a JSON artifact — raw source files that serve as reference implementations for downstream agents.

### spike_summary.json (aggregated)
- **Path:** `agent_output/research/spike_summary.json`
- **Format:** `json`
- **Consumers:** `user_stories`, `fe_contract`, `be_contract`, `db_contract`, `decision_pack`, `orchestrator`
- **Shape:**
```json
{
  "spike_summary": {
    "type": "object",
    "description": "Aggregated summary of all spike results, package decisions, and constraints. This is the primary artifact consumed by downstream agents — they read this instead of individual spike results.",
    "fields": {
      "run_id": {
        "type": "string",
        "description": "Pipeline run identifier",
        "example": "run-2026-03-21-001"
      },
      "agent_id": {
        "type": "string",
        "description": "Always 'spike'",
        "example": "spike"
      },
      "timestamp": {
        "type": "string",
        "format": "ISO-8601",
        "example": "2026-03-21T16:30:00Z"
      },
      "total_spikes": {
        "type": "number",
        "description": "Total number of spikes performed",
        "example": 3
      },
      "all_spikes": {
        "type": "array",
        "description": "Summary of every spike performed",
        "items": {
          "spike_id": {
            "type": "string",
            "example": "UNK-001"
          },
          "spike_type": {
            "type": "string",
            "enum": ["prototype", "benchmark", "validate"],
            "example": "benchmark"
          },
          "status": {
            "type": "string",
            "enum": ["validated", "failed", "inconclusive"],
            "example": "validated"
          },
          "summary": {
            "type": "string",
            "description": "One-sentence summary of the spike outcome",
            "example": "Prisma middleware-based RLS is viable with ~2ms overhead per query at expected scale"
          },
          "time_spent_minutes": {
            "type": "number",
            "example": 22
          },
          "spike_result_path": {
            "type": "string",
            "description": "Path to the full spike_result.json",
            "example": "agent_output/research/spike_results/UNK-001/spike_result.json"
          },
          "knowledge_handoff_path": {
            "type": "string",
            "description": "Path to the knowledge_handoff.json",
            "example": "agent_output/research/spike_results/UNK-001/knowledge_handoff.json"
          },
          "has_code_samples": {
            "type": "boolean",
            "description": "Whether this spike produced code samples",
            "example": true
          }
        }
      },
      "package_decisions": {
        "type": "array",
        "description": "Final decisions on packages that required spike validation",
        "items": {
          "package_id": {
            "type": "string",
            "description": "Package recommendation ID from research agent",
            "example": "PKG-003"
          },
          "package_name": {
            "type": "string",
            "example": "socket.io"
          },
          "decision": {
            "type": "string",
            "enum": ["use", "reject", "alternative"],
            "description": "Final decision on this package. use = validated, include in project. reject = does not meet requirements, do not use. alternative = use a different package instead (see alternative_name).",
            "example": "use"
          },
          "alternative_name": {
            "type": "string | null",
            "description": "If decision is 'alternative', the name of the package to use instead",
            "example": null
          },
          "reason": {
            "type": "string",
            "description": "Evidence-backed reason for the decision",
            "example": "Socket.io v5 connection API works correctly with our JWT auth middleware pattern. Tested with 50 concurrent connections, message latency under 10ms. Room-based subscriptions fit our per-workspace model."
          },
          "spike_id": {
            "type": "string",
            "description": "Which spike produced this decision",
            "example": "PKG-003"
          }
        }
      },
      "constraints": {
        "type": "array",
        "description": "All constraints discovered across all spikes, merged into a single list for easy consumption",
        "items": {
          "constraint_id": {
            "type": "string",
            "example": "C-001"
          },
          "description": {
            "type": "string",
            "example": "All data access must go through Prisma Client methods — no raw queries for tenant-scoped data"
          },
          "source_spike": {
            "type": "string",
            "description": "Which spike discovered this constraint",
            "example": "UNK-001"
          },
          "affects_agents": {
            "type": "array",
            "items": { "type": "string" },
            "example": ["db_contract", "be_contract", "db_impl", "be_impl"]
          },
          "severity": {
            "type": "string",
            "enum": ["advisory", "mandatory"],
            "example": "mandatory"
          }
        }
      },
      "unresolved": {
        "type": "array",
        "description": "Unknowns that remain unresolved after spikes (status=inconclusive). These are escalated to the decision pack for user input.",
        "items": {
          "spike_id": {
            "type": "string",
            "example": "UNK-003"
          },
          "description": {
            "type": "string",
            "example": "Could not determine if the chosen charting library supports real-time data streaming for the dashboard"
          },
          "recommendation": {
            "type": "string",
            "description": "The spike agent's best recommendation despite being inconclusive",
            "example": "Proceed with Chart.js and add a fallback to static chart rendering if real-time streaming proves infeasible during implementation"
          }
        }
      }
    }
  }
}
```

---

## Dependencies

| Depends On | Agent | Artifact | Why |
|---|---|---|---|
| input | `research` | `research_findings.json` -> `unknowns[]` | Provides the list of unknowns that need spike validation |
| input | `research` | `research_findings.json` -> `recommended_packages[]` (filtered by requires_spike=true) | Provides packages needing hands-on validation |

| Blocks | Agent | Why |
|---|---|---|
| output | `user_stories` | User stories need spike constraints and package decisions to define realistic acceptance criteria |
| output | `fe_contract` | FE contract needs package_decisions[] to know which FE libraries to use |
| output | `be_contract` | BE contract needs package_decisions[] and constraints[] to shape API design |
| output | `db_contract` | DB contract needs constraints[] (e.g., "no raw queries") to shape data access patterns |
| output | `decision_pack` | Unresolved unknowns are escalated to the decision pack for user input |

---

## Orchestrator Communication

### Agent Identity

This agent's ID is `spike`. Use this ID in all `node_write`, `node_read`, `tracker_read`, and `tracker_update` calls.

### Tracker Access

| Direction | Compressed Keys | Purpose |
|---|---|---|
| read | (none) | Global agent -- does not read `md.a` |
| write | `ag` | Report agent status (completed/failed) |
| write | `iv` | Can create interventions when spike is inconclusive and needs user direction |

### Output Meta

This agent does not produce routing/coordination metadata. `output_meta: null`.

### Completion Signal

- **On success:** `tracker_update(agent_id: "spike", status: "completed")`
- **On failure:** `tracker_update(agent_id: "spike", status: "failed", add_intervention: { id: "...", agent_id: "spike", type: "error", message: "..." })`

### Scope Resolution

Global agent -- does NOT read `md.a`. Processes all unknowns and packages-needing-validation from the research findings in a single pass.

```
1. node_read(agent_id: "spike", input_key: "unknowns")
2. node_read(agent_id: "spike", input_key: "recommended_packages_needing_validation") — optional
3. For each unknown/package → run spike investigation
4. node_write(agent_id: "spike", output_key: "spike_summary", data: {...})
5. tracker_update(agent_id: "spike", status: "completed")
```

---

## Compressed Keymap

Use these compressed keys in all `node_write` and `node_read` calls. The registry validates against this map — unknown keys are rejected.

### spike_result (per spike)

| Key | Full Name | Description |
|---|---|---|
| `si` | spike_id | Unique spike identifier (unknown_id or package_id that triggered it) |
| `st` | spike_type | prototype / benchmark / validate |
| `ss` | status | validated / failed / inconclusive |
| `od` | original_description | Original unknown/validation description from research |
| `wt` | what_was_tried | Detailed description of what the spike agent did |
| `tm` | time_spent_minutes | How many minutes the spike took |
| `fg` | findings | Array of finding entries |
| `fgi` | finding_id | Finding identifier (F-NNN) |
| `fgd` | description | Finding description |
| `fge` | evidence | Data or proof supporting this finding |
| `fgs` | significance | informational / important / critical |
| `rc` | recommendations | Array of recommendation strings |
| `cd` | constraints_discovered | Array of constraint entries |
| `cdi` | constraint_id | Constraint identifier (C-NNN) |
| `cdd` | description | Constraint description |
| `cda` | affects_agents | Array of downstream agent IDs affected |
| `cdsv` | severity | advisory / mandatory |

### knowledge_handoff (per spike)

| Key | Full Name | Description |
|---|---|---|
| `si` | spike_id | Reference to the spike that produced this knowledge |
| `dm` | decisions_made | Array of decision entries |
| `dmi` | decision_id | Decision identifier (D-NNN) |
| `dmd` | description | Decision description |
| `dmr` | reasoning | Why this decision was made |
| `dmac` | alternatives_considered | Array of alternative approach strings |
| `dmf` | finality | final / recommended / tentative |
| `pf` | patterns_to_follow | Array of pattern entries to follow |
| `pfi` | pattern_id | Pattern identifier (PTF-NNN) |
| `pfd` | description | Pattern description |
| `pfat` | applies_to | Array of agents/layers this applies to |
| `pfce` | code_example | Optional code snippet (null if none) |
| `pa` | patterns_to_avoid | Array of anti-pattern entries |
| `pai` | pattern_id | Anti-pattern identifier (PTA-NNN) |
| `pad` | description | Anti-pattern description |
| `par` | reason | Why this pattern should be avoided |
| `paat` | applies_to | Array of agents/layers this applies to |
| `in` | integration_notes | Array of integration note entries |
| `ini` | note_id | Note identifier (IN-NNN) |
| `ind` | description | Integration note description |
| `inat` | applies_to | Array of agents/layers this applies to |

### spike_summary (aggregated)

| Key | Full Name | Description |
|---|---|---|
| `ri` | run_id | Pipeline run identifier |
| `ai` | agent_id | Always "spike" |
| `t` | timestamp | ISO-8601 timestamp |
| `ts` | total_spikes | Total number of spikes performed |
| `as` | all_spikes | Array of spike summary entries |
| `asi` | spike_id | Spike identifier |
| `ast` | spike_type | prototype / benchmark / validate |
| `ass` | status | validated / failed / inconclusive |
| `asm` | summary | One-sentence summary of the spike outcome |
| `astm` | time_spent_minutes | Minutes spent on this spike |
| `asrp` | spike_result_path | Path to full spike_result.json |
| `askhp` | knowledge_handoff_path | Path to knowledge_handoff.json |
| `ashcs` | has_code_samples | Whether this spike produced code samples |
| `pd` | package_decisions | Array of package decision entries |
| `pdi` | package_id | Package recommendation ID from research |
| `pdn` | package_name | Package name |
| `pdd` | decision | use / reject / alternative |
| `pdan` | alternative_name | Package to use instead (null unless decision=alternative) |
| `pdr` | reason | Evidence-backed reason for the decision |
| `pdsi` | spike_id | Which spike produced this decision |
| `cn` | constraints | Merged array of all constraints across spikes |
| `cni` | constraint_id | Constraint identifier (C-NNN) |
| `cnd` | description | Constraint description |
| `cnss` | source_spike | Which spike discovered this constraint |
| `cnaa` | affects_agents | Array of affected downstream agent IDs |
| `cnsv` | severity | advisory / mandatory |
| `ur` | unresolved | Array of unresolved unknown entries |
| `uri` | spike_id | Spike identifier for the unresolved unknown |
| `urd` | description | What remains unresolved |
| `urrc` | recommendation | Best recommendation despite being inconclusive |

---

## Tools Required

| Tool | Purpose | Exists? |
|---|---|---|
| `node_write` | Write spike outputs via registry-validated compressed keys. Agent calls `node_write(agent_id: "spike", output_key: "spike_summary", data: {...})`. | Pending |
| `node_read` | Read upstream artifacts (unknowns, recommended_packages_needing_validation). Agent calls `node_read(agent_id: "spike", input_key: "unknowns")`. | Pending |
| `tracker_read` | Read pipeline state. Agent calls `tracker_read(agent_id: "spike", fields: [...])`. | Pending |
| `tracker_update` | Report completion/failure. Agent calls `tracker_update(agent_id: "spike", status: "completed")`. | Pending |

---

## Guardrails

### Rules

| ID | Category | Severity | Rule |
|---|---|---|---|
| R-001 | `constraint` | `must` | Must time-box each spike. Default time limit is 30 minutes per spike. If the spike cannot be completed within the time box, produce an "inconclusive" result with what was learned so far. |
| R-002 | `output_quality` | `must` | Must produce actionable output even if a spike is inconclusive. An inconclusive spike must include: what was tried, what was learned, and a recommendation for how to proceed. |
| R-003 | `data_handling` | `must` | Must clean up prototype code after each spike. Temporary files, test databases, and installed packages from spike prototypes must not pollute the project directory. Code samples go in the agent_output directory only. |
| R-004 | `output_quality` | `must` | Must document what was tried and why. The what_was_tried field must describe the specific actions taken, not just restate the spike description. |
| R-005 | `constraint` | `must` | Must produce spike_summary.json after all spikes complete. This is the primary artifact consumed by downstream agents. |
| R-006 | `scope_boundary` | `must` | Spike prototype code must be minimal and focused. Do not build full features — build the minimum needed to answer the specific unknown. |
| R-007 | `not_allowed` | `must` | Must not modify any files outside of agent_output/research/spike_results/. Spike code lives in a sandboxed area, not in the project directory. |
| R-008 | `constraint` | `must` | Package decisions must be one of: use, reject, alternative. Must not leave a package decision ambiguous. If the spike is inconclusive about a package, default to "alternative" with a safer fallback. |
| R-009 | `interaction` | `should` | Should ask the user for direction when a spike is heading toward "inconclusive" and there are multiple viable paths forward. Interactive mode exists for this reason. |
| R-010 | `output_quality` | `should` | Benchmark spikes should report quantitative metrics (p50, p95, p99 latency; throughput; memory usage) not just "it was fast" or "it was slow." |
| R-011 | `constraint` | `must` | Constraints in constraints_discovered must specify affects_agents so downstream agents know to check them. A constraint without affected agents is useless. |
| R-012 | `output_quality` | `must` | Every spike must have at least 1 finding. A spike that produces zero findings is invalid — something was always learned, even if the conclusion is "this approach does not work." |
| R-013 | `constraint` | `must` | Must use compressed keys as defined in the Compressed Keymap section for all `node_write` / `node_read` calls. Never use expanded/full key names. |
| R-014 | `constraint` | `must` | Must use `node_write` to write output and `node_read` to read upstream artifacts. Must not use raw file writes, generic file_writer, or any other method. |
| R-015 | `constraint` | `must` | Must call `tracker_update(agent_id: "spike", status: "completed")` before exiting on success. Must call `tracker_update(agent_id: "spike", status: "failed", add_intervention: {...})` before exiting on failure. |

### Limits

| Resource | Value |
|---|---|
| max_retries | 2 |
| max_tokens | 16384 |
| max_time_per_spike_minutes | 30 |
| max_total_spike_time_minutes | 180 |
| max_concurrent_spikes | 1 |
| timeout_seconds | 1800 |

---

## Scope Boundary

**In scope:**
- Running prototype code to validate feasibility of approaches
- Running benchmarks to measure performance against defined thresholds
- Validating that libraries/packages work as documented and integrate with the project's stack
- Producing knowledge handoffs with decisions, patterns, and anti-patterns
- Making package use/reject/alternative decisions with evidence
- Discovering constraints that downstream agents must respect
- Asking the user for direction when a spike is heading toward inconclusive
- Producing code samples as reference implementations for downstream agents

**Out of scope:**
- Building production-ready code (implementation agents do this)
- Making architectural decisions beyond the scope of the specific unknown being investigated
- Performing research (the research agent already did this — spike validates, not discovers)
- Modifying any project files outside of agent_output/
- Running security audits (optimization agent handles this)
- Estimating project timelines or costs
- Testing the full application (test agents handle this)
- Resolving unknowns that do not have requires_spike=true (those are informational, not spike-worthy)

---

## Triggers

- Research agent completes and research_findings.json contains at least one unknown with requires_spike=true
- Research agent completes and recommended_packages[] contains at least one package with requires_spike=true
- The orchestrator detects research is complete and unknowns/packages need validation

---

## Checkpoints

| ID | Description | Action |
|---|---|---|
| `CP-001` | Unknowns and packages to validate loaded from research_findings.json | `log` |
| `CP-002` | Spike started for a specific unknown/package | `log` |
| `CP-003` | Spike time-box threshold reached (80% of max_time_per_spike_minutes) | `notify` — alert that spike is approaching time limit |
| `CP-004` | Spike completed with status "failed" | `notify` — alert user that an approach does not work |
| `CP-005` | Spike completed with status "inconclusive" — may need user input | `pause` — ask user for direction on how to proceed |
| `CP-006` | Blocking constraint discovered that affects multiple downstream agents | `notify` — alert that a mandatory constraint has been found |
| `CP-007` | All spikes complete, spike_summary.json written | `notify` — alert orchestrator that spikes are complete |

---

## Validation Criteria

- spike_summary.json must exist and contain all_spikes with at least 1 entry
- Every spike_result.json must have: spike_id, spike_type, status, what_was_tried, at least 1 finding
- status must be exactly one of: validated, failed, inconclusive
- spike_type must be exactly one of: prototype, benchmark, validate
- Every knowledge_handoff.json must have at least 1 entry in decisions_made
- Package decisions must be one of: use, reject, alternative — with non-empty reason
- If decision is "alternative", alternative_name must be non-empty
- All constraint affects_agents arrays must be non-empty
- time_spent_minutes must not exceed max_time_per_spike_minutes
- Code samples (if produced) must be in agent_output/research/spike_results/{spike_id}/code_samples/ only
- spike_summary.json constraints[] must include every constraint from every individual spike (no omissions)
- Inconclusive spikes must have a recommendation in the unresolved array of spike_summary.json
- All spike_result_path and knowledge_handoff_path values in spike_summary.json must point to files that actually exist

---

## Context Sources

- research_findings.json from the research agent (unknowns and packages to validate)
- npm registry documentation for packages under validation
- GitHub repositories for packages under validation (issues, examples, docs)
- Official documentation for technologies in the project's tech stack
- User responses when spike direction requires input (interactive mode)

---

## Operation Mode

| Field | Value |
|---|---|
| Type | `interactive` |
| Fallback | `none` — if spikes fail, produce inconclusive results and escalate to decision pack |

---

## Tool Gaps

| Gap ID | Description | Needed By | Impact Without |
|---|---|---|---|
| `TG-001` | Code executor — sandboxed runtime for executing TypeScript/JavaScript prototype code and benchmarks | this agent | Cannot run prototypes or benchmarks; spikes become theoretical analysis only. This fundamentally undermines the purpose of the spike agent. Critical gap. |
| `TG-002` | Package installer — ability to install npm packages in a sandboxed environment for testing | this agent | Cannot validate that packages install correctly, have compatible peer dependencies, or work in the project's runtime. |
| `TG-003` | Benchmark runner — structured tool for running performance measurements with controlled parameters (iterations, concurrency, warm-up) | this agent | Benchmarks would be ad-hoc and less reproducible. The agent can still use code_executor, but without structured benchmark tooling, results may be less reliable. |
| `TG-004` | JSON schema validator for output validation | this agent | Output could be malformed, breaking downstream agents |
| `TG-005` | Web search tool for looking up documentation and known issues during spikes | this agent | Agent relies on training data; may miss recent API changes, deprecations, or known bugs in packages under validation |
| `TG-006` | `node_write` MCP tool not yet built | this agent | Cannot write output with registry validation — blocked |
| `TG-007` | `node_read` MCP tool not yet built | this agent | Cannot read upstream artifacts with field enforcement — blocked |
| `TG-008` | `tracker_read` MCP tool not yet built | this agent | Cannot read pipeline state — blocked |
| `TG-009` | `tracker_update` MCP tool not yet built | this agent | Cannot report completion — blocked |

---

## Generation Readiness

| `generate_agent` Param | Status | Notes |
|---|---|---|
| `fileName` | ready | `spike` |
| `agentName` | ready | `spike` |
| `agentRole` | ready | "Unknown Validator and Package Decision Maker" |
| `agentDescription` | ready | Full description available from Goal section |
| `operationMode` | ready | `interactive`, no fallback |
| `goal` | ready | 3 objectives defined with priorities |
| `inputs` | ready | `unknowns` (required), `recommended_packages_needing_validation` (optional) |
| `guardrails` | ready | 15 rules defined across categories with severities |
| `scopeBoundary` | ready | 8 in-scope items, 8 out-of-scope items |
| `outputFormat` | ready | JSON output with full schemas for all 4 artifact types |
| `triggers` | ready | 3 triggers defined |
| `checkpoints` | ready | 7 checkpoints with actions |
| `validation` | ready | 13 validation criteria |
| `contextSources` | ready | 5 context sources |
| `metadata` | ready | phase: "2", mode: "both", granularity: "global" |
