| name | spec |
| description | Manage a Spec Collection — a single document containing multiple specs, each with a goal, named document references, and JavaScript verification scripts. |
Spec Skill
Manage a Spec Collection (SpecCollectionDoc) that groups multiple specs into a single document. Each spec has a goal, named document references, and verification scripts.
Import
const { createSpecCollection, getSpecCollection } = await workspace.import("skills/spec/index.js");
Types
{
specs: SpecDoc[]
}
{
goal: string,
docs: Record<string, AutomergeUrl>,
requiredDocs: string[],
verifications: Verification[]
}
{ name: string, script: string, documentUrls: Record<string, AutomergeUrl> }
API
createSpecCollection(workspace) (sync)
Creates a new, empty SpecCollectionDoc. Do NOT await — workspace.createDoc() is synchronous.
Returns { handle, url }.
const { createSpecCollection } = await workspace.import("skills/spec/index.js");
const { url } = createSpecCollection(workspace);
getSpecCollection(workspace, url) (async)
Returns a read/write interface for the SpecCollectionDoc at url. Must be awaited.
Collection methods
| Method | Description |
|---|
getSpecs() | Returns a shallow copy of the specs array. |
addSpec(goal) | Adds a new spec with the given goal. Returns a spec handle. |
getSpec(index) | Returns a spec handle for the spec at index. |
removeSpec(index) | Removes the spec at index. |
runAllVerifications(workspace, providedDocs?) | Runs verifications for every spec. providedDocs is Record<string, AutomergeUrl> supplying URLs for required docs. Returns { specIndex, name, passed, error? }[]. |
Spec handle methods
A spec handle is returned by addSpec() or getSpec(). All mutations apply to the collection document.
| Method | Description |
|---|
getGoal() | Returns the spec's goal string. |
setGoal(goal) | Sets the spec's goal. |
getDocs() | Returns a copy of the spec's docs record. |
setDoc(name, url) | Sets a named document reference. |
removeDoc(name) | Removes a named document reference. |
getRequiredDocs() | Returns the requiredDocs array (document names the plan must provide). |
addRequiredDoc(name) | Adds a required document name (no-op if already present). |
removeRequiredDoc(name) | Removes a required document name. |
getVerifications() | Returns verifications as { name, script, documentUrls }[]. |
addVerification(name, script, documentUrls?) | Adds a verification with optional named document URLs. |
removeVerification(name) | Removes the first verification matching name. |
runVerifications(workspace, providedDocs?) | Async. providedDocs is Record<string, AutomergeUrl> supplying URLs for required docs. Evals each script, returns { specIndex, name, passed, error? }[]. |
Required Documents
requiredDocs declares document names that the spec needs but that don't exist yet. A plan executor creates these documents and passes their URLs via the providedDocs argument when running verifications.
const handle = coll.addSpec("ER staffing rules are satisfied");
handle.addRequiredDoc("schedule");
const results = await handle.runVerifications(workspace, { schedule: scheduleUrl });
providedDocs entries are merged with each verification's documentUrls. If both define the same key, documentUrls takes precedence.
Verification Scripts
Each verification is a JavaScript snippet that has access to:
workspace — the workspace object
- Named document URLs — any keys from
documentUrls are injected as variables (e.g. { spec: url1 } makes spec available)
- Provided documents — any keys from
providedDocs passed to runVerifications / runAllVerifications (e.g. { schedule: url2 } makes schedule available)
The script must return true to pass. Any other return value or thrown error counts as a failure.
Verification script pattern
Verification scripts should be short orchestration — merge relevant Datalog documents and check for constraint violations:
const { mergeDatalog } = await workspace.import("skills/datalog/index.js")
const merged = await mergeDatalog(workspace, [spec, schedule, staff])
return merged.checkConflicts('my_constraint_name').length === 0
Examples
Creating a spec collection
const { createSpecCollection, getSpecCollection } = await workspace.import("skills/spec/index.js");
const { createDatalog } = await workspace.import("skills/datalog/index.js");
const { url: collUrl } = createSpecCollection(workspace);
const coll = await getSpecCollection(workspace, collUrl);
const hospitalStaff = createDatalog(workspace, "Hospital Staff");
hospitalStaff.assertFact("staff", ["dr_chen", "doctor", "attending"]);
hospitalStaff.assertFact("staff", ["nurse_kim", "nurse", "senior"]);
const shiftConfig = createDatalog(workspace, "Shift Config");
shiftConfig.assertFact("shift", ["morning"]);
shiftConfig.assertFact("shift", ["afternoon"]);
const erSpec = createDatalog(workspace, "ER Spec");
erSpec.assertFact("dept_shift_hours", ["er", "morning", 8]);
erSpec.assertConstraint("er_no_junior_night", {
body: [
{ pred: "assigned", args: ["P", "er", "night"] },
{ pred: "staff", args: ["P", "_", "junior"] },
],
});
const erHandle = coll.addSpec("ER staffing rules are satisfied");
erHandle.setDoc("spec", erSpec.url);
erHandle.setDoc("staff", hospitalStaff.url);
erHandle.setDoc("shifts", shiftConfig.url);
erHandle.addRequiredDoc("schedule");
erHandle.addVerification("no junior night shifts", `
const { mergeDatalog } = await workspace.import("skills/datalog/index.js")
const merged = await mergeDatalog(workspace, [spec, schedule, staff])
return merged.checkConflicts('er_no_junior_night').length === 0
`, { spec: erSpec.url, staff: hospitalStaff.url });