151 lines
6.7 KiB
Markdown
151 lines
6.7 KiB
Markdown
# SGrobot Adapter Implementation Plan
|
|
|
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
|
|
**Goal:** Build a reusable generator that turns one rrweb semantic trace into SGrobot-compatible `PlanTemplate.toml` and runtime `SKILL.toml` artifacts without changing SGrobot core.
|
|
|
|
**Architecture:** Keep this repository as a Codex skill plus deterministic generator. The generator writes one business-specific plan and one business-specific sgBrowser runtime skill per recording, with the semantic trace baked into the generated page script and saved beside it as evidence.
|
|
|
|
**Tech Stack:** Node.js ESM, `node:test`, SGrobot `PlanTemplate.toml`, SGrobot runtime `SKILL.toml`, sgBrowser `sgBrowserExcuteJsCodeByDomain`.
|
|
|
|
---
|
|
|
|
### Task 1: Generator Contract
|
|
|
|
**Files:**
|
|
- Create: `tests/generate_sgrobot_flow.test.mjs`
|
|
- Create: `scripts/generate_sgrobot_flow.mjs`
|
|
- Create: `package.json`
|
|
|
|
- [ ] **Step 1: Write the failing tests**
|
|
|
|
```js
|
|
import { test } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { mkdtemp, readFile, rm } from "node:fs/promises";
|
|
import { tmpdir } from "node:os";
|
|
import path from "node:path";
|
|
import { generateSgRobotArtifacts, writeArtifacts } from "../scripts/generate_sgrobot_flow.mjs";
|
|
|
|
const trace = {
|
|
id: "expense-approval",
|
|
name: "费用审批",
|
|
description: "打开审批页,填写意见并点击同意。",
|
|
startUrl: "https://oa.example.test/expense/123",
|
|
steps: [
|
|
{ type: "assertText", text: "费用审批", label: "确认在审批页" },
|
|
{ type: "fill", selector: "textarea[name='comment']", value: "同意", label: "填写审批意见" },
|
|
{ type: "click", selector: "button[data-action='approve']", label: "点击同意", risk: "write" }
|
|
]
|
|
};
|
|
|
|
test("generates SGrobot plan and runtime skill artifacts", () => {
|
|
const result = generateSgRobotArtifacts(trace, { defaultMode: "execute" });
|
|
assert.equal(result.summary.planId, "rrweb-expense-approval");
|
|
assert.equal(result.summary.skillName, "rrweb-expense-approval-execute");
|
|
assert.equal(result.summary.requiresApproval, true);
|
|
assert.equal(result.files.get("plan_templates/rrweb-expense-approval/PlanTemplate.toml").includes('skills = ["rrweb-expense-approval-execute"]'), true);
|
|
assert.equal(result.files.get(".sgclaw-workspace/skills/rrweb-expense-approval-execute/SKILL.toml").includes('command = "sgBrowserExcuteJsCodeByDomain"'), true);
|
|
assert.equal(result.files.get(".sgclaw-workspace/skills/rrweb-expense-approval-execute/SKILL.toml").includes('expected_domain = "oa.example.test"'), true);
|
|
assert.equal(result.files.get(".sgclaw-workspace/skills/rrweb-expense-approval-execute/scripts/execute_trace.js").includes('"default_mode": "execute"'), true);
|
|
});
|
|
|
|
test("writes generated artifacts under a SGrobot repository root", async () => {
|
|
const root = await mkdtemp(path.join(tmpdir(), "rrweb-sgrobot-"));
|
|
try {
|
|
const result = generateSgRobotArtifacts(trace);
|
|
await writeArtifacts(root, result.files);
|
|
const plan = await readFile(path.join(root, "plan_templates/rrweb-expense-approval/PlanTemplate.toml"), "utf8");
|
|
const skill = await readFile(path.join(root, ".sgclaw-workspace/skills/rrweb-expense-approval-execute/SKILL.toml"), "utf8");
|
|
const evidence = await readFile(path.join(root, ".sgclaw-workspace/skills/rrweb-expense-approval-execute/trace.semantic.json"), "utf8");
|
|
assert.match(plan, /skills = \["browser\.open_url"\]/);
|
|
assert.match(skill, /script_path = "scripts\/execute_trace.js"/);
|
|
assert.equal(JSON.parse(evidence).id, "expense-approval");
|
|
} finally {
|
|
await rm(root, { recursive: true, force: true });
|
|
}
|
|
});
|
|
```
|
|
|
|
- [ ] **Step 2: Run test to verify it fails**
|
|
|
|
Run: `node --test tests/generate_sgrobot_flow.test.mjs`
|
|
|
|
Expected: FAIL with `Cannot find module '../scripts/generate_sgrobot_flow.mjs'`.
|
|
|
|
- [ ] **Step 3: Implement the generator**
|
|
|
|
Create `scripts/generate_sgrobot_flow.mjs` exporting `generateSgRobotArtifacts(trace, options)` and `writeArtifacts(root, files)`. The generator must validate `id`, `name`, `startUrl`, and `steps`, derive `expected_domain` from `startUrl`, emit SGrobot paths, and support `defaultMode` of `dry-run` or `execute`.
|
|
|
|
- [ ] **Step 4: Add package metadata**
|
|
|
|
Create `package.json` with:
|
|
|
|
```json
|
|
{
|
|
"name": "rrweb-sgrobot-skill",
|
|
"version": "0.1.0",
|
|
"type": "module",
|
|
"private": true,
|
|
"scripts": {
|
|
"test": "node --test tests/*.test.mjs",
|
|
"generate:sgrobot": "node scripts/generate_sgrobot_flow.mjs"
|
|
}
|
|
}
|
|
```
|
|
|
|
- [ ] **Step 5: Run test to verify it passes**
|
|
|
|
Run: `npm test`
|
|
|
|
Expected: PASS with both generator tests green.
|
|
|
|
### Task 2: Codex Skill Wrapper
|
|
|
|
**Files:**
|
|
- Create: `SKILL.md`
|
|
- Create: `references/sgrobot-adapter.md`
|
|
- Create: `examples/basic-trace.json`
|
|
|
|
- [ ] **Step 1: Write the skill instructions**
|
|
|
|
Create a concise `SKILL.md` that tells Codex to use `scripts/generate_sgrobot_flow.mjs` when adapting rrweb semantic traces for SGrobot. It must state that `.agents/skills/**/SKILL.md` is only an agent helper, while SGrobot runtime artifacts are `PlanTemplate.toml` and `.sgclaw-workspace/skills/**/SKILL.toml`.
|
|
|
|
- [ ] **Step 2: Add focused SGrobot mapping reference**
|
|
|
|
Create `references/sgrobot-adapter.md` with the SGrobot-specific mapping rules: one generated plan per recording, one generated runtime skill per recording, `browser.open_url` first, generated sgBrowser execution skill second, optional `json.save_result` third, and step-level approval when execute mode includes mutating actions.
|
|
|
|
- [ ] **Step 3: Add a realistic example trace**
|
|
|
|
Create `examples/basic-trace.json` using the same schema covered by the test: `id`, `name`, `description`, `startUrl`, and `steps`.
|
|
|
|
- [ ] **Step 4: Validate the skill folder**
|
|
|
|
Run: `python3 /Users/zhaoyilun/.codex/skills/.system/skill-creator/scripts/quick_validate.py /Users/zhaoyilun/Documents/rrweb-skill`
|
|
|
|
Expected: PASS.
|
|
|
|
### Task 3: Verification Against SGrobot Shape
|
|
|
|
**Files:**
|
|
- Read-only check: `/Users/zhaoyilun/projects/sgrobot-architecture-design/docs/concepts/skill-and-plan-template.md`
|
|
- Read-only check: `/Users/zhaoyilun/projects/sgrobot-architecture-design/docs/how-to/add-skill.md`
|
|
|
|
- [ ] **Step 1: Generate sample output to a temp directory**
|
|
|
|
Run: `node scripts/generate_sgrobot_flow.mjs --trace examples/basic-trace.json --out /tmp/rrweb-sgrobot-sample --default-mode dry-run --force`
|
|
|
|
Expected: the command prints paths for the plan, skill, runtime script, and trace evidence.
|
|
|
|
- [ ] **Step 2: Inspect generated files**
|
|
|
|
Run: `find /tmp/rrweb-sgrobot-sample -type f | sort`
|
|
|
|
Expected: files exist under `plan_templates/rrweb-expense-approval/` and `.sgclaw-workspace/skills/rrweb-expense-approval-execute/`.
|
|
|
|
- [ ] **Step 3: Re-run tests**
|
|
|
|
Run: `npm test`
|
|
|
|
Expected: PASS.
|