-
Notifications
You must be signed in to change notification settings - Fork 1
Description
Overview
Add the runtime field to Expert schema in @perstack/core to support multi-runtime execution.
Background
The runtime field declares which runtimes an Expert is compatible with (similar to engines in package.json). This is a compatibility declaration, not parallel execution:
runtime = ["cursor", "claude-code"] # Works on both, runs on ONE at a timeThis enables:
- Publishing Experts that work across multiple runtimes
- Users choosing their preferred runtime via
--runtimeoption - Registry filtering by runtime compatibility
See: Multi-Runtime Support Documentation
Implementation
1. Add Runtime Name Type Definition
File: packages/core/src/schemas/runtime-name.ts (new file)
Create a new file to avoid confusion with runtime.ts (which contains RunEvent, RuntimeEvent, etc.):
import { z } from "zod"
export type RuntimeName = "perstack" | "cursor" | "claude-code" | "gemini"
export const runtimeNameSchema = z.enum(["perstack", "cursor", "claude-code", "gemini"])Note:
RuntimeName(execution environment) is semantically different fromRuntimeEvent(infrastructure events). Separate file prevents confusion.
2. Update Expert Schema
File: packages/core/src/schemas/expert.ts
Add runtime field to Expert interface:
export interface Expert {
// ... existing fields ...
/** Target runtimes for this Expert (default: ["perstack"]) */
runtime: RuntimeName[]
}Add to expertSchema:
export const expertSchema = z.object({
// ... existing fields ...
runtime: z
.union([
runtimeNameSchema,
z.array(runtimeNameSchema),
])
.optional()
.default(["perstack"])
.transform((value) => (typeof value === "string" ? [value] : value)),
})3. Update PerstackConfig Expert Schema
File: packages/core/src/schemas/perstack-toml.ts
Add runtime field to PerstackConfigExpert:
export interface PerstackConfigExpert {
// ... existing fields ...
/** Target runtimes (default: ["perstack"]) */
runtime?: RuntimeName | RuntimeName[]
}Update perstackConfigSchema.experts:
z.object({
// ... existing fields ...
runtime: z
.union([
runtimeNameSchema,
z.array(runtimeNameSchema),
])
.optional(),
})4. Export New Types
File: packages/core/src/index.ts
Add exports:
export type { RuntimeName } from "./schemas/runtime-name.js"
export { runtimeNameSchema } from "./schemas/runtime-name.js"Affected Files
| File | Change |
|---|---|
packages/core/src/schemas/runtime-name.ts |
New: RuntimeName type and schema |
packages/core/src/schemas/expert.ts |
Add runtime field, import RuntimeName |
packages/core/src/schemas/perstack-toml.ts |
Add runtime field, import RuntimeName |
packages/core/src/index.ts |
Export new types from runtime-name.ts |
Testing
Add tests in packages/core/src/schemas/expert.test.ts:
- Default runtime value is
["perstack"] - Single string
"cursor"transforms to["cursor"] - Array
["cursor", "claude-code"]is preserved - Invalid runtime name is rejected
Example test:
describe("runtime field", () => {
it("defaults to perstack", () => {
const result = expertSchema.parse({
key: "test",
name: "test",
version: "1.0.0",
instruction: "test",
})
expect(result.runtime).toEqual(["perstack"])
})
it("transforms single string to array", () => {
const result = expertSchema.parse({
key: "test",
name: "test",
version: "1.0.0",
instruction: "test",
runtime: "cursor",
})
expect(result.runtime).toEqual(["cursor"])
})
it("preserves array", () => {
const result = expertSchema.parse({
key: "test",
name: "test",
version: "1.0.0",
instruction: "test",
runtime: ["cursor", "claude-code"],
})
expect(result.runtime).toEqual(["cursor", "claude-code"])
})
it("rejects invalid runtime", () => {
expect(() =>
expertSchema.parse({
key: "test",
name: "test",
version: "1.0.0",
instruction: "test",
runtime: "invalid",
}),
).toThrow()
})
})Documentation
No additional documentation needed (already documented in multi-runtime.mdx).
Acceptance Criteria
-
RuntimeNametype exported from@perstack/core -
runtimeNameSchemaexported from@perstack/core -
Expert.runtimefield defaults to["perstack"] - Single string runtime transforms to array
- Invalid runtime names are rejected
- All tests pass
-
pnpm typecheckpasses
Dependencies
None (this is the first issue in the implementation chain)
Blocked By
None
Blocks
- chore: version packages #2 CLI --runtime option
- Fix: maxSteps off-by-one error #3 RuntimeAdapter interface