Skip to content

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

Closed
huangruiteng wants to merge 2 commits into
mainfrom
codex/quota-selection-conflict-diagnostic
Closed

huangruiteng wants to merge 2 commits into
mainfrom
codex/quota-selection-conflict-diagnostic

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.

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 handles 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 I will verify the shipped chat bundle contains the new kind after promoting the local install, and report it in the PR 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.

A guard bound with `--todo-id` whose selection cannot be reconciled with the
current projection used to raise a bare RuntimeError. That surfaced as
`quota_unexpected_collection_error` with the reason "quota collection failed"
and a recommended action pointing at heartbeat receipt writeback, so the real
cause was invisible to the Agent that hit it.

The preflight now raises a typed `QuotaActionSelectionConflictError` that names
the requested Todo, the projection's current selection, and the qualification
state, and that carries its own `error_code` and `recommended_action`:

- `kind=unqualified`: the projection carries no typed qualification at all;
- `kind=conflict`: the requested Todo is neither the current selection nor
  deferred/rejected by it.

`quota_error_code` returns `quota_action_selection_conflict`, and
`quota_failure_payload` reports the conflict as its own `status` with a typed
`action_selection_conflict` block (`kind`, `requested_todo_id`,
`selected_todo_id`, `qualification_state`) instead of the generic collection
failure. The recommended action is a real next read: rerun without `--todo-id`
to see the current selection, then bind that Todo, a deferred Todo, or the Todo
the recovery obligation must settle.

The neighboring typed paths are unchanged: a Todo that is deferred or rejected
by the delivery frontier keeps its existing typed payload, and a qualified
selection for the requested Todo still passes the preflight.

Covered by `tests/control_plane/test_quota_action_selection_conflict.py`: both
raise kinds, the code mapping, the failure payload shape, and the non-conflict
case that must keep returning False.

Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
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

Copy link
Copy Markdown
Collaborator Author

Closed: this branch still carried the already-merged #4545 commits, so the PR re-showed those files. Reopening from a clean branch based on the current main with only the dashboard change.

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