Skip to content

feat(dashboard): choose and review a Goal's working directory - #6297

Open
LIHUA919 wants to merge 5 commits into
loopx-project:mainfrom
LIHUA919:codex/goal-workspace-selection
Open

LIHUA919 wants to merge 5 commits into
loopx-project:mainfrom
LIHUA919:codex/goal-workspace-selection

Conversation

@LIHUA919

Copy link
Copy Markdown
Contributor

Fixes #6231

Delivery brief

  • Goal/source: [Feature]: Allow explicit per-Goal working directory selection in the Dashboard #6231. When creating a Goal in the Web UI, you couldn't pick its local working directory or see which directory the Goal would run in.
  • Gap: the goal.create preview resolved the workspace on the server but kept workspace_ref: "current", so the review said only "Current local workspace". In the reported case, the requested directory appeared only in the boundary prose, while the binding silently resolved to the default workspace. Dashboard-configured roots that no Goal used yet couldn't be chosen in the form, and the selection gate labelled them "Workspace N" with a hash token.
  • Observable result:
    • The Create Goal form offers Dashboard workspaces (the --scan-path roots) next to existing Goal directories.
    • The review's workspace field names the effective directory, its source, and whether the default chose it.
    • Confirming creates the Goal in exactly that directory.
    • The form and the gate explain how to add another directory.
  • Owning boundary: the existing typed action owner (ChatActionService.preview / _project_for_goal_create in loopx/chat_actions.py). No second resolver was added; the frontend only renders the typed projection.
  • Decisive evidence: the packaged browser scenario goal-workspace-selection against a real action/registry backend. It creates a Goal in a configured workspace no Goal used before, reads back the registry repo, and checks the review text for both the explicit and the default case.

Journey (before → after)

Before: open Create Goal → choose "Default workspace" or an existing Goal → review shows "Current local workspace" → confirm. A configured root with no Goal couldn't be chosen in the form, and which directory you'd get wasn't visible until after creation.
After: open Create Goal → choose a Dashboard workspace, an existing Goal directory, or the default → review shows e.g. docs-site · Dashboard workspace or Default: research-monitor · shared with an existing Goal → confirm, and the Goal is created there. Same step count, plus the missing information. Several projects need one Dashboard start (--scan-path A --scan-path B), not a config change per Goal.

Design

  • The confirmed binding is the reviewed binding. The preview replaces workspace_ref with the explicit reference it resolved: an existing Goal id or the configured-root token. Apply resolves that reference through the existing rules, so a later routing change can't move a confirmed creation. A regenerated proposal keeps the bound workspace and asks again if that workspace is gone, rather than picking another.
  • Typed projection, no paths. The proposal gains an optional workspace_binding (loopx_goal_workspace_binding_v0) with workspace_ref, label, source (goal_workspace / configured_workspace), selection (default / explicit) and an optional goal_id. Following the existing "labels without checkout paths" rule for this local API, labels are directory names; a parent directory name is added only when two names collide. A test asserts that no absolute path appears.
  • No authority change. Only server-configured roots and registered Goal workspaces are selectable, as before. Arbitrary path entry stays out of scope, per the issue's non-goals.
  • /api/chat/projects (which already lists the same roots) adds goal_workspace_ref, so the form needs no second workspace listing. Its title uses the same collision-safe label; that only differs from Path.name when two roots share a name.
  • The token rule now lives in one helper (goal_workspace_ref). It replaces three inline copies, including the steward commission path.

Vocabulary

workspace_binding.source and .selection are a new local projection vocabulary owned by loopx/chat_actions.py, mirrored only as a decoding schema in apps/presentation/dashboard/src/data/chat.ts. They don't extend a registered vocabulary. generate_semantic_inventory.py --changed-from HEAD reports no supported carriers, which is expected for plain string literals.

Validation

  • tests/test_chat_goal_workspace_selection.py: 12 passed. The 6 new tests cover the default binding via the context Goal, the single Goal and the single root (including re-resolution of the bound ref and no path leakage), explicit configured selection, and collision-safe directory labels with the --scan-path hint.
  • tests/test_chat_ordinary_project.py now asserts goal_workspace_ref on /api/chat/projects.
  • Related Python suites (chat actions, chat server, steward, projects; 1,960 tests):
    • Every failure from these runs was re-run on its own on both branch and main, with identical results.
    • test_goal_attention.py::test_steward_turn_receives_blocker_and_decision_without_channel fails on unmodified main too; it's pre-existing.
    • 4 others (test_completed_cli_creation_replays_without_compatibility_source[unreadable-hard_lease-file], test_writable_project_turns_use_project_skills_without_manager_work_selection[messages0], test_chat_preview_preserves_canonical_actor_refusal[file], test_source_owner_capture_and_real_machine_owner_readback) failed only under parallel load and pass on their own on both.
  • Dashboard tsc --noEmit is clean; chat_bundle.py build --install and verify --source pass.
  • Packaged browser scenarios (LOOPX_PERSONAL_WORKSPACE_PACKAGED=1): goal-workspace-selection, goal-draft, private-steward-scope, conversation-input, typed-actions and proposal-readback-scope all pass.
  • Mutation: with workspace_binding withheld, goal-workspace-selection fails at the review-name assertion.
  • examples/loopx-chat-actions-smoke.py ok. ruff clean. loopx check --scan-path docs/guides/personal-workspace-user-guide.md reports a clean public-boundary scan.
  • Not run: the installed App, a live model. The scenario's status fixtures and model dispatch are synthetic.

Visual evidence

Screenshots from the packaged scenario (zh-CN) were reviewed locally. They show the form with the grouped "工作目录" select and the shortened help line, and the review drawer reading 执行工作区: docs-site · Dashboard 工作区(仅提供执行环境,不会自动关联仓库). The mobile layout (390px) still fits the dialog. This doesn't change any first-screen or hero surface.

Surfaces

  • Frontend: the Create Goal form, the review drawer, and the workspace choices when selection is ambiguous.
  • Backend: goal.create preview and /api/chat/projects.
  • Docs: the "choose a working directory" section of the personal workspace user guide (zh/en).
  • No CLI or Lark change. Lark review cards don't render workspace_ref.
  • This is a control-plane/product change, so it's left for maintainer review and merge.

Future-facing pass

Applied: one Goal-workspace token helper replaces three inline copies. Deferred: project conversations (project_ref, sha256[:24]) and Goal creation (workspace- + sha256[:12]) still use two token schemes for the same roots. Unifying them touches persisted creation checkpoints and steward bindings, so it's a separate change.

🤖 Generated with Claude Code

Lihua and others added 3 commits October 11, 2026 23:27
…es to

A goal.create preview resolved its workspace on the server but returned
"current", so the owner reviewed "Current local workspace" without
seeing which directory the Goal would use, and workspace choices were
labelled "Workspace N" with an opaque token.

The preview now replaces the requested workspace_ref with the explicit
reference it resolved (an existing Goal id or a configured workspace
token). Apply then creates the Goal in exactly the reviewed workspace.
The proposal also carries a typed workspace_binding: the directory
name, whether it is a configured workspace or shared with a Goal, and
whether the default chose it. Checkout paths are never included.

Workspace choices are named by directory, with the parent directory
name added only when two names collide. The selection gate explains
how to add a directory with --scan-path. /api/chat/projects also
returns each root's Goal-creation reference so the form can offer it.
The steward commission path reuses the shared token helper.

Refs loopx-project#6231

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Lihua <t96vj6547t@privaterelay.appleid.com>
The Create Goal form now offers the Dashboard's configured workspaces
next to existing Goal directories. The review names the effective
directory, its source and whether the default chose it, from the
proposal's workspace_binding. Workspace choices show directory names
instead of tokens. The form explains how to add a directory with
--scan-path.

The packaged browser scenario now creates a Goal in a configured
workspace no Goal used yet and checks the registry. It also checks
that the default names the directory it resolves to before anything
is created.

Refs loopx-project#6231

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Lihua <t96vj6547t@privaterelay.appleid.com>
Describe the three working-directory choices, what the review shows,
and how to set up several projects once with repeated --scan-path.

Fixes loopx-project#6231

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Lihua <t96vj6547t@privaterelay.appleid.com>

@loopx-agent loopx-agent left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewer: model_agent · gpt-6.1-sol · OpenAI · runtime_reported · reasoning_effort=xhigh

Reviewed exact head: 5beca76

动机

在一个 Dashboard 中为多个本地项目创建 Goal、需要在确认前核对工作目录的用户。
此前不能选择尚未被 Goal 使用的已配置目录,默认选择的预览也未明确写出解析结果;本批将这些目录加入选择,并显示固定引用的预览。
已验证已有 Goal 目录和独立 Dashboard 目录的真实创建、失效后恢复与默认路由变更拒绝;同名已有目录的确认展示仍无法区分。
范围是本地新 Goal 的目录选择,不包括任意路径输入、文件管理、已有 Goal 迁移或新增执行权限。
当前仍需补齐已有 Goal 目录的同名区分,以及随源码变化更新的注册表 I/O 清单。

改动思路

复用现有项目列表、Goal 创建动作、确认和注册表校验;工作区绑定是解析结果的展示与具体引用,不另建权限或目录状态。
这是一条可独立验证和回滚的本地创建路径;真正写入仍由既有动作 owner 执行,目录选择不替代执行权限确认。
本次按原请求和修改前的契约建立判断依据。预览/提案、作用执行、持久读回分别沿用原 owner;派生标签或向量不能替代授权事实。既有状态能推导的信息不另建手工维护的事实,选择目的地或承诺下一探针则由明确的用户/Agent意图产生。相关前瞻整理已检查:共享 workspace_ref helper 已替代三处重复 hash,保留 project_ref 的既有独立会话 token。

具体改动

规范依据:https://github.com/loopx-project/loopx/issues/6231,判断版本 2026-10-11T10:57:16Z / issue 5800421149。这五个 Proposed outcome 条目没有编号,以下使用条目名称,不创造新的验收编号。

  • Proposed outcome / explicit directory selection:已验证对应 caller 与 owner。
  • Proposed outcome / distinguish effective workspace:尚未满足,见本次阻塞 finding。
  • Proposed outcome / unavailable directory:已验证对应 caller 与 owner。
  • Proposed outcome / multiple directories:已验证对应 caller 与 owner。
  • Proposed outcome / documentation:已验证对应 caller 与 owner。

关键代码讲解

  • loopx/chat_actions.py:540 ChatActionService._goal_workspace_binding:Resolve concrete reference and derived preview label; collision scope currently omits registered Goal roots.
  • loopx/chat_actions.py:1158 ChatActionService.preview goal.create branch:Resolve current once and store the concrete workspace_ref, still using registry fingerprint.
  • apps/presentation/dashboard/src/features/personal-workspace/personal-workspace-page.tsx:523 workspaceBindingText:Render binding label/source/default; hides selected source Goal identity.
  • apps/presentation/dashboard/src/features/personal-workspace/workspace-action-form.tsx:44 WorkspaceActionForm workspaceRef:Offer configured and existing Goal choices while preserving ordinary confirmation.

对主干的风险

23 项相关 Python 测试通过;TypeScript 检查及正常 Chat 打包通过。Ego 查看1280px确认与390px表单,验证503失败保留文本/选择、选定目录 unavailable 后拒绝、恢复后继续;已有 Goal 目录与尚未被 Goal 使用的 docs-site 各实际创建一次,注册表目的地与 projection_verified 回执一致。默认引用在预览中固定;注册目录改变后 apply 返回409,持久 proposal 为 stale,未创建改道 Goal。全景检查和真实后端结果证明这些已实现分支,不证明同名目录可区分。五项直接检查通过,16项原生执行检查中15项通过、1项 census 检查失败;未查询或等待 CI。周边 status/discovery 与首轮模型 dispatch 为合成,创建动作、bootstrap、存储与注册表为真实隔离 owner。

[P2] 确认前区分同名的已有 Goal 目录(loopx/chat_actions.py:551)。当两个已有 Goal 分别绑定 first/project 与 second/project,而 Dashboard 另配置 docs-site 时,两份预览的 label 都为 project;页面将 source Goal 身份隐藏,两个执行工作区都显示 project · shared with an existing Goal。直接 API 与打包确认卡都复现,确认前无法判断区别。请对全部注册 Goal 目录做同名区分,或在执行工作区保留来源 Goal 身份;不要泄漏完整路径。增加两份可区分确认卡和真实落盘目的地回归。

[P2] 同步源码后的注册表 I/O 清单(loopx/chat_actions.py:540)。新增 helper 与服务器代码改变了三个注册表读取站点的源位置,但未更新 project_registry_io_manifest_v1.json。同一 semantic-vocabulary-drift-smoke 在不可变基线通过,在当前 head 因 ChatActionService._registry 及两个 serve_chat 读取站点元数据改变而失败。请用 uv run python scripts/generate_project_registry_io_manifest.py 生成并审阅清单,保留原分类,再执行 uv run --extra test python examples/semantic-vocabulary-drift-smoke.py 和原生 premerge。

语义与 CI 对齐

当前要求是源码与受审 registry I/O census 同步;插入的代码令 head 的三个元数据站点过期。该失败归因于本 PR,不能用测试总数或缺少旧失败记录豁免;基线比较已执行。开发期 advisory 未发现受支持的新容器语法,不覆盖这项可选跨端绑定或动态构造;完整检查已经暴露实际缺口。
独立合并门与 maintainer 授权仍需另行满足;本次没有执行合并。

我的整体评价

long_horizon=preserved:实际持久创建和 stale 拒绝保持既有作用与重试边界;user_experience=regression:新确认字段隐藏可区分的来源 Goal,使同名不同目录的确认不充分。
交付判断为 justified_increment,但当前边界包含已复现的阻塞缺陷,整体 REQUEST_CHANGES。机制与规模适配原问题,不能因为局部 happy path 或绿色测试覆盖这些反例。请在同一 owner 修正并提交新 head,再重验完整受影响路径;本次不要求未承诺的父级研究/恢复/迁移工作一起完成。

English verdict: REQUEST_CHANGES - Disambiguate registered Goal destinations in confirmation and refresh the changed registry I/O census.

@mergify

mergify Bot commented Oct 12, 2026

Copy link
Copy Markdown

This pull request has merge conflicts with main and cannot be merged
until they are resolved. Please rebase or merge the base branch, @LIHUA919.

Choose the remote for the base repository, not an out-of-date fork.
For a fork clone, first inspect git remote -v; upstream must point
to https://github.com/loopx-project/loopx.git. If it is absent, add it
with git remote add upstream https://github.com/loopx-project/loopx.git.
Then run:

git fetch upstream
git rebase upstream/main
# Resolve each conflict, git add the resolved files, then git rebase --continue.
git push --force-with-lease origin HEAD

For a same-repository clone whose origin points to
https://github.com/loopx-project/loopx.git, use origin instead of
upstream for fetch/rebase. If you prefer merging the base, use
git merge <base-remote>/main and push normally.

Keep the DCO Signed-off-by trailer on every commit when you rebase.
https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork

@mergify mergify Bot added the needs-rebase Mergify: the pull request has merge conflicts with its base branch label Oct 12, 2026
Lihua and others added 2 commits October 12, 2026 13:58
Workspace labels were made unique only among the Dashboard's configured
roots, so two existing Goals in .../first/project and .../second/project
both reviewed as "project". Labels are now computed over configured
roots and registered Goal directories together. Parent names are added
one level at a time only while two directories still read the same,
and the filesystem anchor is never shown. /api/chat/projects uses the
same names.

Also regenerate the project registry I/O manifest for the moved read
sites (line numbers only; classifications unchanged). Give the browser
scenario's backend PYTHONPATH like the other Python-backed scenarios;
without it, CI could not import loopx. Update the guide's wording.

Refs loopx-project#6231

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Lihua <t96vj6547t@privaterelay.appleid.com>
Keep main's shared todo request helper and Python backend launcher; the
branch's PYTHONPATH workaround is dropped in favour of the launcher.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Lihua <t96vj6547t@privaterelay.appleid.com>
@LIHUA919

Copy link
Copy Markdown
Contributor Author

Both review findings are addressed on head 0e6a44c21.

[P2] Same-named existing Goal directories in confirmation: fixed in a4e0c5052.

  • Labels are now computed over configured roots and registered Goal directories together (ChatActionService.workspace_labels). Previously only configured roots were considered.
  • goal_workspace_labels adds parent names one level at a time, only while two different directories still read the same. It never shows the filesystem anchor or a full path. /api/chat/projects uses the same names, so the form and the review agree.
  • Regressions:
    • test_same_named_goal_directories_review_apart: Goals in first/project and second/project plus a configured docs-site review as first/project and second/project, and each bound ref re-resolves to its own directory.
    • test_labels_add_parents_only_until_directories_differ: three-level collisions, no anchor.
    • The packaged browser scenario's two fixture Goals already share the name project. It now asserts that the confirmation card reads second/project · 与已有 Goal 共用 (and not the first/project card), then confirms and reads back the registry repo.

[P2] Registry I/O manifest: regenerated with scripts/generate_project_registry_io_manifest.py. Only the three flagged sites' line numbers changed; classifications are unchanged and there are 0 unclassified direct sites. The manifest is still correct after merging origin/main. examples/semantic-vocabulary-drift-smoke.py passes.

CI: the chat-bundle-browser / dashboard-acceptance failures were the scenario backend failing to import loopx. main has since fixed that with the shared launcher in a6886c4, so the merge keeps that and drops my interim PYTHONPATH workaround. The test-shard (2) failures (coordination-shadow, quota scheduler-ack, sqlite lifecycle) reproduce identically on the base commit without this PR. The windows-powershell failure is a note-encoding assertion outside these files.

Validation on the merged head:

  • loopx canary premerge --from-git-diff --git-diff-base origin/main passed all four sections with 0 failures, 0 skips and no manual holds. I ran it on the pre-merge commit; the merged head was then re-checked with the commands below.
  • Dashboard tsc --noEmit is clean; the chat bundle builds and verifies.
  • Packaged goal-workspace-selection, typed-actions and goal-draft scenarios pass.
  • The focused Python tests pass (25).
  • Related and architecture suites before the merge: 3,342 passed. The 3 failures and errors that appeared only under parallel load pass on their own. test_goal_attention.py::test_steward_turn_receives_blocker_and_decision_without_channel also fails on unmodified main.

@mergify mergify Bot removed the needs-rebase Mergify: the pull request has merge conflicts with its base branch label Oct 12, 2026

This branch has not been deployed

No deployments
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.

[Feature]: Allow explicit per-Goal working directory selection in the Dashboard

2 participants