Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .claude/skills/adding-skill-program/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,8 @@ Outside the folder, the
[TUI program registry](../../../src/tui/programs/index.ts) imports and spreads
the entry. A program with no entry runs
[AGENT_SKILL_STEPS](../../../src/tui/programs/shared/skill-flow.ts): intro,
health check, auth, run, outro, and keep-skills, with the skill deck. Map
health check, auth, run, and outro, with the skill deck. `runProgram`
removes the skills a run installed when it ends. Map
its steps to swap in your own intro screen, as
[audit's flow](../../../src/tui/programs/audit/flow.ts) does. Auth also applies
the shared [AI opt-in gate](../../../src/tui/ai-opt-in-gate.ts),
Expand Down
10 changes: 5 additions & 5 deletions .claude/skills/exploring-the-wizard/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ own decisions through the same state and action contract.
dismissing it. `integration` says `failed` once the run's phase is `error`;
a host exit can instead surface as a socket error.
6. After successful agent completion, finish the offered outro and follow-up
actions. For the integration flow, `session.skillsComplete` marks the tail's
completion. Other programs can have a terminal outro or exit screen.
actions. `session.flowComplete` turns true once the flow reaches its exit
screen, which ends the run.

| Decision | Action and `params` |
| ---------------------------------------- | -------------------------------------------------------------------------------------------- |
Expand All @@ -103,10 +103,10 @@ own decisions through the same state and action contract.
| Finish outro | `dismiss_outro` |
| Record MCP outcome | `set_mcp_outcome`, `{ outcome: "skipped" }` or `{ outcome: "installed", clients: [...] }` |
| Dismiss suggested prompts / Slack step | `dismiss` / `dismiss_slack` |
| Record keep-skills choice | `keep_skills`, `{ kept: true }` or `{ kept: false }` |

MCP and keep-skills actions commit store state; recording an outcome does not
perform the corresponding installation or cleanup. Report which outcomes were
The MCP action commits store state; recording an outcome does not perform the
installation. `runProgram` removes the skills a run installed when the run
ends, so the app has none left afterwards. Report which outcomes were
simulated. A completed run also needs a review of the app diff and expected
integration behavior before calling the integration valid.

Expand Down
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -719,8 +719,7 @@ Example prompt — explore against
> for any setup question, then `run_agent` at auth.
> 4. Poll `read_state` until `integration` is `done` (or `failed` — then report
> `integrationError`), snapshotting as the run screen progresses.
> 5. Finish the tail: `dismiss_outro`, `set_mcp_outcome`, then
> `keep_skills`.
> 5. Finish the tail: `dismiss_outro`, then `set_mcp_outcome`.
>
> Then show me the saved snapshots in order, the screen path, whether `posthog`
> landed in the app, and anything that broke.
Expand Down
8 changes: 4 additions & 4 deletions e2e-harness/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,10 @@ again to refresh background status. Legal actions come from
`read_state.actions`; the MCP has no `list_actions` or `wait_for_change` tool.

Keep handling overlays while the agent runs. `runPhase=completed` ends the main
work, while `session.skillsComplete` ends the integration flow's follow-up
screens and the host with it. Recording MCP or keep-skills outcomes only commits
store state; it does not perform installation or cleanup. For failures, capture
the error outro before dismissing it: dismissing it ends the run.
work, while `session.flowComplete` marks the flow reaching its exit screen,
which ends the host. Recording an MCP outcome only commits store state; it does
not perform the installation. For failures, capture the error outro before
dismissing it: dismissing it ends the run.

## Current host limitations

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`e2e flow snapshot — ai-observability > walks intro → health → auth → run → outro → skills 1`] = `
exports[`e2e flow snapshot — ai-observability > walks intro → health → auth → run → outro → exit 1`] = `
{
"profile": {
"ask": "first",
"healthCheck": "dismiss",
"mcp": "skip",
"setup": "first",
"skills": "delete",
"slack": "skip",
},
"program": "ai-observability",
Expand All @@ -32,25 +31,17 @@ exports[`e2e flow snapshot — ai-observability > walks intro → health → aut
"action": "dismiss_outro",
"screen": "outro",
},
{
"action": "keep_skills",
"params": {
"kept": false,
},
"screen": "keep-skills",
},
],
}
`;

exports[`e2e flow snapshot — metrics > walks intro → health → auth → run → outro → skills 1`] = `
exports[`e2e flow snapshot — metrics > walks intro → health → auth → run → outro → exit 1`] = `
{
"profile": {
"ask": "first",
"healthCheck": "dismiss",
"mcp": "skip",
"setup": "first",
"skills": "delete",
"slack": "skip",
},
"program": "metrics",
Expand All @@ -75,13 +66,6 @@ exports[`e2e flow snapshot — metrics > walks intro → health → auth → run
"action": "dismiss_outro",
"screen": "outro",
},
{
"action": "keep_skills",
"params": {
"kept": false,
},
"screen": "keep-skills",
},
],
}
`;
Expand All @@ -94,7 +78,6 @@ exports[`e2e flow snapshot — posthog-integration > Next.js (with a setup quest
"mcp": "skip",
"notice": "keep",
"setup": "first",
"skills": "delete",
"slack": "skip",
},
"program": "posthog-integration",
Expand Down Expand Up @@ -134,13 +117,6 @@ exports[`e2e flow snapshot — posthog-integration > Next.js (with a setup quest
},
"screen": "mcp",
},
{
"action": "keep_skills",
"params": {
"kept": false,
},
"screen": "keep-skills",
},
],
}
`;
Expand Down Expand Up @@ -176,13 +152,6 @@ exports[`e2e flow snapshot — posthog-integration > Node (no setup question) wa
},
"screen": "mcp",
},
{
"action": "keep_skills",
"params": {
"kept": false,
},
"screen": "keep-skills",
},
],
}
`;
Expand Down Expand Up @@ -234,7 +203,6 @@ exports[`e2e flow snapshot — self-driving > integration-first (no existing Pos
"integrate": true,
"mcp": "skip",
"setup": "first",
"skills": "delete",
"slack": "skip",
},
"program": "self-driving",
Expand Down Expand Up @@ -293,7 +261,6 @@ exports[`e2e flow snapshot — upload-source-maps > walks a stable path 1`] = `
"healthCheck": "dismiss",
"mcp": "skip",
"setup": "first",
"skills": "delete",
"slack": "skip",
},
"program": "error-tracking-upload-source-maps",
Expand All @@ -318,18 +285,11 @@ exports[`e2e flow snapshot — upload-source-maps > walks a stable path 1`] = `
"action": "dismiss_outro",
"screen": "source-maps-outro",
},
{
"action": "keep_skills",
"params": {
"kept": false,
},
"screen": "keep-skills",
},
],
}
`;

exports[`e2e flow snapshot — warehouse-source > walks intro → auth → run → outro → skills 1`] = `
exports[`e2e flow snapshot — warehouse-source > walks intro → auth → run → outro → exit 1`] = `
{
"profile": {
"ask": "first",
Expand Down Expand Up @@ -383,7 +343,6 @@ exports[`e2e flow snapshot — warehouse-source > walks intro → auth → run
"mcp": "skip",
"notice": "keep",
"setup": "first",
"skills": "delete",
"slack": "skip",
},
"program": "warehouse-source",
Expand All @@ -404,13 +363,6 @@ exports[`e2e flow snapshot — warehouse-source > walks intro → auth → run
"action": "dismiss_outro",
"screen": "outro",
},
{
"action": "keep_skills",
"params": {
"kept": false,
},
"screen": "keep-skills",
},
],
}
`;
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@ exports[`keyboard commit vs control action commit > audit-outro: any key vs dism
{
"action": {
"outroDismissed": true,
"screen": "keep-skills",
"screen": "exit",
},
"keyboard": {
"outroDismissed": true,
"screen": "keep-skills",
"skillsComplete": true,
"screen": "exit",
},
}
`;
Expand All @@ -29,15 +28,6 @@ exports[`keyboard commit vs control action commit > intro: enter on Continue vs
}
`;

exports[`keyboard commit vs control action commit > keep-skills: mount with no skills dir vs keep_skills 1`] = `
{
"action": {
"skillsComplete": true,
},
"keyboard": {},
}
`;

exports[`keyboard commit vs control action commit > manual-auth-code: escape vs dismiss_auth_code 1`] = `
{
"action": {
Expand Down Expand Up @@ -73,7 +63,7 @@ exports[`keyboard commit vs control action commit > mcp: enter on install result
"Claude Code",
],
"mcpOutcome": "installed",
"screen": "keep-skills",
"screen": "exit",
},
"keyboard": {
"mcpComplete": true,
Expand All @@ -82,8 +72,7 @@ exports[`keyboard commit vs control action commit > mcp: enter on install result
"Claude Code",
],
"mcpOutcome": "installed",
"screen": "keep-skills",
"skillsComplete": true,
"screen": "exit",
},
}
`;
Expand Down Expand Up @@ -176,12 +165,11 @@ exports[`keyboard commit vs control action commit > source-maps-outro: any key v
{
"action": {
"outroDismissed": true,
"screen": "keep-skills",
"screen": "exit",
},
"keyboard": {
"outroDismissed": true,
"screen": "keep-skills",
"skillsComplete": true,
"screen": "exit",
},
}
`;
Expand Down
12 changes: 6 additions & 6 deletions e2e-harness/__tests__/e2e-flow-snapshot.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ async function traceFlow(
else store.setRunPhase(RunPhase.Completed);
}

if (decision.done || store.skillsComplete) break;
if (decision.done || store.currentScreen === ScreenId.Exit) break;
}
return trace;
}
Expand Down Expand Up @@ -229,7 +229,7 @@ describe('e2e flow snapshot — upload-source-maps', () => {
describe('e2e flow snapshot — ai-observability', () => {
const profile = profileFor(Program.AiObservability);

it('walks intro → health → auth → run → outro → skills', async () => {
it('walks intro → health → auth → run → outro → exit', async () => {
expect({
program: 'ai-observability',
profile,
Expand All @@ -245,7 +245,7 @@ describe('e2e flow snapshot — ai-observability', () => {
describe('e2e flow snapshot — metrics', () => {
const profile = profileFor(Program.Metrics);

it('walks intro → health → auth → run → outro → skills', async () => {
it('walks intro → health → auth → run → outro → exit', async () => {
expect({
program: 'metrics',
profile,
Expand All @@ -270,13 +270,13 @@ describe('e2e flow snapshot — metrics', () => {
screen: 'metrics-intro',
action: 'confirm_setup',
});
expect(trace.at(-1)?.action).toBe('keep_skills');
expect(trace.at(-1)?.action).toBe('dismiss_outro');
expect(trace.length).toBeLessThan(40);
});
});

describe('e2e flow snapshot — warehouse-source', () => {
it('walks intro → auth → run → outro → skills', async () => {
it('walks intro → auth → run → outro → exit', async () => {
expect({
program: 'warehouse-source',
profile: profileFor(Program.WarehouseSource),
Expand All @@ -298,7 +298,7 @@ describe('e2e flow snapshot — warehouse-source', () => {
screen: 'warehouse-intro',
action: 'confirm_setup',
});
expect(trace.at(-1)?.action).toBe('keep_skills');
expect(trace.at(-1)?.action).toBe('dismiss_outro');
expect(trace.length).toBeLessThan(40);
});
});
3 changes: 1 addition & 2 deletions e2e-harness/__tests__/e2e-profile-ask.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -543,9 +543,8 @@ describe('warehouse-source profile', () => {
expect(warehouse.askAnswers?.length).toBeGreaterThan(0);
});

it('keeps the task notice and deletes installed skills', () => {
it('keeps the task notice', () => {
expect(warehouse.notice).toBe('keep');
expect(warehouse.skills).toBe('delete');
expect(warehouse.mcp).toBe('skip');
});

Expand Down
20 changes: 10 additions & 10 deletions e2e-harness/__tests__/e2e-result.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ describe('buildE2eResult', () => {
newDeps: ['posthog-node'],
envFile: '/app/.env',
screenPath: ['warehouse-intro', 'run'],
skillsComplete: true,
flowComplete: true,
};

function build(recorder = new E2eRunRecorder()) {
Expand Down Expand Up @@ -454,7 +454,7 @@ describe('buildE2eResult', () => {
'reportFile',
'runPhase',
'screenPath',
'skillsComplete',
'flowComplete',
'taskOutcomes',
'tasks',
'unansweredAsks',
Expand All @@ -466,24 +466,24 @@ describe('buildE2eResult', () => {
expect(build()).toMatchObject(base);
});

it('replaces an outro result only when the final skills decision is written', () => {
it('replaces an outro result only when the end of the flow is written', () => {
const directory = fs.mkdtempSync(path.join(os.tmpdir(), 'wizard-result-'));
const file = path.join(directory, 'result.json');
let skillsComplete = false;
let flowComplete = false;
const write = createE2eResultWriter(file, () => ({
...build(),
skillsComplete,
flowComplete,
}));
const written = () => JSON.parse(fs.readFileSync(file, 'utf8'));

try {
write();
expect(written()).toMatchObject({ skillsComplete: false });
skillsComplete = true;
expect(written()).toMatchObject({ flowComplete: false });
flowComplete = true;
write();
expect(written()).toMatchObject({ skillsComplete: false });
expect(written()).toMatchObject({ flowComplete: false });
write(true);
expect(written()).toMatchObject({ skillsComplete: true });
expect(written()).toMatchObject({ flowComplete: true });
} finally {
fs.rmSync(directory, { recursive: true, force: true });
}
Expand Down Expand Up @@ -591,7 +591,7 @@ describe('security rail — no answer value reaches the payload', () => {
newDeps: [],
envFile: null,
screenPath: [],
skillsComplete: true,
flowComplete: true,
},
recorder,
session: { frameworkContext: {}, outroData: null },
Expand Down
Loading
Loading