feat: 同步引擎+Agent配置+依赖自动落位+看板大改(预览/md渲染/筛选/徽章组)

后端:
- src/sync/todo-sync.ts: todo.json 单向同步引擎(导入=首次同步,幂等,source_ref 映射,
  subs 复杂度修正为 easy,旧侧 done 历史事实优先 forceDone)
- 依赖自动落位:ready 意图按 deps 落位 blocked,依赖全 done 自动放行,
  daemon 启动 reconcileDeps 对账,手动绕过会弹回
- 新 API: PATCH projects/:id(autonomy/concurrency)、POST :id/sync、GET /api/agents、
  PATCH tasks/:id(title/priority/complexity 重置)
- daemon 定时同步(MAESTRO_SYNC_INTERVAL 默认 300s) + project.synced 事件
- priority 语义翻转: P0 最高/P1 默认/P2 最低,取值限 0..2,排序/映射/MCP/CLI 全跟进
- 静态服务发 no-cache 头(修浏览器吃旧 CSS/JS)
- schema 迁移: tasks.source_ref / projects.last_sync_at(ensureColumn 平滑升级旧库)

看板:
- 全屏预览模式(94vh 读完整方案+就地裁决,Esc/遮罩/裁决自动关闭)
- 产出 markdown 渲染为 HTML(零依赖渲染器,转义优先)
- 任务树筛选(复杂度/状态分组/关键字)+ 顶栏徽章组(待审批/可执行/执行中,hover 展开)
- 依赖可视化:详情 DEPS 区块 + 行内⛓等依赖 + 锚点跳转定位
- 按钮收敛:提交评审/编辑产出移除(CC 经 MCP 操作),界面只留用户动作
- Agent 执行面板 + 项目配置(并发/工作模式)+ 同步按钮

测试:21 个全过(新增 sync 幂等/迁移/patch/依赖落位/对账幂等)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-06-12 23:58:28 +08:00
parent c6e66baa19
commit fa472a06a7
18 changed files with 1942 additions and 368 deletions
+56 -2
View File
@@ -39,9 +39,47 @@
<div id="projMeta" class="proj-meta"></div>
</div>
<div class="spacer"></div>
<div id="gateCount" class="gate-count" hidden></div>
<div id="badgeGroup" class="badge-group" hidden>
<span id="bdgGate" class="bdg bdg-gate" title="等待你裁决的审批闸">
<span class="bdg-ico"></span><span class="bdg-n">0</span><span class="bdg-label">项待审批</span>
</span>
<span id="bdgReady" class="bdg bdg-ready" title="叶子任务 · 可执行 · 依赖全部完成">
<span class="bdg-ico"></span><span class="bdg-n">0</span><span class="bdg-label">可执行</span>
</span>
<span id="bdgRun" class="bdg bdg-run" title="agent 正在执行">
<span class="bdg-ico"></span><span class="bdg-n">0</span><span class="bdg-label">执行中</span>
</span>
</div>
<div id="topActions" class="top-actions" hidden>
<span id="syncMeta" class="sync-meta"></span>
<button id="btnSync" class="btn btn-xs" data-action="sync-todo">⟳ 同步 todo</button>
<button class="btn btn-xs" data-action="toggle-config" title="项目配置(并发 / 工作模式)">⚙ 配置</button>
</div>
</header>
<section id="configPanel" class="panel config-panel" hidden>
<div class="form-row">
<label class="narrow">最大并发
<input id="cfgConcurrency" type="number" min="1" step="1" value="1">
</label>
<label>工作模式
<select id="cfgAutonomy">
<option value="manual">manual · 手动</option>
<option value="auto-easy">auto-easy · 自动执行 Easy</option>
<option value="auto-approved">auto-approved · 自动执行已批准</option>
</select>
</label>
<span id="cfgCurrent" class="cfg-current"></span>
<button class="btn btn-solid" data-action="save-config">保存配置</button>
<button class="btn" data-action="toggle-config">收起</button>
</div>
</section>
<section id="agentSection" class="agent-section">
<div class="sec-head"><span class="head-mark cyan"></span>Agent 执行<span id="agentTotal" class="agent-total"></span></div>
<div id="agentBody"></div>
</section>
<section id="gateSection" class="gate-section" hidden>
<div class="gate-stripe"></div>
<div class="sec-head gate-head"><span class="head-mark amber"></span>审批闸 · 等待裁决</div>
@@ -72,7 +110,11 @@
<select name="parentId"><option value="">(顶层)</option></select>
</label>
<label class="narrow">优先级
<input name="priority" type="number" value="0" step="1">
<select name="priority">
<option value="0">P0 · 高</option>
<option value="1" selected>P1 · 中</option>
<option value="2">P2 · 低</option>
</select>
</label>
</div>
<div class="form-row form-actions">
@@ -81,6 +123,17 @@
</div>
</form>
<div id="filterBar" class="filter-bar" hidden>
<div class="filter-row">
<input id="filterKw" type="text" placeholder="⌕ 搜索标题…" autocomplete="off">
<span id="filterCplx" class="fchips"></span>
<span class="spacer"></span>
<span id="filterCount" class="filter-count"></span>
<button class="btn btn-ghost btn-xs" id="filterClear" data-action="filter-clear" hidden>✕ 清除筛选</button>
</div>
<div id="filterStatus" class="filter-row"></div>
</div>
<div id="taskTree"></div>
</section>
</main>
@@ -116,6 +169,7 @@
</form>
</div>
<div id="previewRoot" class="preview-root" hidden></div>
<div id="toastRoot" class="toast-root"></div>
<script src="app.js"></script>
</body>