Files
OneOS1.2/docs/superpowers/plans/2026-07-25-autorodo-web.md

23 KiB
Raw Permalink Blame History

AutoRDO Web 工作台 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: 落地 Axhub 原型 autorodo-web:粘贴素材 → 复制 AutoRDO 清洗提示词 → 粘回初稿 → 按需求分页确认待确认并回填描述 → 填写优先级/标签/提交部门/提交人 → 一键复制 YunxiaoPMapp 多条记录需求提示词。

Architecture: 纯函数放在原型内 lib/(解析初稿、出题回填、拼提示词、识别部门/提交人UI 为三步工作台React + OneOS V2 Token。MVP 不连云效、不嵌 OCR/ASRCursor 往返靠剪贴板。预留 agentAdapter 接口形状供组织 Agent。

Tech Stack: React + TypeScriptAxhub Make 原型、Vitest、OneOS V2oneos-ds-tokens.css + UIComponents 按需)、PrototypeAnnotationHost

Spec: docs/superpowers/specs/2026-07-25-autorodo-web-design.md


文件结构(锁定)

路径 职责
src/prototypes/autorodo-web/lib/types.ts 需求稿、待确认点、元数据、步骤状态类型
src/prototypes/autorodo-web/lib/parseDraft.ts 解析粘回的 AutoRDO Markdown → RequirementDraft[]
src/prototypes/autorodo-web/lib/parseDraft.test.ts 解析单测
src/prototypes/autorodo-web/lib/applyAnswer.ts 选择题/补充 → 回填描述、消待确认
src/prototypes/autorodo-web/lib/applyAnswer.test.ts 回填单测
src/prototypes/autorodo-web/lib/buildCleanPrompt.ts 步骤 1 清洗提示词
src/prototypes/autorodo-web/lib/buildRecordPrompt.ts 步骤 3 YunxiaoPMapp 多条口令
src/prototypes/autorodo-web/lib/buildRecordPrompt.test.ts 上报词单测
src/prototypes/autorodo-web/lib/inferMeta.ts 从原文推断提交部门/提交人
src/prototypes/autorodo-web/lib/inferMeta.test.ts 推断单测
src/prototypes/autorodo-web/lib/agentAdapter.ts 预留 clean / record 接口类型MVP 空实现)
src/prototypes/autorodo-web/lib/clipboard.ts navigator.clipboard.writeText 封装
src/prototypes/autorodo-web/lib/pendingQuestions.ts 从「待确认」文案生成选择题选项(词典摘要常量)
src/prototypes/autorodo-web/components/StepPaste.tsx 步骤 1 UI
src/prototypes/autorodo-web/components/StepConfirm.tsx 步骤 2 UI
src/prototypes/autorodo-web/components/StepExport.tsx 步骤 3 UI
src/prototypes/autorodo-web/AutorodoWebApp.tsx 三步状态机主壳
src/prototypes/autorodo-web/index.tsx 入口 @name + AnnotationHost
src/prototypes/autorodo-web/style.css 布局与触控样式
src/prototypes/autorodo-web/annotation-source.json 标注目录
src/prototypes/autorodo-web/.spec/requirements-prd.md AutoPRD
src/prototypes/autorodo-web/.spec/confirm-flow.md 确认与回填规则全文
src/resources/prd/autorodo-web-autoprd.md PRD 副本(若项目惯例需要)
修改 .axhub/make/project.json 注册原型(若 Make 未自动发现)
修改 ~/.cursor/skills/YunxiaoPMapp + 项目内副本 记录需求解析四字段Task 8可同迭代或紧随

不放入 src/common/:逻辑仅本原型使用,避免污染公共层。


Task 1: 类型 + parseDraft + 单测

Files:

  • Create: src/prototypes/autorodo-web/lib/types.ts

  • Create: src/prototypes/autorodo-web/lib/parseDraft.ts

  • Create: src/prototypes/autorodo-web/lib/parseDraft.test.ts

  • Step 1: 写入类型

// src/prototypes/autorodo-web/lib/types.ts
export type Priority = '紧急' | '高' | '中' | '低';

export type RequirementMeta = {
  priority: Priority | '';
  tags: string[];
  submitDept: string;
  submitter: string;
  titleKind: '新增' | '优化' | '';
};

export type PendingItem = {
  id: string;
  raw: string;
  question: string;
  options: string[];
  resolved: boolean;
  answer?: string;
};

export type RequirementDraft = {
  index: number;
  title: string;
  description: string;
  pendings: PendingItem[];
  meta: RequirementMeta;
  confirmed: boolean;
};

export type WizardStep = 1 | 2 | 3;
  • Step 2: 写失败单测(多条 + 待确认)
// src/prototypes/autorodo-web/lib/parseDraft.test.ts
import { describe, it, expect } from 'vitest';
import { parseAutorodoDraft } from './parseDraft';

const SAMPLE = `共 2 条

### 1
## 原始诉求AutoRDO

**标题**:合同台账批量导出

**描述**
在租赁合同台账支持批量导出

待确认:
- 导出范围未说明

### 2
## 原始诉求AutoRDO

**标题**:证照到期提醒

**描述**
调整证照到期提醒口径

待确认:
`;

describe('parseAutorodoDraft', () => {
  it('splits multiple requirements and pending items', () => {
    const list = parseAutorodoDraft(SAMPLE);
    expect(list).toHaveLength(2);
    expect(list[0].title).toContain('合同台账');
    expect(list[0].pendings).toHaveLength(1);
    expect(list[0].pendings[0].raw).toContain('导出范围');
    expect(list[1].pendings).toHaveLength(0);
    expect(list[1].confirmed).toBe(true);
  });
});
  • Step 3: 跑测确认失败

Run: npx vitest run src/prototypes/autorodo-web/lib/parseDraft.test.ts
Expected: FAIL模块不存在或 parseAutorodoDraft 未定义)

  • Step 4: 实现解析
// src/prototypes/autorodo-web/lib/parseDraft.ts
import type { RequirementDraft, PendingItem, RequirementMeta } from './types';

const emptyMeta = (): RequirementMeta => ({
  priority: '',
  tags: [],
  submitDept: '',
  submitter: '',
  titleKind: '',
});

function parsePendings(block: string, reqIndex: number): PendingItem[] {
  const m = block.match(/待确认[:]\s*([\s\S]*?)(?=\n###|\n##\s*原始诉求|$)/);
  if (!m) return [];
  const body = m[1].trim();
  if (!body || body === '无') return [];
  const lines = body
    .split('\n')
    .map((l) => l.replace(/^[-*•\d.、)\s]+/, '').trim())
    .filter(Boolean);
  return lines.map((raw, i) => ({
    id: `r${reqIndex}-p${i}`,
    raw,
    question: raw,
    options: [],
    resolved: false,
  }));
}

export function parseAutorodoDraft(md: string): RequirementDraft[] {
  const parts = md.split(/(?=^###\s*\d+)/m).filter((p) => /原始诉求AutoRDO/.test(p) || /\*\*标题\*\*/.test(p));
  const chunks =
    parts.length > 0
      ? parts
      : md.includes('**标题**')
        ? [md]
        : [];

  return chunks.map((chunk, index) => {
    const title = (chunk.match(/\*\*标题\*\*[:]\s*(.+)/)?.[1] ?? '').trim();
    const descMatch = chunk.match(/\*\*描述\*\*[:]\s*\n?([\s\S]*?)(?=\n待确认|$)/);
    const description = (descMatch?.[1] ?? '').trim();
    const pendings = parsePendings(chunk, index);
    return {
      index,
      title,
      description,
      pendings,
      meta: emptyMeta(),
      confirmed: pendings.length === 0,
    };
  });
}
  • Step 5: 跑测确认通过

Run: npx vitest run src/prototypes/autorodo-web/lib/parseDraft.test.ts
Expected: PASS

  • Step 6: Commit(仅当用户要求提交时执行;否则跳过所有 Commit 步骤)
git add src/prototypes/autorodo-web/lib/types.ts src/prototypes/autorodo-web/lib/parseDraft.ts src/prototypes/autorodo-web/lib/parseDraft.test.ts
git commit -m "$(cat <<'EOF'
feat(autorodo-web): add AutoRDO draft parser and types

EOF
)"

Task 2: applyAnswer + pendingQuestions

Files:

  • Create: src/prototypes/autorodo-web/lib/applyAnswer.ts

  • Create: src/prototypes/autorodo-web/lib/applyAnswer.test.ts

  • Create: src/prototypes/autorodo-web/lib/pendingQuestions.ts

  • Step 1: 写回填单测

import { describe, it, expect } from 'vitest';
import { applyPendingAnswer } from './applyAnswer';
import type { RequirementDraft } from './types';

const base: RequirementDraft = {
  index: 0,
  title: '合同台账批量导出',
  description: '在租赁合同台账支持批量导出\n导出范围待确认',
  pendings: [
    {
      id: 'r0-p0',
      raw: '导出范围未说明',
      question: '导出范围指什么?',
      options: ['当前筛选结果', '全部合同', '仅勾选行'],
      resolved: false,
    },
  ],
  meta: { priority: '', tags: [], submitDept: '', submitter: '', titleKind: '' },
  confirmed: false,
};

describe('applyPendingAnswer', () => {
  it('merges answer into description and marks pending resolved', () => {
    const next = applyPendingAnswer(base, 'r0-p0', '当前筛选结果');
    expect(next.pendings[0].resolved).toBe(true);
    expect(next.pendings[0].answer).toBe('当前筛选结果');
    expect(next.description).toContain('当前筛选结果');
    expect(next.description).not.toMatch(/导出范围:待确认/);
    expect(next.confirmed).toBe(true);
  });
});
  • Step 2: 跑测确认失败

Run: npx vitest run src/prototypes/autorodo-web/lib/applyAnswer.test.ts
Expected: FAIL

  • Step 3: 实现 applyAnswer + 简易出题
// applyAnswer.ts
import type { RequirementDraft } from './types';

export function applyPendingAnswer(
  draft: RequirementDraft,
  pendingId: string,
  answer: string,
): RequirementDraft {
  const pendings = draft.pendings.map((p) =>
    p.id === pendingId ? { ...p, resolved: true, answer: answer.trim() } : p,
  );
  const hit = draft.pendings.find((p) => p.id === pendingId);
  let description = draft.description;
  if (hit) {
    const label = hit.raw.replace(/未说明|待确认|不清楚/g, '').trim() || hit.raw;
    if (/待确认/.test(description)) {
      description = description.replace(/待确认/g, answer.trim());
    } else {
      description = `${description.replace(/\s+$/, '')}\n${label}${answer.trim()}`;
    }
  }
  const confirmed = pendings.every((p) => p.resolved);
  return { ...draft, description, pendings, confirmed };
}

// pendingQuestions.ts — MVP关键词启发式无匹配则「是 / 否 / 需补充」
export function buildOptionsForPending(raw: string): { question: string; options: string[] } {
  if (/导出范围|范围/.test(raw)) {
    return {
      question: '「导出范围」指什么?',
      options: ['当前筛选结果', '全部数据', '仅勾选行'],
    };
  }
  if (/模块|归属/.test(raw)) {
    return {
      question: '归属哪个业务模块?',
      options: ['租赁合同管理', '车辆管理', '证照管理', '还车应结款'],
    };
  }
  return {
    question: raw.endsWith('') || raw.endsWith('?') ? raw : `${raw}`,
    options: ['确认按原文理解', '暂不纳入本需求', '需文字补充'],
  };
}

export function enrichPendingsWithQuestions(draft: RequirementDraft): RequirementDraft {
  return {
    ...draft,
    pendings: draft.pendings.map((p) => {
      const q = buildOptionsForPending(p.raw);
      return { ...p, question: q.question, options: q.options };
    }),
  };
}

parseAutorodoDraft 返回前对每条调用 enrichPendingsWithQuestions,或在 UI 粘回后统一 enrich二选一推荐 UI 粘回后调用,保持 parse 纯净)。

  • Step 4: 跑测确认通过

Run: npx vitest run src/prototypes/autorodo-web/lib/applyAnswer.test.ts
Expected: PASS

  • Step 5: Commit(用户要求时)

Task 3: inferMeta + buildCleanPrompt + buildRecordPrompt

Files:

  • Create: src/prototypes/autorodo-web/lib/inferMeta.ts

  • Create: src/prototypes/autorodo-web/lib/inferMeta.test.ts

  • Create: src/prototypes/autorodo-web/lib/buildCleanPrompt.ts

  • Create: src/prototypes/autorodo-web/lib/buildRecordPrompt.ts

  • Create: src/prototypes/autorodo-web/lib/buildRecordPrompt.test.ts

  • Create: src/prototypes/autorodo-web/lib/clipboard.ts

  • Create: src/prototypes/autorodo-web/lib/agentAdapter.ts

  • Step 1: inferMeta 单测

import { describe, it, expect } from 'vitest';
import { inferSubmitMeta } from './inferMeta';

describe('inferSubmitMeta', () => {
  it('detects dept and person from free text', () => {
    const r = inferSubmitMeta('业务管理组张三反馈:合同导出不好用');
    expect(r.submitDept).toMatch(/业务/);
    expect(r.submitter).toContain('张三');
  });
});
  • Step 2: 实现 inferMeta
const DEPT_PATTERNS = [
  /(?:提交部门|部门)[:]\s*([^\s,;]+)/,
  /(业务管理组|产品部|安全部|运营部|财务部|技术部)/,
];
const PERSON_PATTERNS = [
  /(?:提交人|反馈人|提出人)[:]\s*([^\s,;]+)/,
  /@([\u4e00-\u9fa5]{2,4})/,
  /([\u4e00-\u9fa5]{2,4})反馈/,
];

export function inferSubmitMeta(text: string): { submitDept: string; submitter: string } {
  let submitDept = '';
  let submitter = '';
  for (const re of DEPT_PATTERNS) {
    const m = text.match(re);
    if (m?.[1]) {
      submitDept = m[1];
      break;
    }
  }
  for (const re of PERSON_PATTERNS) {
    const m = text.match(re);
    if (m?.[1]) {
      submitter = m[1];
      break;
    }
  }
  return { submitDept, submitter };
}
  • Step 3: buildRecordPrompt 单测 + 实现
// buildRecordPrompt.test.ts
import { describe, it, expect } from 'vitest';
import { buildYunxiaoRecordPrompt } from './buildRecordPrompt';
import type { RequirementDraft } from './types';

const one: RequirementDraft = {
  index: 0,
  title: '合同台账批量导出',
  description: '在租赁合同台账支持按筛选结果批量导出 Excel',
  pendings: [],
  meta: {
    priority: '中',
    tags: ['租赁合同'],
    submitDept: '产品部',
    submitter: '王冕',
    titleKind: '新增',
  },
  confirmed: true,
};

describe('buildYunxiaoRecordPrompt', () => {
  it('includes priority tags dept submitter', () => {
    const text = buildYunxiaoRecordPrompt([one]);
    expect(text).toContain('YunxiaoPMapp');
    expect(text).toContain('记录需求:【新增】合同台账批量导出');
    expect(text).toContain('优先级=中');
    expect(text).toContain('标签=租赁合同');
    expect(text).toContain('提交部门=产品部');
    expect(text).toContain('提交人=王冕');
    expect(text).toContain('推进至=暂不推进');
  });

  it('throws when meta incomplete', () => {
    expect(() =>
      buildYunxiaoRecordPrompt([{ ...one, meta: { ...one.meta, submitter: '' } }]),
    ).toThrow(/提交人/);
  });
});
// buildRecordPrompt.ts
import type { RequirementDraft } from './types';

export function metaComplete(d: RequirementDraft): boolean {
  const m = d.meta;
  return Boolean(
    m.priority && m.tags.length > 0 && m.submitDept.trim() && m.submitter.trim() && m.titleKind,
  );
}

export function buildYunxiaoRecordPrompt(drafts: RequirementDraft[]): string {
  for (const d of drafts) {
    if (!d.confirmed) throw new Error(`需求 ${d.index + 1} 仍有未确认项`);
    if (!metaComplete(d)) throw new Error(`需求 ${d.index + 1} 缺少优先级/标签/提交部门/提交人/类型`);
  }
  const lines = drafts.map((d) => {
    const title = `【${d.meta.titleKind}${d.title.replace(/^【(?:新增|优化)】/, '')}`;
    const tags = d.meta.tags.join('、');
    return `记录需求:${title};描述=${d.description.replace(/\n/g, ' ')};优先级=${d.meta.priority};标签=${tags};提交部门=${d.meta.submitDept};提交人=${d.meta.submitter};推进至=暂不推进`;
  });
  return [
    'YunxiaoPMapp',
    '请按下列条目依次记录需求;每条保持独立,不要合并。',
    '',
    ...lines,
  ].join('\n');
}
// buildCleanPrompt.ts
export function buildCleanPrompt(payload: {
  text: string;
  attachmentNames: string[];
}): string {
  const files =
    payload.attachmentNames.length > 0
      ? `附件:${payload.attachmentNames.join('、')}(请用多模态识读图/语音内容)`
      : '附件:无';
  return [
    'AutoRDO',
    '请按 AutoRDO 规则清洗下列素材:多条独立诉求拆成多份;提炼标题与书面描述;不确定处标「待确认」;描述无结尾句号。',
    files,
    '',
    '—— 素材开始 ——',
    payload.text.trim() || '(仅附件,无正文)',
    '—— 素材结束 ——',
  ].join('\n');
}
// clipboard.ts
export async function copyText(text: string): Promise<void> {
  await navigator.clipboard.writeText(text);
}

// agentAdapter.ts
export type CleanRequest = { text: string; attachmentNames: string[] };
export type RecordRequest = { drafts: import('./types').RequirementDraft[] };

/** MVP未接组织 Agent后续替换实现即可 */
export const agentAdapter = {
  async clean(_req: CleanRequest): Promise<null> {
    return null;
  },
  async record(_req: RecordRequest): Promise<null> {
    return null;
  },
};
  • Step 4: 跑全部 lib 单测

Run: npx vitest run src/prototypes/autorodo-web/lib
Expected: 全部 PASS

  • Step 5: Commit(用户要求时)

Task 4: 原型壳 + 步骤 1 UI

Files:

  • Create: src/prototypes/autorodo-web/index.tsx
  • Create: src/prototypes/autorodo-web/AutorodoWebApp.tsx
  • Create: src/prototypes/autorodo-web/style.css
  • Create: src/prototypes/autorodo-web/components/StepPaste.tsx
  • Create: src/prototypes/autorodo-web/annotation-source.json(先最小目录)
  • Modify: .axhub/make/project.json(若启动后未出现在列表则补注册)

前置Read src/resources/design-system/DESIGN.mdToken、触控 ≥44px、禁止原生 select

  • Step 1: 入口与主壳骨架
// index.tsx
/**
 * @name AutoRDO 需求清洗工作台
 */
import './style.css';
import '../../../resources/design-system/oneos-ds-tokens.css';
import React from 'react';
import { PrototypeAnnotationHost } from '../../common/prototype-annotation-host';
import type { AnnotationSourceDocument } from '@axhub/annotation';
import annotationSourceDocument from './annotation-source.json';
import { AutorodoWebApp } from './AutorodoWebApp';

export default function AutorodoWebEntry() {
  return (
    <>
      <AutorodoWebApp />
      <PrototypeAnnotationHost
        source={annotationSourceDocument as AnnotationSourceDocument}
        options={{}}
      />
    </>
  );
}

AutorodoWebApp:左侧步骤 1/2/3step state步骤 1 渲染 StepPaste

  • Step 2: StepPaste

  • contentEditable 或 textarea 接收粘贴;onPaste 收集 clipboardData.filesimage/、audio/)为附件名列表(原型阶段可用 File.name不上传

  • 按钮「确认并复制清洗提示词」→ buildCleanPrompt + copyTexttoast「已复制请粘到 Cursor 跑 AutoRDO」

  • 可选 checkbox「粘贴后自动复制」默认关

  • 控件:优先 V2 封装;按钮 min-height 44px

  • Step 3: 本地预览

Run: 打开 Make 预览 /prototypes/autorodo-web
Expected: 能粘贴、点确认后剪贴板为 AutoRDO 提示词

  • Step 4: Commit(用户要求时)

Task 5: 步骤 2 确认 UI

Files:

  • Create: src/prototypes/autorodo-web/components/StepConfirm.tsx

  • Modify: src/prototypes/autorodo-web/AutorodoWebApp.tsx

  • Step 1: 粘回区 + 解析

  • textarea「粘回 AutoRDO 初稿」

  • 按钮「解析初稿」→ parseAutorodoDraftenrichPendingsWithQuestions → 对每条 inferSubmitMeta(description + title) 预填 meta

  • Pill 切换需求 index

  • Step 2: 左右分栏确认

  • 左:标题(可编辑)+ 描述(只读展示实时稿)

  • 右:当前未解决 pending单选 options补充 input「确认并下一题」调用 applyPendingAnswer

  • 元数据:优先级四选一;标签用可增删 chip自由输入 MVP提交部门/提交人 input类型 新增/优化

  • 全部 confirmed 且 metaComplete 后可进步骤 3

  • Step 3: 手动验收路径

  1. 粘贴 Task1 SAMPLE 初稿
  2. 答完题后描述含答案且无「待确认」
  3. 填齐元数据
  • Step 4: Commit(用户要求时)

Task 6: 步骤 3 导出 + 样式打磨

Files:

  • Create: src/prototypes/autorodo-web/components/StepExport.tsx

  • Modify: style.cssAutorodoWebApp.tsx

  • Step 1: StepExport

  • 预览 buildYunxiaoRecordPrompt(drafts)

  • 「一键复制上报提示词」

  • 「复制定稿 Markdown」输出多份已确认 ## 原始诉求AutoRDO,待确认:无)

  • 错误态:捕获 throw页面内提示缺哪条哪字段

  • Step 2: 响应式

  • ≤767px步骤条顶置确认区改为上下堆叠触控 ≥44px正文 ≥14px

  • Step 3: 预览验收

全路径走通:粘贴 → 复制清洗词 →(模拟)粘回 SAMPLE → 确认 → 复制上报词,粘贴到文本编辑器检查字段齐全。

  • Step 4: Commit(用户要求时)

Task 7: AutoPRD + 标注 + 导航同步

Files:

  • Create: src/prototypes/autorodo-web/.spec/confirm-flow.md

  • Create: src/prototypes/autorodo-web/.spec/requirements-prd.md

  • Create: src/resources/prd/autorodo-web-autoprd.md(与 PRD 同步)

  • Modify: annotation-source.json(产品需求说明节点)

  • Step 1: 写 confirm-flow.md

含:判定顺序表、前置条件、数据源(粘回 Markdown / 本地启发式选项)、用户可见结果、标明未接真 API。

  • Step 2: 写 requirements-prd.md

按 oneos-autoprd 结构:总览/目标/边界/角色/用户故事(起点→运作→闭环)/验收;链到 confirm-flow.md

  • Step 3: 同步标注与导航
npm run nav:sync -- --prototype autorodo-web --note "新增 AutoRDO 网页清洗与云效上报提示词工作台"
  • Step 4: Commit(用户要求时)

Task 8: YunxiaoPMapp 消费四字段Skill 扩展)

Files:

  • Modify: ~/.claude/skills/YunxiaoPMapp/references/commands.md(及项目 .cursor / .claude 同步副本)

  • Modify: YunxiaoPMapp SKILL.md 记录需求参数说明

  • 可选:references/record-meta-fields.md 新文件

  • Step 1: 文档约定口令字段

在口令面增加:

记录需求:…;优先级=紧急|高|中|低;标签=…;提交部门=…;提交人=…;推进至=…

说明:网页上报词已带齐时,禁止再 Plan 重复问这四项(除非缺省);缺省则仍问。

  • Step 2: 实现侧

若现有 live 脚本已支持 priority/tag把提交部门/提交人映射到云效字段(查 runtime-ids.json;未验证字段则先写入描述尾部 提交部门/提交人,并在文档标明「字段 ID 待验证」)。

  • Step 3: 与网页联调

用步骤 3 复制出的提示词在 Cursor 跑一次「记录需求」(测试项目或 dry-run 策略按团队惯例)。

  • Step 4: Commit(用户要求时)

规格覆盖自检

Spec 要求 Task
粘贴文/图/语音 + 复制清洗词 4
粘回初稿 + 分页确认 + 回填 1,2,5
优先级/标签/部门/提交人 3,5,6
一键 Yunxiao 多条口令 3,6
预留组织 Agent 3 agentAdapter
AutoPRD / 业务规则文档 7
Yunxiao 消费四字段 8
不直连云效 / 无 OCR 全程遵守

执行说明

  • 所有 Commit 步骤默认跳过,除非用户明确要求提交。
  • Task 8 可与 Task 47 并行由另一会话做;网页 MVP 以 Task 17 为可演示闭环(上报词已含四字段,即使 Skill 暂未写入云效自定义字段,口令仍可用于人工/后续 Skill

Plan complete and saved to docs/superpowers/plans/2026-07-25-autorodo-web.md.

两种执行方式:

  1. Subagent-Driven推荐 — 每任务开新子代理,任务间复核,迭代快
  2. Inline Execution — 本会话按 executing-plans 连续执行,设检查点

要哪一种?