Skip to content

feat(steward): make the steward prompts reachable from the Goal composer - #4591

Merged
huangruiteng merged 9 commits into
mainfrom
codex/steward-prompt-chips
Sep 17, 2026
Merged

huangruiteng merged 9 commits into
mainfrom
codex/steward-prompt-chips

Conversation

@huangruiteng

Copy link
Copy Markdown
Collaborator

Goal And Delivered Outcome

Source: owner feedback on the journey scenario's gap 1 (stewardPrompts defined in the client model but unreachable from the conversation), with the first-screen preview reviewed and approved by the owner before this commit.

Before: the Goal composer offered next-action, progress-report and schedule chips; the steward's blocker and evidence prompts had no surface at all.

After: the existing quick-prompt row carries two more chips, 看阻塞 (Ask what blocks) and 查证据 (Check what is proven), whose prompt text comes from stewardPrompts, and both act immediately like the existing progress-report chip.

Scope And Continuation

Changed surfaces: apps/presentation/dashboard/src/features/personal-workspace/personal-workspace-page.tsx (two chips in the existing row plus one small prompt-text lookup) and i18n.tsx (EN/zh labels). Frontend-only: no protocol, kernel, permission or scoring change.

Deliberately not done:

  • no second prompt row (the first preview stacked two near-identical rows; the owner flagged it and the chips now extend the existing one);
  • no new "ask what's next" chip, because the row already has one;
  • no change to the existing draft chips (询问下一步, 配置定时检查) — if those should also send directly, that is a separate behavior decision.

Follow-up once this lands: flip gap 1 from gap to proven in the journey scenario (PR #4588) and in the product case (PR #4589), both still open.

Public And Private Boundary

No fixture, no local path, no live Goal or agent data. The preview used the existing synthetic browser fixture; screenshots stayed in the gitignored output path.

Validation

  • Browser preview: exactly one .personal-quick-prompts row, no duplicate chip row, labels 询问下一步 / 向 Agent 获取进度报告 / 配置定时检查 / 看阻塞 / 查证据, composer empty after clicking 看阻塞, and the steward gate prompt delivered as a Turn; no client-side errors.
  • tsc --noEmit exit 0; workspace-theme contract: ok.
  • Owner reviewed the first-viewport preview screenshots before the commit.

@huangruiteng

Copy link
Copy Markdown
Collaborator Author

Revision after owner review (562ef9a): the whole quick-prompt row now sends on click and carries no state label.

  • Every prompt chip in both rows sends immediately; the draft/immediate badges, their labels and the draft-only helpers are removed, leaving one neutral chip style (13 insertions / 33 deletions net).
  • Goal row: ask-what's-next, progress report, scheduled check, blockers, evidence. Manager row: tasks and all-Goals progress. Creating a Goal stays an action rather than a message.
  • The scheduled-check chip sends its template as-is; the check target remains the one field the owner supplies in the conversation.
  • workspace-theme.test.mjs encoded the old badge rule, so the assertion now states the simplified contract (one neutral chip style, no badge class).

Preview assertions: one row, labels 询问下一步 / 向 Agent 获取进度报告 / 配置定时检查 / 看阻塞 / 查证据, no badge text, composer empty after clicking, prompt delivered as a Turn, no client errors. tsc --noEmit exit 0 and workspace-theme contract: ok.

@huangruiteng

Copy link
Copy Markdown
Collaborator Author

Follow-up (f6c34d0), found by running the full personal-workspace smoke after the simplification.

Making every chip send broke the durable contract in typed-actions: the scheduled-check chip must still fill the composer, because the check target is the one field the owner supplies and that scenario asserts the draft carries Check target:. The chip now keeps its draft behavior with no badge, so the row stays label-free while only the message prompts send on click.

Validation after the fix:

  • node examples/personal-workspace-browser-smoke.mjs → ok (navigation-sorting, chat-recovery, typed-actions, team-plan, execution-chip, progressive-loading)
  • tsc --noEmit exit 0; workspace-theme contract: ok
  • preview: one row, five clean labels, no badge text, ask-what's-next sends directly, scheduled-check fills a draft containing the check target, no client errors

`stewardPrompts` (ask what's next / what blocks / what is proven) existed in the
client model but no workspace surface rendered it, so an owner had no steward
affordance in the Goal conversation.

Fold the two prompts that are not already covered -- blockers and evidence --
into the existing quick-prompt row instead of adding a second row, and take
their text from `stewardPrompts` so the model stays the single source. "Ask
what's next" is skipped: the row's existing next-action chip already covers it.

The two chips act immediately, matching the existing progress-report chip, so
one click produces an answer instead of a draft the owner must then send.

Validated: `tsc --noEmit` exit 0, `workspace-theme contract: ok`, and a browser
preview asserting exactly one quick-prompt row, no duplicate chip row, the five
labels, an empty composer after clicking, and the steward gate prompt sent as a
Turn. Preview screenshots were reviewed by the owner before this commit.

Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
The quick-prompt row distinguished draft chips from immediate chips with a
label on every chip, so an owner had to read a badge to know whether a click
would send. The row now uses one neutral chip style: every prompt is delivered
on click, the draft/immediate badges and their labels are gone, and the draft
helpers that only existed for those chips are removed.

Goal row: ask-what's-next, progress report, scheduled check, blockers and
evidence all send. Manager row: tasks and all-Goals-progress send; creating a
Goal stays an action, not a message, so it keeps opening the Goal draft.

The scheduled-check chip sends its template as-is; the check target is the one
field the owner still supplies in the conversation.

`workspace-theme.test.mjs` encoded the old badge rule, so it now asserts the
simplified contract: one neutral chip style and no badge class anywhere.

Validated: `tsc --noEmit` exit 0, `workspace-theme contract: ok`, and a browser
preview asserting one row, the five clean labels, no badge text, an empty
composer after clicking, and the prompt delivered as a Turn.

Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Making every quick prompt send broke the durable scenario contract: the
scheduled-check chip must still put its template in the composer, because the
check target is the one field the owner has to supply and typed-actions asserts
the draft carries it. Only the message prompts send on click; the chip keeps no
badge, so the row stays label-free.

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

Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
The scheduled-check shortcut was the last chip that filled the composer
instead of sending, so an owner had to click, read a template with an empty
check target, and press send. It now delivers its bounded request on click
like every other chip: the message names the check target, frequency and
stop condition, so the confirmation card carries a concrete target the owner
can still adjust before anything is created.

`typed-actions.mjs` asserted the old draft contract in both locales; it now
asserts the shortcut maps to one `monitor.create` preview with cadence `2h`,
stop condition `goal_complete`, the localized default check target, and zero
durable writes before confirmation. `personal-workspace-contract.test.mjs`
repeated the draft wording, so its two shortcut assertions now describe the
send-on-click contract. That file is not wired into a smoke runner and still
fails on an unrelated `onOpenGoal` assertion; this change only stops it
encoding the retired draft behavior.

`composer.monitorHint` described the retired draft boundary and is removed
from both locales.

Validated: `tsc --noEmit` exit 0, `workspace-theme contract: ok`, and
`typed-actions` in development mode 3/3 passing.

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

Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
@huangruiteng
huangruiteng force-pushed the codex/steward-prompt-chips branch from 7a5295b to 1f6df5c Compare September 16, 2026 18:48
@huangruiteng

Copy link
Copy Markdown
Collaborator Author

Owner first-screen approval recorded for exact head 1f6df5ce4b18dba5aea04a912e86665d5d7acd91.

  • The owner reviewed the previewed Goal-composer quick-prompt row and confirmed it (确认首屏, 2026-09-17).
  • The preview artifact is the same tree as this head: the first-viewport captures were taken after the tip commit was authored, so the approval is not attached to an earlier revision.
  • Since this changes the Chat frontend and the shipped bundle, the change still needs the owner's merge decision; it is not a docs-only self-merge.

The PR cannot merge yet for a reason unrelated to its diff: the required dashboard-acceptance check is red on main, and it fails checks, pytest and merge-gate for every open PR. The failure is examples/personal-workspace-browser/execution-chip.mjs:221 ("Chat runtime picker ignored the declared steward executor: Chat Codex"); it reproduces on current main in CI and does not reproduce locally, so it is being repaired as its own change before this PR is re-evaluated.

Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
@huangruiteng
huangruiteng force-pushed the codex/steward-prompt-chips branch from 712add5 to a10a93c Compare September 16, 2026 21:56
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
@huangruiteng

Copy link
Copy Markdown
Collaborator Author

Owner first-screen approval re-anchored to the current head eb5eff63478a06f3eaa5413fb09a2c68155c36c3.

The owner reviewed the Goal-composer quick-prompt row previews captured after tip commit 1f6df5ce4b18dba5aea04a912e86665d5d7acd91 and confirmed them (确认首屏, 2026-09-17). The branch has since taken two merges of main (a10a93c88, eb5eff634), so the head moved while the presentation did not:

git diff --stat 1f6df5ce4 eb5eff634 -- apps/presentation/dashboard loopx/web/chat
# (empty)

The source and the shipped loopx/web/chat bundle the previews rendered are byte-identical to the approved revision, so the recorded approval covers eb5eff634 without a new preview round.

What still holds this PR is not the first screen. test-shard (1) failed on this head at tests/test_chat_manager_context.py:149 (AssertionError: assert 7 == 6) because that test builds its six-day fixture from datetime.now(timezone.utc) and so spans seven UTC dates whenever the shard starts between 00:00 and 00:11 UTC — a required-check failure this PR does not cause. PR #4612 repairs that fixture; after it lands, this branch can take one more main merge and the required checks should clear for the unchanged, approved presentation.

huangruiteng added a commit that referenced this pull request Sep 17, 2026
…over (#4612)

`test_turn_context_reads_a_bounded_window_and_declares_sources` built six
days of receipts at `datetime.now(timezone.utc)` minus `(days_ago, minutes=index)`.
When the shard runs between 00:00 and 00:11 UTC the oldest row lands on a
seventh UTC date, so a correct 7-day window reports seven day buckets and the
assertion `len(matched_by_day) == 6` fails. PR #4591's test-shard (1) failed
exactly that way at 2026-09-17T00:08:46Z.

Anchor the newest receipt to 18:00 UTC on the previous UTC day, which keeps
every row in the past and at least eleven minutes inside its own UTC date, so
the fixture exercises the per-day and total bounds instead of the wall clock.

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

Copy link
Copy Markdown
Collaborator Author

Head re-anchored to 18153d15c1aecf0a0705fca269208d74477e3da9 after PR #4612 merged.

#4612 landed the required-check repair (squash merge 6b3264fce): the manager evidence-window fixture no longer spans seven UTC dates when a shard starts before 00:11 UTC, which is what failed this branch's test-shard (1) at tests/test_chat_manager_context.py:149. This branch merged main with a signed commit, so its required checks no longer inherit that failure. The merge brought in exactly that test change plus main's history:

git diff --stat eb5eff634 HEAD -- apps/presentation/dashboard loopx/web/chat
# (empty)

The approved first screen is unchanged by the re-base, so the owner's 确认首屏 approval and the published gate record still describe this head's presentation. Next step is unchanged: settle this PR's merge decision once its checks are green on 18153d15c.

@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.

动机

两件事同时收口:一是 owner 的明确要求“全都改成立即发送,且不用标注『立即发送』,这样界面简洁”;二是本车道记录的 journey gap 1——管家有界提示集只存在于客户端模型里,对话里点不到。这个 PR 让提示行成为真正的入口,而不是要求 owner 先打字或先清草稿。

改动思路

以客户端 stewardPrompts 作为唯一来源,提示行只做投递:每个 chip 点击即发送自己的消息,草稿/立即发送徽标与其对应 i18n 键一起删除,新增“看阻塞”“查证据”两个 chip;定时检查 chip 也从“填草稿”改为发送带本地化默认检查目标的模板,但仍停在既有的 monitor.create 确认卡片,确认前零 durable write。

具体改动

  • i18n:删除 composer.draft、composer.immediate、composer.prepareDraft、composer.monitorHint,新增 composer.blockers、composer.evidence、composer.monitorShortcutTemplate(英文与 zh-CN 同步)。
  • personal-workspace-page.tsx:fillQuickPrompt 换成 sendMessage,选中 Goal 时五个 chip、未选 Goal 时三个 chip,发送中禁用。
  • CSS:移除 .is-draft / .is-immediate / .personal-prompt-badge 及其主题覆盖,改为 button:disabled 态。契约测试与 typed-actions smoke 由“只填草稿”改为断言点击即发送;loopx/web/chat 随包 bundle 重建。

对主干的风险

删除 i18n 键的风险已核实:在该 head 上 git grep "composer\.\(draft\|immediate\|prepareDraft\|monitorHint\)"(排除 bundle 产物)无任何残留引用,唯二命中是无关的 loopx-pw-composer-drafts sessionStorage 键。定时检查语义仍受确认卡约束,不会因为少填草稿就多出 durable write。我在这条分支的 head 上实测:开发态与打包态(从 /chat/ 服务)steward-journey 都通过,beats=4、gaps 只剩 3-confirm 与 4~7,说明“提示行可达并点击即发送”这一拍已从 gap 变为 proven。首屏门禁方面,owner 已在 确认首屏 中确认同一份呈现,重新 base 后 apps/presentation/dashboard 与 loopx/web/chat 与获批树逐字节一致(PR 评论 5706730202)。

我的整体评价

把“界面简洁”的直觉要求和“提示可达”的旅程缺口一次收口,且没有用静默行为换取简洁(定时检查仍走确认)。建议在该 head 的必过检查转绿、owner 结清合并决定后合并。

Approval conclusion (author-owned PR; GitHub blocks formal self-approval)

Head: 18153d1

English verdict: APPROVE

@huangruiteng
huangruiteng merged commit aae37d9 into main Sep 17, 2026
31 checks passed
@huangruiteng
huangruiteng deleted the codex/steward-prompt-chips branch September 17, 2026 03:02
pull Bot pushed a commit to ShinnChow/loopx that referenced this pull request Sep 17, 2026
…ng it

The steward journey recorded its prompt row as gap 1 because the bounded
prompt set lived in the client model while the conversation could not
reach it. PR loopx-project#4591 shipped the row, so the scenario now asserts the
shipped contract: the row must expose the five released labels, clicking
看阻塞 must post its message as an accepted Turn, and the composer must
stay empty afterwards. A regression fails with the missing labels named,
so it cannot pass silently.

Both locale mirrors renumber the remaining gaps and record why the prompt
row is no longer one of them.

Validation: steward-journey in development and packaged modes; the
negative control (row buttons removed) fails naming 看阻塞 / 查证据 as
missing; the full seven-scenario personal-workspace development suite;
workspace-theme contract; docs-governance-smoke; docs-asset-integrity-smoke;
loopx check --scan-path over the three changed files (errors=0, boundary
scan clean).

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