Skip to content

feat: structured worker positions for deterministic orchestration - #5

Open
WuFenG-Hub wants to merge 2 commits into
metaid-developers:mainfrom
WuFenG-Hub:feature/worker-position
Open

feat: structured worker positions for deterministic orchestration#5
WuFenG-Hub wants to merge 2 commits into
metaid-developers:mainfrom
WuFenG-Hub:feature/worker-position

Conversation

@WuFenG-Hub

Copy link
Copy Markdown

背景与问题

当前 MetaBot 只有两级分类:metabot_type'twin' | 'worker'(建表 CHECK 约束)。worker 之间的区分完全依赖自由文本 role / soul / goal / bio,带来两个痛点:

  1. 创建环节:新 worker 没有「职务」概念,全靠手填自由文本,不同用户对「开发 bot」的理解各异,无法复用。
  2. 编排环节:Group Task 的 chair 选人靠读 roster 的 name (role) + role/bio 文本做语义猜测(groupTaskPrompts.ts),结果不可枚举、不可回归测试;「任务需要什么能力 → 谁适合」没有结构化映射。

方案:参照二级招聘市场模式,给 worker 增加结构化「职位(position)」维度——内置 dev / researcher / designer / writer / operator 五个职位,各带 persona 模板(role/soul/goal/bio)、默认技能与权限说明。编排时职位优先匹配、自由文本语义兜底,即信息检索的双通道结构:精确索引层(职位)负责快速命中,语义层覆盖无法归类的自定义 bot。

改动内容

文件 改动
数据 src/main/sqliteStore.ts metabots.position TEXT 列 + 幂等迁移 migrateMetabotPositionColumn();重建表迁移兼容透传
Store src/main/metabotStore.ts create/update 读写 position;twin 永不携带;worker 提升为 twin 时清除;undefined 保留旧值
模板库 src/main/positions.ts(新增) 5 个内置职位 + getPositions/getPosition/normalizePosition/positionAppliesTo
IPC src/main/preload.ts / src/main/main.ts / src/renderer/types/electron.d.ts create/update 入参透传 position;新增只读 metabot:getPositions
UI MetaBotEditTabs.tsx + MetabotsManager.tsx + i18n.ts Persona tab 职位下拉,套用模板自动填充 role/soul/goal/bio(仍可手动修改),展示默认技能与权限说明
编排 groupTaskPrompts.ts / groupTaskDaemon.ts roster 行 - name (worker, position: dev);profile 行注入 Position;chair playbook 新增「按职位优先分配、语义兜底」规则
目录 metabotDirectoryService.ts list-metabots 输出 position,供 twin 规划
文档 SKILLs/metabot-group-task/SKILL.md 选人步骤改为「position first, bio/role fallback」
测试 tests/metabotPosition.test.mjs(新增) + 既有测试更新 8 项新断言(职位库/目录/prompt 注入/chair 规则);既有断言同步

兼容性

  • 老 worker position = NULL → 展示「未设定(通用)」,编排自动走语义兜底,功能零回退
  • metabot_type CHECK 约束不动;twin 逻辑不变
  • position 纯本地维度,绝不写入链上 pin(EDIT_TAB_SYNC_GROUPS 不含 position,有测试守护)
  • 未知 slug 保留原样(允许自定义职位),仅做小写规范化

验证

  • npm run compile:electron:与基线相比零新增编译错误(基线 15 个为本地 node_modules 依赖缺失导致,与本次改动无关)
  • 新增 tests/metabotPosition.test.mjs:8/8 通过
  • 回归:groupTaskStore/groupTaskDaemon/groupTaskService/twinWorkerDirectoryService 等 77 项 mjs 测试 + metabotEditTabs 7 项 tsx 测试全过

设计取舍

  • 模板≠锁定:position 只提供初始模板文本与结构标签,role/soul/goal/bio 始终可编辑
  • 检索键优先:职位是确定性匹配,自由文本是兜底——与招聘市场同构(JD 职位名是结构化字段,简历自由文本是深入了解)
  • 后续可演进:职位模板外部化(用户自定义职位 JSON)、黄页按职位筛选、任务类型→职位规则映射表

Introduce a second-level worker classification (position) under
metabot_type='worker', modeled after job-market roles: dev, researcher,
designer, writer, operator. Each position carries persona templates
(role/soul/goal/bio), default skills and permission notes.

- DB: metabots.position TEXT column + idempotent migration; legacy
  workers keep NULL and stay fully compatible.
- Store: create/update round-trips position; twin bots never carry one;
  promoting a worker to twin clears it.
- UI: Persona tab position dropdown applies the built-in templates
  (still editable afterwards); default skills shown as guidance.
- Orchestration: group-task roster/profile lines expose position and
  the chair playbook assigns subtasks by position first, falling back
  to role/bio semantic matching only when no position fits.
- Directory: list-metabots entries carry position for twin planning.
- Position stays local-only: never published on-chain (covered by tests).
- Tests: positions/directory/prompt coverage + updated expectations.
newfish added a commit that referenced this pull request Aug 8, 2026
P0-1: failed noise steps no longer block owner acceptance
- acceptGroupTask counts only active steps (blocked/ready/queued/running) as
  unfinished; failed/cancelled steps (mistaken mentions, aborted turns) pass
- failed->completed transition added to the step state machine; ignoreFailedSteps
  demotes noise steps with an ignored marker, auto-run on review entry

P0-2: member_names now honored on create
- RPC create no longer force-overrides autoSelectWorkers=true; explicit members
  (ids or names) are used as-is, full-roster auto-select only when none named

P0-3: kickoff no longer triggers the whole roster
- kickoff roster line drops the @ prefixes
- mention matching is word-boundary @name only; a bare name (roster line,
  recap text) never triggers a reply
- per-tick cap/cooldown skips are DEFERRED and retried on a later tick, so a
  skipped worker still gets its chance instead of being dropped

P1-4: DELIVERABLE parsing hardened
- chair messages never collected; URI-shaped tokens with placeholder/malformed
  URIs (metaapp://<pinId>, metaapp://[PINID]) rejected as a whole
- cleanup hatch: deliverable-delete store method + RPC endpoint + skill action

P1-5: chair planning directive embeds the full member roster (name/role/bio/goal)

P2-6: show/getGroupTask returns the latest group transcript (messages)

P2-7: chair auto responses suppressed when the Twin already replied to the
triggering message (reply_pin match), plus a 1-per-tick cap on auto responses

P2-8: same-message multi-step creation fixed by the word-boundary mention rule

Tests: 83 pass across groupTaskDaemon/orchestrationBridge/service/store suites
newfish added a commit that referenced this pull request Aug 8, 2026
… tag occurrence

- New groupTaskDeliverableParser module: strictly line-scoped, one candidate
  per [DELIVERABLE] tag occurrence (two tag lines -> two rows), strict
  validation (placeholder <pinId>/[PINID] and ellipsis/truncation rejected;
  a real URI must carry a 64-hex+i0 pinid token or be an ^https?:// URL),
  trailing markdown/punctuation and full-width paren annotations never leak
  into the recorded URI, kind follows the candidate's own scheme.
- Daemon ingestion records every valid candidate separately (dedupe per
  msg_pin_id + uri + kind) instead of one row per message, so real URIs on
  sibling tag lines are no longer dropped (#7 msg94: metaapp + share link).
- Correction-first aggregation hook (findSupersededDeliverable) lands with
  the ingestion rewrite: a message declaring 更正/修正/以…为准 supersedes the
  matched prior row in place (same author + shared pinid token).
- Regression set from the real #5/#6/#7 messages (placeholder planning
  samples, truncated metafile, body dir paths, paren annotations, multi-line
  and single-line-multi-tag messages, trailing markdown).
- keep both feature sets: structured positions (HEAD) + OpenTeam remote
  teammates (upstream) in types, daemon prompt roster, and SKILL docs
- remote members render without a fake 'position: generic' label
- fix PR's own missing 'position' field on renderer Metabot type (build
  was broken since 2026-08-08; no CI ever caught it)
- drop 2 unused eslint-disable directives inherited from upstream baseline
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant