Skip to content

feat(dashboard): let the owner confirm a validated steward team plan - #4547

Merged
huangruiteng merged 1 commit into
mainfrom
codex/dashboard-team-plan-confirmation
Sep 16, 2026
Merged

huangruiteng merged 1 commit into
mainfrom
codex/dashboard-team-plan-confirmation

Conversation

@huangruiteng

Copy link
Copy Markdown
Collaborator

What Was Broken

The steward's team intake produces one validated multi-lane preview, and the Chat action surface already owns team.plan with a preview and an apply that re-validates at PRE_SETTLEMENT. The dashboard could not render it: typedActionKindSchema in src/data/chat.ts is a closed zod enum without team.plan, so a validated preview failed to parse on the client instead of becoming a card an owner could confirm. The one-sentence team flow therefore had no confirmation surface at all — the last missing piece of this lane's P1.

(An earlier attempt at this PR was closed: it was pushed onto the branch of the already-merged #4545. This branch is based on the current main and contains only the dashboard change.)

What This Changes

  • Transport: typedActionKindSchema accepts team.plan; WorkspaceActionPreview["actionKind"] matches.
  • Presentation: new presentation-only reducer src/features/personal-workspace/team-plan-preview.ts renders what an owner must see before confirming:
    • the Goal the plan staffs, and its objective;
    • one row per lane: the Agent that runs it, that lane's first bounded Todo (priority · action_kind · text), and its acceptance signal;
    • a staffing gap lane reads unstaffed · <reason_code> · <declined work> and never shows invented work;
    • the gap summary, the quota envelope, and the stop condition.
      It owns no authority, performs no effects, and never renders a lane as already created.
  • Card wiring: the proposal card uses that reducer, with a localized summary, an impact statement that says exactly what confirming does (ready lanes get their first bounded Todo through the canonical owner; a gap lane creates nothing; no lane exists before the apply receipt returns; a Goal/Agent change makes the proposal stale), and a confirmation label.
  • i18n: the new strings in English and Chinese.

The generic review-plan compiler already treats a preview_ready non-lifecycle proposal as applyable, so no authority logic was added or changed.

Verification

  • tsc --noEmit -p tsconfig.json — clean.
  • npm run smoke:team-plan-proposal (new) — team plan proposal smoke ok. It asserts the transport accepts the kind (the exact thing that was broken), that the kind stays exact, that a ready lane shows its first Todo, priority and acceptance, that a gap lane is reported as unstaffed with its reason and declined work, and that the quota envelope and stop condition render.
  • npm run smoke:chat-route — chat-route-smoke: ok; npm run smoke:action-review-plan — PASS. The kind addition does not disturb the other proposal surfaces.
  • loopx canary premerge --from-git-diff — passed (diff hygiene, 8 risk-profile smokes, catalog canaries; one advisory: the known baseline maintainability ratchet).

Entry Points And Scope

  • Changed: the dashboard chat/workspace proposal card.
  • Unchanged: the Lark goal channel, the CLI, and the Chat backend action surface (they already carried team.plan).
  • First screen: the card only exists when a validated team plan is present, so the dashboard's first viewport, hero, primary CTA and opening navigation are unchanged; no preview gate applies.
  • Packaged frontend: the dashboard bundle is built at install time, so the shipped chat bundle is verified to contain the new kind after promoting the local install; the readback is reported in the approval comment.

Residual Gaps

  • There is no browser-level end-to-end acceptance for confirming a team plan from the dashboard (it would need a seeded steward preview plus a running dev server); the smoke covers the transport and the presentation reducer, not the click path.
  • The card renders the preview it is given. If an apply makes the plan stale mid-flight, the existing stale/regenerate path handles it; nothing new was added for that flow.

The steward's team intake produces one validated multi-lane preview, and the
Chat action surface already owns `team.plan` with a preview and an apply. The
dashboard could not render it: its transport schema accepted a fixed action-kind
enum without `team.plan`, so a validated preview failed to parse instead of
becoming a card an owner could confirm. That left the one-sentence team flow
with no confirmation surface at all.

- `src/data/chat.ts`: the transport kind enum accepts `team.plan`.
- `personal-workspace-model.ts`: the workspace preview kind matches.
- `team-plan-preview.ts` (new): a presentation-only reducer that turns the
  preview into what an owner reads before confirming — the Goal the plan staffs,
  its objective, one row per lane (the Agent, that lane's first bounded Todo with
  its priority and action kind, and its acceptance signal), the declared staffing
  gaps with the work they did not staff, the quota envelope and the stop
  condition. It owns no authority and claims no lane already exists.
- `personal-workspace-page.tsx`: the proposal card uses that reducer, with a
  summary, an impact statement that says what confirming does, and a primary
  label for confirmation.
- `i18n.tsx`: the new strings in English and Chinese.

Verification: `tsc --noEmit -p tsconfig.json` clean; `npm run
smoke:team-plan-proposal` (new) prints `team plan proposal smoke ok`; the
existing `smoke:chat-route` and `smoke:action-review-plan` stay green, so the
kind addition did not disturb the other proposal surfaces.

The card only appears when a validated team plan exists, so the dashboard's
first viewport, hero and navigation are unchanged. The user-visible entry point
covered here is the dashboard chat/workspace proposal card; the Lark goal
channel and the CLI are unchanged by this diff.

Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>

@huangruiteng huangruiteng left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
Reviewed exact head: 89833943e8d2a8964a7e91b82c9afe1336e3ca2e

English verdict: APPROVE

动机

管家的团队入端口径会产出一份已验证的多 lane 预览,Chat 侧也已经拥有 team.plan 的 preview 与 apply(apply 在 PRE_SETTLEMENT 会用宿主事实重新校验)。但 Dashboard 渲染不了它:src/data/chat.ts 的 typedActionKindSchema 是一个封闭 zod 枚举,不含 team.plan,于是已校验的预览在客户端解析失败,业主根本没有确认界面——这正是本 lane P1 的最后一处缺口。

(本条此前误开为 #4546:分支仍带着已合并的 #4545 提交。已关闭,并从当前 main 重新开分支,只含本次 Dashboard 改动。)

改动思路

  • 只补传输层枚举 + 呈现层,不碰任何权威逻辑:compileActionReviewPlan 对非 lifecycle 的 preview_ready 提案本来就是 applyable,所以这次没有新增/修改授权判断。
  • 把"业主确认前必须看到什么"抽成一个纯呈现 reducer(新文件),便于脱离 React 单测:Goal、objective、逐 lane(执行 Agent、该 lane 的首个有界 Todo 及其 priority/action_kind、acceptance)、声明了编制缺口的 lane(reason + 未配出的工作)、缺口汇总、quota 包络、stop condition。
  • 明确"预览不是效果":卡片陈述确认后会做什么(就绪 lane 经 canonical owner 建首个有界 Todo;缺口 lane 不创建任何东西;apply 回执返回前不存在任何 lane;Goal/Agent 变化会让提案 stale),且绝不把 lane 渲染成已创建。
  • 文案中英同步。

具体改动

  • src/data/chat.ts(+3):typedActionKindSchema 接受 team.plan。
  • personal-workspace-model.ts(+2/-1):WorkspaceActionPreview["actionKind"] 对齐。
  • team-plan-preview.ts(新,+115):纯呈现 reducer 与两个小访问器(lane 数、Goal)。
  • personal-workspace-page.tsx(+11):卡片接入 reducer,并给出 summary / impact / 主按钮文案。
  • i18n.tsx(+16):新增 8 个 key 的中英文案。
  • smoke/team-plan-proposal-smoke.ts(新,+133)与 package.json(+1):回归 smoke 与脚本。

对主干的风险

  1. 传输层放宽一个枚举值:只增加一个 action kind,其他 kind 的解析路径不变;smoke:chat-route、smoke:action-review-plan 均保持通过。
  2. 呈现层是纯函数:新 reducer 无副作用、无网络、无写入,不产生任何权威或状态;卡片只在存在已验证团队预览时出现。
  3. 首屏门禁:本改动不改动首屏、hero、主 CTA 或开场导航(卡片只在有提案时出现),因此不触发首屏评审门禁;这一点已作为"已验证的理由"写在 PR body。
  4. 打包前端:Dashboard bundle 在安装时构建。本 review 之后会在把本机安装提升到该 main 后回读已打包的 chat bundle 是否含新 kind,并把结果补在评论里。
  5. 未做:没有浏览器级端到端点击验收(需要播种一份管家预览 + 起 dev server),smoke 覆盖的是传输与呈现 reducer,不是点击链路;已在 PR body 的 residual gaps 点名。

我的整体评价

正向且 proportional:+281/-1、单一目的,把"已验证的团队预览无法被业主确认"这一真实断点补上,并且把可测的判定逻辑从 React 页面里抽出来单测。

验证:tsc --noEmit -p tsconfig.json 干净;新 smoke 输出 team plan proposal smoke ok;既有 smoke:chat-route、smoke:action-review-plan 通过;canary premerge(内容与本 head 相同)通过,唯一 advisory 为已知基线 maintainability ratchet。

作为作者自有 PR,GitHub 不允许正式 self-approve,故以本 COMMENTED review 作为放行结论。

@huangruiteng
huangruiteng merged commit 9e3901d into main Sep 16, 2026
5 of 6 checks passed
@huangruiteng
huangruiteng deleted the codex/dashboard-team-plan-confirmation branch September 16, 2026 12:33
huangruiteng added a commit that referenced this pull request Sep 16, 2026
…tion (#4548)

The dashboard chat bundle at `loopx/web/chat` is tracked in the repository, so
the source change that lets an owner confirm a validated steward team plan
(#4547) is not shipped until that bundle is rebuilt. This rebuilds it with
`npm run build:chat` and publishes the new generation.

The rebuilt bundle contains the `team.plan` action kind the transport now
accepts, so the shipped chat surface can render a team preview card instead of
failing to parse it. Retention stays bounded: the manifest keeps the new entry
plus the previous generation, and the older generation's JavaScript is removed.

Validated with `python3 examples/dashboard-pwa-bundle-smoke.py`, which checks the
index/manifest contract and the bounded chat asset retention, after the rebuild.

Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Co-authored-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
@huangruiteng

Copy link
Copy Markdown
Collaborator Author

Packaged frontend readback, as promised in the review: loopx/web/chat is a git-tracked bundle, so the source change did not ship on its own. Follow-up shipped in #4548 (rebuilt with npm run build:chat, retention still bounded), the local install was promoted again (release 20260916T123949Z), and the served asset was read back over HTTP: GET /chat/ references /chat/assets/index-Bv3vTPKb.js, and that served file contains team.plan (2 occurrences). The confirmation surface is now live on this machine, not only in source.

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