Rendering Phases And Agent Kit For Agentic Generation
Rendering Phases and Agent Kit for Agentic Generation
Written for AI agents. See Log Methodology Note below for details.
Date: February 6, 2026
Status: Draft
Related: Design Logs #34, #50 (phases & headless), #80, #84, #76
Background
Jay Stack agentic generation relies on several existing pieces:
- Rendering phases (Design Logs #34, #50): Slow (build-time), fast (request-time), interactive (client). Contracts declare phase per tag; agents need to know which data is available when.
- Contract discovery and materialization (Design Log #80): We use a single command
jay-stack agent-kit(renamed fromcontracts) that prepares the full agent kit: materializes dynamic contracts, writes contracts-index and plugins-index, and outputs to agent-kit/ only (no separatebuild/materialized-contracts/). Agents read from agent-kit to understand data shapes and bindings. - Actions and params discovery via CLI (Design Log #84):
jay-stack action <plugin>/<action>,jay-stack params <plugin>/<contract>(load params), and plugin.action.yamlfiles let agents discover valid prop/param values.
Today an agent must:
- Know to run
jay-stack agent-kitand read materialized contracts from agent-kit - Know to run
jay-stack params/jay-stack actionfor discovery - Know jay-html syntax, headless script tags,
jay:instances, phase semantics - Have instructions and context scattered (README, plugin docs, etc.)
Problem
We need a single, coherent folder that a coding agent can use to create jay-html files: instructions, contracts index, plugins index, and minimal content (which components and how to use them). Content is input for the agent to generate pages — agent-only, no CLI transform. Metadata (contract shape, phases) lives in the kit; content does not repeat it.
Questions and Answers
Q1: Where should the agent kit live?
Options:
A) src/.agent-kit/ — next to source, dot-prefix for “tooling”
B) agent-kit/ at project root — visible, not mixed with app source
C) build/agent-kit/ — generated; populated by CLI from contracts + plugin manifests
Answer: Option B — agent-kit/ at project root.
Rationale: Visible to agents and humans; not inside src/ so it doesn’t blur app vs instructions. Materialized contracts, contracts index, and plugins index live in agent-kit (single place; no build copy). Can be committed (instructions + content) or partially generated (contracts, plugins index, actions/params refs).
Q2b: Should we materialize plugin.yaml files?
Options:
A) Index only — plugins-index.yaml lists plugin name + path to each plugin (e.g. node_modules/@wix/stores). Agent reads plugin.yaml from that path when needed.
B) Materialize — Copy each plugin’s plugin.yaml into agent-kit/plugins/<name>.yaml so the kit is self-contained; agent never touches node_modules.
C) Index + optional materialize — Index always; --materialize-plugins or similar copies plugin.yaml into agent-kit for offline/read-only use.
Answer: Option A for v1 (index only). Option B or C if we need a fully self-contained kit (e.g. agent has no filesystem access to node_modules).
Rationale: Index is enough for discovery; contract metadata is already in materialized-contracts. Materializing plugin.yaml duplicates data and must be kept in sync. Prefer index-only unless the agent cannot read plugin paths.
Q2: What belongs in the agent kit?
Proposed contents:
| Item | Purpose |
|---|---|
README.md or INSTRUCTIONS.md |
How to generate jay-html: phases, headless usage, CLI commands, file layout |
plugin.yaml (or symlink/copy) |
Optional: project-level plugin manifest or pointer to plugins in use |
| Contracts (materialized) | agent-kit/materialized-contracts/ + contracts-index.yaml; jay-stack agent-kit writes here (no build copy). Agents discover contracts from here. |
| Plugins index | Generated by jay-stack agent-kit; lists plugins (name, path, contracts). Single discovery point for which plugins/contracts exist. |
| Actions / params reference | Optional: output of jay-stack params …, jay-stack action …, or curated list in references/ |
| Content as Markdown | Minimal: which components (plugin/contract) and how to use them. All other metadata (contract shape, phases) lives in the kit; do not repeat here. |
Q3: How do we indicate headless components (including nested) with their properties?
We need to support nested components with specific props, e.g. “3 product cards with specific product IDs” — not only “one list key + forEach”. The content format must express:
- Which components (plugin + contract) to use.
- How to use them: page-level (key), or nested with static props (e.g. three product cards:
productId: id1, id2, id3), or dynamic (forEach + trackBy + props from context).
Options:
A) Frontmatter (page-level) + minimal spec for nested — e.g. frontmatter: headless: [{ plugin, contract, key }]. Nested: list of { plugin, contract, props?: { productId: "id1" } | { forEach, trackBy, props } } so we can say “3 product cards, productId = x, y, z” or “product cards from list”.
B) Single minimal spec file per page — One YAML with page: { headless: [...] } and nested: [{ contract (or plugin/contract), props | forEach/trackBy/props }]. No duplication of contract metadata; just component ref + usage.
C) Block directives in markdown — e.g. ::: wix-stores/product-card productId="id1" for each instance; or one directive with forEach and props.
Answer: Minimal information only: component ref (plugin/contract) + how to use (key for page-level; for nested: either fixed props per instance or forEach + trackBy + props). All metadata (contract shape, phases, valid prop names) lives in the kit (contracts, plugins index); content does not repeat it. Prefer a single minimal spec (frontmatter for page-level, small YAML or inline for nested) so the agent has one clear place to read “what components + how”.
Rationale: Nested with specific IDs (e.g. 3 product cards) is a core case; the format must support both “list from data” (forEach) and “fixed set of instances with props”. Keeping content minimal avoids drift from contract metadata and keeps the kit as the single source of truth.
Q4: Who transforms content → jay-html?
Answer: Agent only. The content (markdown + minimal spec) is input to help the agent generate the pages. The agent reads the kit (instructions, contracts index, plugins index, materialized contracts) and the minimal content (which components, how to use them), then produces jay-html. No CLI transform step: content is not “compiled” into jay-html by the stack; it is guidance for the agent.
Q5: Should we specify params in the contract (like props) and generate the Params interface?
Background: Components that use URL/load params (e.g. product page with [slug]) today declare the params type manually, e.g. in product-page.ts:
export interface ProductPageParams extends UrlParams {
slug: string;
}
The contract file (product-page.jay-contract) currently has only tags (ViewState); it has no declaration of params. Design Log #84 describes load params discovery via jay-stack params <plugin>/<contract> and withLoadParams.
Proposal: Specify params in the contract (similar to props), and generate the Params interface from the contract so the contract is the single source of truth. Example:
Contract (e.g. product-page.jay-contract):
name: product-page
params:
slug: string # type is ignored; URL params are always string (UrlParams = Record<string, string>)
tags:
- { tag: _id, type: data, dataType: string }
# ...
Generated (e.g. from contract compiler): Params are always string in the generated type.
export interface ProductPageParams extends UrlParams {
slug: string;
}
Answer: Yes. Add params to the contract format and generate ProductPageParams extends UrlParams { ... } in the same place we generate ViewState/Refs (contract compiler). The agent kit then exposes contracts that include params, so the agent knows which URL/load params a page expects. This is an addition to Phase 1 (contract format + codegen; agent kit materialization already emits contracts, so once contracts include params, the kit automatically exposes them).
Rationale: Single source of truth; component code (e.g. product-page.ts) can use the generated ProductPageParams instead of defining it manually; agents see params in the materialized contract. Reference: wix/packages/wix-stores/lib/contracts/product-page.jay-contract and wix/packages/wix-stores/lib/components/product-page.ts.
Design
Agent kit folder layout
agent-kit/
├── INSTRUCTIONS.md # How to generate jay-html (phases, headless, CLI, layout)
├── materialized-contracts/ # Output of jay-stack agent-kit (no build copy)
│ ├── contracts-index.yaml # Index of all contracts (static refs + dynamic materialized)
│ ├── plugins-index.yaml # Index of plugins (generated with contracts command)
│ └── <plugin>/ # Per-plugin materialized dynamic contracts
│ └── *.jay-contract
├── plugins/ # Optional: materialized plugin.yaml copies (if Q2b = materialize)
│ └── wix-stores.yaml
├── content/ # Minimal: which components + how to use them (agent input)
│ ├── blog/
│ │ ├── index.md
│ │ └── [slug].md
│ └── shop/
│ └── index.md
└── references/ # Optional: jay-stack params/action outputs
└── wix-stores-params.json
- INSTRUCTIONS.md: Summarize rendering phases (slow/fast/interactive), contract tags and phases, headless import +
jay:instances,keyand props,forEach/trackBy. List CLI:jay-stack agent-kit(writes toagent-kit/materialized-contracts/and generates plugins index),jay-stack params <plugin>/<contract>,jay-stack action <plugin>/<action>. Describesrc/pageslayout and that page dir can havepage.jay-html,page.jay-contract,page.conf.yaml(Design Log #50). - materialized-contracts/: Single place for materialized contracts and plugins-index.yaml;
jay-stack agent-kitwrites here (default or--output agent-kit/materialized-contracts). No copy inbuild/. Agents read contracts-index, plugins-index, and contract files from here. - content/: Minimal only. Which components (plugin/contract) and how to use them (key; or nested with props / forEach+trackBy+props). All other metadata is in the kit — do not repeat contract shape, phases, etc. here. Content is input for the agent to generate jay-html.
Content format (minimal)
Principle: Specify only which components (plugin/contract) and how to use them. Contract shapes, phases, and valid prop names come from the kit (contracts-index, materialized contracts, plugins index).
Page-level: YAML frontmatter with route and headless: [{ plugin, contract, key }].
Nested / instances with props: e.g. 3 product cards with specific product IDs — specify component (plugin/contract) and either:
- Fixed instances: list of props per instance, e.g.
[{ contract: product-card, props: { productId: "id1" } }, { productId: "id2" }, { productId: "id3" }], or - From list:
forEach+trackBy+props(e.g.productId: "{_id}").
Example (minimal frontmatter + nested):
# content/shop/featured.md — minimal
---
route: /shop/featured
headless:
- plugin: wix-stores
contract: product-card
nested:
- plugin: wix-stores
contract: product-card
instances:
- { productId: 'prod-1' }
- { productId: 'prod-2' }
- { productId: 'prod-3' }
---
# Optional: prose for context; agent uses components above to generate jay-html.
Alternative for “cards from list”: nested: [{ plugin, contract, forEach: "products.items", trackBy: "_id", props: { productId: "{_id}" } }]. INSTRUCTIONS.md explains the format; the agent reads the kit for contract details and emits jay-html.
Rendering phases in the kit
INSTRUCTIONS.md should state:
- Slow: Build-time; use for static data, SSG params (from
jay-stack params). - Fast: Request-time; use for per-request data.
- Interactive: Client; use for mutable UI state.
Contracts in agent-kit/materialized-contracts/ use phase: slow | fast | fast+interactive (Design Log #50). The agent should bind only tags that exist on the contract and be aware that phase affects when data is available (e.g. no interactive-only tags during slow render).
Discovery flow for the agent
- Read
agent-kit/INSTRUCTIONS.md. - Ensure contracts and plugins index exist: run
jay-stack agent-kit(writes toagent-kit/materialized-contracts/: contracts-index.yaml, plugins-index.yaml, materialized contract files). Read contracts-index and plugins-index from there. - For load params (e.g. SSG routes): run
jay-stack params <plugin>/<contract>; use output when generating pages with dynamic routes. - For prop values (e.g. product IDs): run
jay-stack action <plugin>/<action>or read plugin action docs in the kit. - For each page: read
agent-kit/content/...(minimal: which components + how to use them). Use kit metadata (contracts, phases) to generatesrc/pages/.../page.jay-html(and optionalpage.jay-contract,page.conf.yamlper #50).
Implementation Plan
Phase 1: Agent kit structure, contract format (params), and instructions
- Define
agent-kit/layout and document it (e.g. in design log or README). - Add INSTRUCTIONS.md template: rendering phases, headless usage, CLI commands,
src/pageslayout, reference to #50 (page.conf.yaml when jay-html is missing). - Contract materialization:
jay-stack agent-kitwrites toagent-kit/materialized-contracts/by default (no build copy) and generates plugins-index.yaml in the same folder. Design Log #80 implementation is extended so output directory defaults to or can be set toagent-kit/materialized-contracts/, and the command also emits a plugins index (plugin name, path, list of contracts). - Params in the contract: Extend the contract format to include params (URL/load params), similar to props. Generate
export interface <ComponentName>Params extends UrlParams { ... }from the contract (e.g.ProductPageParams extends UrlParams { slug: string }). Contract compiler emits this in the same generated output as ViewState/Refs. Materialized contracts in the agent kit then include params, so the agent knows which URL params a page expects. Reference:wix-storesproduct-page.jay-contractandproduct-page.ts.
Phase 2: Content format (minimal)
- Specify minimal content format: which components (plugin/contract) and how to use them (key; nested: fixed instances with props or forEach+trackBy+props). No repetition of contract metadata.
- Document in INSTRUCTIONS.md: page-level (route + headless list), nested (instances with props or from list).
- Add example
agent-kit/content/with one page-level and one nested example (e.g. 3 product cards with specific IDs).
Phase 3: Optional tooling
- Default output:
jay-stack agent-kitdefaults toagent-kit/materialized-contracts/(or--output agent-kit/materialized-contracts), writes contracts-index and plugins-index. No separate build copy. - Materialize plugin.yaml (optional): If Q2b option B/C is adopted, add flag or separate step to copy plugin.yaml files into
agent-kit/plugins/.
Examples
Example 1: Agent kit INSTRUCTIONS.md (excerpt)
# Generating Jay-HTML Pages
## Rendering phases
- **Slow**: Build-time (SSG). Data and route params from loadParams.
- **Fast**: Per-request (SSR). Data from fast render.
- **Interactive**: Client. Mutable state.
Contracts list phase per tag. Only use tags in the phase where they are available.
## Headless components
1. Page-level: add `<script type="application/jay-headless" plugin="..." contract="..." key="...">` in head.
2. Nested / multiple instances: use `<jay:contract-name>` with props and optional inline template.
3. Discover contracts and plugins: read contracts-index.yaml and plugins-index.yaml (run `jay-stack agent-kit` first).
4. Discover params: `jay-stack params <plugin>/<contract>`.
5. Discover prop values: `jay-stack action <plugin>/<action>` or plugin actions in references/.
## Page layout
- One directory per route under src/pages/.
- Each page dir: page.jay-html (required for view), optional page.jay-contract, optional page.conf.yaml (used when jay-html is missing; see Design Log #50).
Example 2: Minimal content — page-level
---
route: /blog
headless:
- plugin: my-cms
contract: cms/blog-posts-list
key: blog
---
Agent uses kit (contract shape, phases) to produce src/pages/blog/page.jay-html; no metadata repeated in content.
Example 3: Minimal content — nested with specific props (3 product cards)
---
route: /shop/featured
headless:
- plugin: wix-stores
contract: product-card
nested:
- plugin: wix-stores
contract: product-card
instances:
- { productId: 'prod-1' }
- { productId: 'prod-2' }
- { productId: 'prod-3' }
---
Agent produces page with one headless script (product-card) and three <jay:product-card productId="..."> instances. Contract details (props, phases) come from the kit.
Trade-offs
| Approach | Pros | Cons |
|---|---|---|
| Agent kit at project root | Single place for agent; clear separation from src | One more top-level folder |
| Frontmatter + spec for headless | Page-level simple; nested explicit in spec | Two places to look (md + spec) |
| Block directives in markdown | Single file | Parsing and escaping in markdown |
| Agent-only transform (v1) | No new CLI; flexible | Consistency depends on instructions |
| Optional CLI later | Reproducible builds from content | More to build and maintain |
Verification criteria
- An agent with access only to
agent-kit/and the repo can, after runningjay-stack agent-kit(output inagent-kit/materialized-contracts/, including plugins-index), produce valid jay-html for a given route using the right headless components and bindings. - Instructions clearly describe rendering phases and how they relate to contract tags.
- Content is minimal: component refs (plugin/contract) + how to use (key; nested instances or forEach). No repeated contract metadata.
- Agent-only: Content is input for the agent to generate jay-html; no CLI transform step.
- Discovery path (contracts-index, plugins-index, params/actions via CLI) is documented and usable as in #80 and #84.
Summary
- Rendering phases (#34, #50): Document in the agent kit so agents know slow/fast/interactive and contract phase tags.
- Contract materialization (#80): Materialized contracts and plugins index live in agent-kit only:
agent-kit/materialized-contracts/(no build copy).jay-stack agent-kitwrites contracts-index, plugins-index, and materialized contract files here. - Plugin.yaml materialization (Q2b): Index-only by default; optionally materialize plugin.yaml into
agent-kit/plugins/for self-contained kit. - Nested components with props (Q3): Content format supports page-level (key) and nested — fixed instances with props (e.g. 3 product cards with specific productIds) or forEach+trackBy+props. Minimal: component ref + usage only; metadata in kit.
- Agent-only (Q4): Content is input to help the agent generate pages; no CLI transform step.
- Content format: Minimal — which components (plugin/contract) and how to use them. No repetition of contract shape, phases, or other metadata (all in kit).
- Params in the contract (Q5, Phase 1): Specify params in the contract (like props), e.g.
params: { slug: string }in.jay-contract; generateexport interface ProductPageParams extends UrlParams { slug: string }from the contract. Agent kit then exposes params in materialized contracts; component code (e.g.product-page.ts) uses the generated type. Reference: wix-storesproduct-page.jay-contractandproduct-page.ts. - Agent kit folder:
agent-kit/with INSTRUCTIONS.md, materialized-contracts (contracts-index, plugins-index, contract files including params), optional plugin refs, and content/ as minimal spec so a coding agent can create jay-html consistently.
Implementation Results
Phase 1 (partial): Agent kit command and plugins index
Done:
- Plugins index —
contract-materializer.tsnow writesplugins-index.yamlalongsidecontracts-index.yaml. Format:{ materialized_at, jay_stack_version, plugins: [{ name, path, contracts: [{ name, type, path }] }] }. Path is relative to project root. jay-stack agent-kitcommand — New CLI command; default output isagent-kit/materialized-contracts. Same options ascontracts(--output, --list, --yaml, --plugin, --dynamic-only, --force, --verbose).contractscommand kept for backward compat (defaultbuild/materialized-contracts).- Path fix — Default output is passed as relative path (
agent-kit/materialized-contractsorbuild/materialized-contracts); CLI joins withprojectRootso path is correct.
Files changed:
stack-server-runtime/lib/contract-materializer.ts: AddedPluginsIndexEntry,PluginsIndex; during materialization collect per-plugin (path + contracts) and writeplugins-index.yaml.stack-cli/lib/cli.ts: ExtractedrunMaterialize(projectRoot, options, defaultOutputRelative); addedagent-kitcommand (defaultagent-kit/materialized-contracts);contractscommand usesrunMaterializewith defaultbuild/materialized-contracts.
Params in the contract (Q5) — implemented:
- Params are always string: URL/load params are always strings (
UrlParams = Record<string, string>). Contract format acceptsparams: { slug: string }(type values ignored); generated interface always usesstringfor each param. - Contract format: Optional
paramsas object of param name → type (e.g.params: { slug: string }). Parsed intoContractParam[]withnameonly (no dataType). - Types:
ContractParam { name: string }andContract.params?: ContractParam[]incontract.ts. - Parser:
contract-parser.tsparsesparamsfrom YAML (object form); uses keys only (no type validation). - Codegen:
contract-compiler.tsgeneratesimport { UrlParams } from '@jay-framework/fullstack-component';andexport interface <Name>Params extends UrlParams { <prop>: string; ... }when contract has params. - Tests: Parser tests for params (single, multiple, absent); compiler tests use
expect(await prettify(result.val)).toBe(await prettify(...))with full expected output (no toContain).
INSTRUCTIONS.md template (Phase 1):
- When
jay-stack agent-kitruns (and not with--list), after materializing contracts we call ensureAgentKitInstructions(projectRoot). - If
agent-kit/INSTRUCTIONS.mddoes not exist, we createagent-kit/and write a default INSTRUCTIONS.md (Design Log #85 Example 1): rendering phases, headless components, discovery (contracts-index, plugins-index, params, actions), page layout. We do not overwrite an existing INSTRUCTIONS.md. - stack-cli/lib/cli.ts: Added
AGENT_KIT_INSTRUCTIONS_TEMPLATEconstant andensureAgentKitInstructions(projectRoot); agent-kit command action calls it afterrunMaterializewhen!options.list.
Verification: jay-stack agent-kit creates agent-kit/materialized-contracts/contracts-index.yaml and plugins-index.yaml. With zero plugins, both files have empty arrays. Rebuild stack-server-runtime after changing contract-materializer so CLI picks up the new code.
Phase 1 (continued): Cursor skills (IDE-specific)
Created Cursor skills in .cursor/skills/ for IDE-native discovery. These mirror the agent-kit docs but are auto-discovered by the Cursor IDE agent.
Skills: jay-agent-kit, jay-html-authoring, jay-cli-commands, jay-contracts-and-plugins, jay-dev-server-test (pre-existing).
Phase 1 (continued): Agent kit as generated docs folder
Done:
The jay-stack agent-kit command now generates a self-contained agent-kit/ folder with comprehensive documentation that any AI agent can use to build a jay-stack website.
Generated files:
agent-kit/
├── INSTRUCTIONS.md # Main entry point: overview, workflow, references
├── jay-html-syntax.md # Jay-HTML template syntax + headless patterns
├── routing.md # Directory-based routing
├── contracts-and-plugins.md # Reading contracts, plugin.yaml, indexes
├── cli-commands.md # CLI commands: validate, params, action, dev
├── materialized-contracts/ # Generated contracts + indexes
│ ├── contracts-index.yaml
│ ├── plugins-index.yaml
│ └── <plugin>/...
Implementation:
stack-cli/agent-kit-template/— Folder with actual.mdfiles shipped with the package. Contains:INSTRUCTIONS.md,jay-html-syntax.md,routing.md,contracts-and-plugins.md,cli-commands.md.stack-cli/lib/cli.ts—ensureAgentKitDocs(projectRoot, force?)resolves the template folder viaimport.meta.url(../agent-kit-template/relative todist/index.jsorlib/in dev), reads all.mdfiles, and copies them toagent-kit/. Skips existing files unless--force.stack-cli/package.json— Addedagent-kit-templatetofilesarray for publishing.- Deleted the old
agent-kit-templates.tsstring constants approach — real files are simpler to edit and review.
Documentation coverage:
- INSTRUCTIONS.md — What jay-stack is, rendering phases table, full workflow (discover → read contracts → create pages → validate → test), quick start example, links to all reference docs.
- jay-html-syntax.md — File structure, data binding
{expr}, conditionalsif, loopsforEach/trackBy, refs, headless patterns (key-based + instance-based withjay:prefix), page-level contracts, styling, complete example. - routing.md —
src/pages/structure, static/dynamic routes ([param],[[param]],[...param]), route priority, page files (page.jay-html,page.jay-contract,page.conf.yaml), load params. - contracts-and-plugins.md — plugins-index.yaml format, contracts-index.yaml format, plugin.yaml structure, .jay-contract format (tag types, phases, props, params, linked sub-contracts), step-by-step contract-to-jay-html mapping table.
- cli-commands.md —
jay-stack agent-kit,validate,params,action,devwith examples and output formats.
Type-check: tsc --noEmit passes.
Update: .jay-action metadata in agent-kit (see Design Log #92)
The agent-kit docs and materialized indexes now include .jay-action metadata. plugins-index.yaml lists actions with { name, description, path } — matching the contract pattern where the index is a lightweight discovery layer and the .jay-action file at the path has full input/output schemas. Documentation templates updated: INSTRUCTIONS.md, contracts-and-plugins.md, cli-commands.md.
Log Methodology Note
Note: These design logs are written primarily for AI agents as part of the Design Log methodology and made accessible here for human readers. The language and structure are optimized for machine consumption — expect precise, specification-style prose rather than narrative documentation.