mirror of
https://github.com/nexu-io/open-design.git
synced 2026-10-27 09:51:12 +01:00
Fix Simple Deck discovery form contract (#2602)
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
const discoveryAtomPath = fileURLToPath(
|
||||
new URL('../../../plugins/_official/atoms/discovery-question-form/SKILL.md', import.meta.url),
|
||||
);
|
||||
const simpleDeckManifestPath = fileURLToPath(
|
||||
new URL('../../../plugins/_official/examples/simple-deck/open-design.json', import.meta.url),
|
||||
);
|
||||
|
||||
describe('bundled discovery-question-form atom prompt contract', () => {
|
||||
it('is included in Simple Deck before generation starts', async () => {
|
||||
const manifest = JSON.parse(await readFile(simpleDeckManifestPath, 'utf8')) as {
|
||||
od?: { pipeline?: { stages?: Array<{ id?: string; atoms?: string[] }> } };
|
||||
};
|
||||
const stages = manifest.od?.pipeline?.stages ?? [];
|
||||
|
||||
expect(stages.map((stage) => stage.id)).toEqual(['discovery', 'generate']);
|
||||
expect(stages[0]?.atoms).toContain('discovery-question-form');
|
||||
expect(stages[1]?.atoms).toEqual(['file-write', 'live-artifact']);
|
||||
});
|
||||
|
||||
it('teaches agents to emit the wrapped question-form renderer contract', async () => {
|
||||
const body = await readFile(discoveryAtomPath, 'utf8');
|
||||
|
||||
expect(body).toContain('<question-form id="discovery"');
|
||||
expect(body).toContain('"questions": [');
|
||||
expect(body).toContain('</question-form>');
|
||||
expect(body).toMatch(/Do not emit a bare question object by itself/);
|
||||
});
|
||||
|
||||
it('does not present a bare keyed question JSON object as the canonical emission shape', async () => {
|
||||
const body = await readFile(discoveryAtomPath, 'utf8');
|
||||
const emissionShape = body.slice(
|
||||
body.indexOf('## Emission shape'),
|
||||
body.indexOf('## Question object shape'),
|
||||
);
|
||||
|
||||
expect(emissionShape).not.toMatch(/```jsonc\s*\{\s*"id":/s);
|
||||
expect(body).not.toMatch(/```jsonc[\s\S]*"id": "audience"/);
|
||||
});
|
||||
});
|
||||
@@ -20,19 +20,44 @@ up turn doesn't re-ask).
|
||||
- Brief explicitly invites questions ("ask me anything if unclear").
|
||||
- The discovery skill or pipeline declares a `discovery` stage.
|
||||
|
||||
## Question shape (as the agent emits one)
|
||||
## Emission shape
|
||||
|
||||
```jsonc
|
||||
Emit the form as a `question-form` block whose body is a JSON object with a
|
||||
top-level `questions` array. Do not emit a bare question object by itself; the
|
||||
renderer only recognizes the wrapped form contract.
|
||||
|
||||
```html
|
||||
<question-form id="discovery" title="Quick brief — 30 seconds">
|
||||
{
|
||||
"id": "audience",
|
||||
"label": "Who's the primary audience?",
|
||||
"type": "checkbox", // or 'radio' | 'text' | 'number'
|
||||
"options": ["VC", "Customer", "Internal team"],
|
||||
"maxSelections": 2,
|
||||
"required": true
|
||||
"description": "I'll lock these in before building. Skip what doesn't apply — I'll fill defaults.",
|
||||
"questions": [
|
||||
{
|
||||
"id": "audience",
|
||||
"label": "Who's the primary audience?",
|
||||
"type": "checkbox",
|
||||
"options": ["VC", "Customer", "Internal team"],
|
||||
"maxSelections": 2,
|
||||
"required": true
|
||||
}
|
||||
]
|
||||
}
|
||||
</question-form>
|
||||
```
|
||||
|
||||
## Question object shape
|
||||
|
||||
Each entry in the top-level `questions` array uses:
|
||||
|
||||
- `id`: stable answer key, for example `audience`.
|
||||
- `label`: user-facing question copy.
|
||||
- `type`: one of `radio`, `checkbox`, `select`, `text`, or `textarea`.
|
||||
- `options`: required for choice controls; strings are allowed, or objects with
|
||||
localized `label` and stable `value`.
|
||||
- `maxSelections`: include this for checkbox controls with a limited selection
|
||||
count.
|
||||
- `required`: set to `true` only when the answer is needed before work can
|
||||
continue.
|
||||
|
||||
## Convergence
|
||||
|
||||
The discovery atom completes when every required question has an answer
|
||||
|
||||
@@ -125,6 +125,12 @@
|
||||
},
|
||||
"pipeline": {
|
||||
"stages": [
|
||||
{
|
||||
"id": "discovery",
|
||||
"atoms": [
|
||||
"discovery-question-form"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "generate",
|
||||
"atoms": [
|
||||
|
||||
Reference in New Issue
Block a user