Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Design Doc: Approved Publish Identity Routing

## Related issue

- Issue: #10, #11
- What was unsatisfactory: The execution-card rules covered evidence boundaries and person-led task content, but approved publication did not define how to select a sender identity for an external group. A valid user profile could be denied while a bot identity in the same profile could publish successfully.
- Expected behavior: Before publishing, resolve the destination and probe the configured profile's user and bot capabilities. Prefer the user identity, fall back once to an in-group bot on the same profile when the user route is denied, and persist the actual sender and fallback reason in the receipt.

## Optimization goal

Make approved publication deterministic across private chats and external groups, while preserving the existing content approval, idempotency, and no-Base-mutation boundaries.

## Direction

Keep `METAINFLOW_FEISHU_PROFILE` as the only default profile. Add sender routing at the approved-publish boundary: user first, same-profile bot fallback when membership and readiness are confirmed, otherwise a blocked result with the exact reason. Keep the execution-card content contract person-led and require direct task/cell links only for registered deliverables.

## Implementation plan

- Skill sections to change: approved publish routing, execution-card structure, publication receipt contract.
- Files to update: `SKILL.md`, `references/daily-operations.md`, `tests/test_business_contracts.py`, `.evozeus-wrapper/CHANGELOG.md`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Record the behavior change in the Unreleased changelog

This implementation plan explicitly includes .evozeus-wrapper/CHANGELOG.md, but the commit leaves that file untouched; its current Unreleased section only describes the earlier v0.15.0 harness upgrade. Because this repository treats the changelog as the Skill release fact source, the new sender-routing and execution-card contracts would be omitted from the next release record, including their related issues, verification, and rollback details.

Useful? React with 👍 / 👎.

- Backward compatibility: Scheduled runs remain `proposal_only` and `private_draft`; no automatic publish, Base mutation, status change, acceptance, or baseline change is added.

## Verification plan

- Source discovery check: verify the configured profile, destination chat, and both identity readiness states before publish.
- Manual checks: user-denied/bot-available publishes once as bot; user-available publishes as user; both unavailable returns blocked; receipt records sender and fallback.
- Regression cases: duplicate idempotency key does not resend; changed card fingerprint or destination invalidates approval; unregistered collaboration item has no fabricated link.
- Preflight command: `python3 .evozeus-wrapper/scripts/evozeus_wrapper_preflight.py doctor --repo MetaInFLow/metainflow-dev-tasks`.

## Release plan

- Changelog entry: Unreleased patch for approved-publish identity routing and person-led card contract.
- Release tag: next patch release after review and merge.
- Release description summary: closes the publication gap observed while delivering the 2026-09-22 execution card.

## Risks and rollback

- Risk: Some external chats may permit neither sender identity; the Skill must report a blocked publication without trying unrelated profiles.
- Rollback plan: revert the focused Skill, reference, test, changelog, and design-doc changes; retain the existing Issue #10/#11 behavior.
2 changes: 1 addition & 1 deletion SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ description: 通过 lark-cli/LarkSuite CLI 处理「源子技术部管理系统
- 工时或日报:新建 `03-工作日志`,绑定 `所属子任务`,填写日期、记录人、工作内容、投入工时、完成百分比和阻塞情况。
- 早会或日计划评估:先解析相关任务所属项目并读取项目任务安排,再读取会议纪要、会前日计划、上一工作日项目相关聊天和相关 Base 记录,按“项目范围、里程碑、计划覆盖、优先级、负责人、时间节点、验收口径、阻塞风险、系统同步”逐项核对;输出已对齐项、冲突/缺口和建议同步动作。
- 状态、排期或负载询问:读取 `01-需求池`、`02-子任务`、`03-工作日志`、`13-团队成员` 的相关记录;涉及当前或当天执行时同时读取会前日计划。回答时给出标题、状态、负责人、优先级、日期/截止日期和关联对象,并说明是否只读取了分页样本。
- 消息卡片或群消息:“生成/做一个”只生成私下草稿;只有用户对最终内容和目标群/聊天给出明确发布授权时才发送。修改后重新确认,发布后返回发布回执。
- 消息卡片或群消息:“生成/做一个”只生成私下草稿;只有用户对最终内容和目标群/聊天给出明确发布授权时才发送。卡片草稿按人员组织,每人的可验收产出单独列出;已登记产出必须使用对应任务记录/单元格直链。`approved_publish` 发送前先用配置 profile 核对目标会话和可用身份:优先用户身份;用户身份因权限或外部群策略失败时,在同一 profile 检查并尝试已在群内的机器人身份一次。成功后回执必须记录实际发送身份、回退原因、目标会话、消息标识和幂等键;用户与机器人均不可用才返回阻塞。

## 最低写入信息

Expand Down
6 changes: 5 additions & 1 deletion references/daily-operations.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,11 @@ checkpoint_path: optional consumer-workspace path
- 明确的目标会话;
- 本次发送 `idempotency_key`。

发布后记录 `publication_receipt`,至少包含目标、发布时间、消息标识和可用链接。卡片实质修改、目标变更或批准已撤回时,旧授权立即失效。
发送前先用配置的 profile 查询或核对目标会话,并分别确认用户身份与机器人身份的可用性和群成员关系。默认优先用户身份;若用户身份返回权限、外部群策略或不在群内错误,且同一 profile 的机器人身份已在目标群内,则只回退一次到机器人身份。不得因为用户身份失败直接判定目标群不可发,也不得无依据切换到其他 profile。

发布后记录 `publication_receipt`,至少包含目标、发布时间、消息标识、可用链接、`sender_identity`、`delivery_mode`、`fallback_reason` 和 `idempotency_key`。用户身份失败后的机器人回退沿用同一内容指纹和幂等键,避免重复发布。卡片实质修改、目标变更或批准已撤回时,旧授权立即失效。

执行卡草稿采用“人 -> 今日动作 -> 交付物 -> 截止 -> 交接/验收”的主结构;项目只作为任务上下文,不把不同成员机械均分成同级项目区块。需要形成产出物的事项必须附已登记任务的记录/单元格直链;没有任务登记的协同事项明确标记为待同步,不伪造链接。

## 日终收口

Expand Down
22 changes: 22 additions & 0 deletions tests/test_business_contracts.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,28 @@ def test_capacity_and_publication_gates_are_defined(self):
for term in ["WIP", "请假后容量", "草稿", "明确发布授权", "目标群", "发布回执", "重新确认"]:
self.assertIn(term, self.contract)

def test_approved_publish_routes_sender_identity_and_preserves_receipt(self):
for term in [
"配置的 profile",
"用户身份",
"机器人身份",
"同一 profile",
"sender_identity",
"delivery_mode",
"fallback_reason",
"同一内容指纹和幂等键",
]:
self.assertIn(term, self.skill + self.daily_operations)

def test_execution_card_is_person_led_and_links_registered_outputs(self):
for term in [
"人 -> 今日动作 -> 交付物 -> 截止 -> 交接/验收",
"任务的记录/单元格直链",
"待同步",
"按人员组织",
]:
self.assertIn(term, self.skill + self.daily_operations)

def test_skill_routes_recurring_management_to_daily_loop(self):
self.assertIn("references/daily-operations.md", self.skill)
self.assertIn("每日项目管理闭环", self.skill)
Expand Down
Loading