diff --git a/.agents/skills/change-implementation-plan/SKILL.md b/.agents/skills/change-implementation-plan/SKILL.md index e6bbaa55..74386fe5 100644 --- a/.agents/skills/change-implementation-plan/SKILL.md +++ b/.agents/skills/change-implementation-plan/SKILL.md @@ -1,6 +1,6 @@ --- name: change-implementation-plan -description: Use when drafting, revising, or persisting an implementation plan for a feature, bug fix, refactor, documentation update, or other repository change. Guides agents to write English plans with Background, Goals, Implementation Plan, Acceptance Criteria, and Validation Commands, and to save every implementation plan under docs/plans/ with a timestamped filename. +description: Use when drafting, revising, or persisting an implementation plan for a feature, bug fix, refactor, documentation update, or other repository change. Guides agents to write English plans with Background, Goals, Implementation Plan, Acceptance Criteria, and Validation Commands, and to save every implementation plan under the appropriate docs/tasks lifecycle folder with a timestamped filename. --- # Change Implementation Plan @@ -11,7 +11,12 @@ This skill is for implementation planning. Do not use it for GitHub issue drafti ## Persistence Rule -Every implementation plan must be saved under `docs/plans/`. +Every implementation plan must be saved under the appropriate `docs/tasks/` lifecycle folder. + +- Use `docs/tasks/backlog/` for planned work that has not started. +- Use `docs/tasks/doing/` for active implementation work. +- Use `docs/tasks/done/` for completed work. +- Use `docs/tasks/trash/` for abandoned or superseded work. Name the file with a 24-hour timestamp prefix: @@ -91,7 +96,7 @@ Write the plan in English using these sections, in this order: Before returning or saving the plan, check: -- The file is saved under `docs/plans/` with a timestamped filename. +- The file is saved under the correct `docs/tasks/` lifecycle folder with a timestamped filename. - The plan includes `Background`, `Goals`, `Implementation Plan`, `Acceptance Criteria`, and `Validation Commands`. - Optional Background subsections are used only when they improve clarity. - Acceptance criteria describe outcomes, not internal implementation steps. diff --git a/.gitignore b/.gitignore index 51b439e0..7ab4caae 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,8 @@ .history .cache/ .idea/ +.antigravitycli/ +.agents/skills/source-command-opsx-*/ # IntelliJ/Melos generated files *.iml diff --git a/AGENTS.md b/AGENTS.md index af47f782..6d97a428 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -6,9 +6,11 @@ - Run mobile app Flutter tests from the package directory: `cd apps/mobile_chat_app && flutter test` (avoid `flutter test apps/mobile_chat_app` from repo root). - Never pass Markdown files (for example `*.md`) to `dart format` or other Dart source-only tooling commands; target only Dart files/directories. -## Plan persistence -- For each feature or task, create and save a markdown plan file in `docs/plans/`. -- Name plan files with a `YYYY-MM-DD-HH-mm` prefix using 24-hour time (optionally appending a timezone such as `-UTC`). +## Task lifecycle +- For each feature or task, create and save a markdown task file under `docs/tasks/`. +- Use the lifecycle folders: `docs/tasks/backlog/` for planned or not-yet-started work, `docs/tasks/doing/` for active work, `docs/tasks/done/` for completed work, and `docs/tasks/trash/` for abandoned or superseded work. +- Name task files with a `YYYY-MM-DD-HH-mm` prefix using 24-hour time (optionally appending a timezone such as `-UTC`). +- Do not add new files to the legacy plan/backlog folders; use `docs/tasks/` instead. ## Worktree task workflow - The repository may contain a top-level `.worktree/` directory for storing local git worktrees. diff --git a/README.md b/README.md index bdb3f118..a5826355 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,46 @@ **The agent console for tinkerers.** -- Connect your OpenClaw and run multiple agent threads side by side -- Manage your todo lists and data tables (like Notion) through conversation -- Fully open-source: frontend, backend, iOS, and Android +Bricks helps you run AI work across conversations, OpenClaw nodes, and durable workspace resources. + +## What Bricks does today + +- Capture useful information from chat as todos, tables, notes, and highlights +- Keep different topics separated with channels and threads, like Discord +- Connect OpenClaw and route messages to specific agent nodes +- Configure AI model providers, channel instructions, thread instructions, and node tokens +- Automate recurring agent tasks on a schedule +- Fully open source: Flutter app, Node backend, mobile clients, docs, and plugin runtime + +## What you can save + +Bricks keeps useful information available after the chat moves on. + +- **Todo Lists** - Project checklists, follow-up tasks, meeting actions, and homework plans. +- **Tables** - Comparison matrices, research trackers, lead lists, bug triage, and planning data. +- **Notes** - Research summaries, reusable instructions, generated reports, study notes, and specs. +- **Highlights** - Important facts, decisions, quotes, references, and snippets worth keeping. + +## Showcase + +### Topic-based agent work + +![Bricks channel and thread workspace](docs/assets/showcase/chat-knowledge-organization.png) + +### Saved highlights and notes + +![Bricks resources for highlights and notes](docs/assets/showcase/resources-highlights.png) + +## What's on the roadmap + +- Richer note content, including purpose-specific charts, diagrams, and examples +- Grammar issue collection for review, reuse, and analysis +- Website artifacts for saving generated pages and interactive previews +- Charts for turning structured data into visual summaries +- Motion outputs for animation, timing, and visual storytelling +- GitHub integration for issues, pull requests, and repository workflows + +[Tell us your story or request a feature](https://github.com/askman-dev/bricks/issues/new) ## Documentation diff --git a/apps/docs_site/sidebars.ts b/apps/docs_site/sidebars.ts index bf353c44..1bec5b6c 100644 --- a/apps/docs_site/sidebars.ts +++ b/apps/docs_site/sidebars.ts @@ -34,11 +34,11 @@ const sidebars: SidebarsConfig = { }, { type: 'category', - label: 'Plans', + label: 'Tasks', items: [ - 'plans/2026-04-26-10-20-UTC-docusaurus-doc-ia-restructure', - 'plans/2026-04-21-07-57-UTC-readme-refresh-consolidated-plan', - 'plans/2026-04-08-02-40-UTC-code-map-foundation', + 'tasks/done/2026-04-26-10-20-UTC-docusaurus-doc-ia-restructure', + 'tasks/done/2026-04-21-07-57-UTC-readme-refresh-consolidated-plan', + 'tasks/done/2026-04-08-02-40-UTC-code-map-foundation', ], }, ], diff --git a/docs/assets/showcase/chat-knowledge-organization.png b/docs/assets/showcase/chat-knowledge-organization.png new file mode 100644 index 00000000..b49053fa Binary files /dev/null and b/docs/assets/showcase/chat-knowledge-organization.png differ diff --git a/docs/assets/showcase/resources-highlights.png b/docs/assets/showcase/resources-highlights.png new file mode 100644 index 00000000..475ffbde Binary files /dev/null and b/docs/assets/showcase/resources-highlights.png differ diff --git a/docs/backlog/2026-05-26-scheduled-agent-tasks.md b/docs/backlog/2026-05-26-scheduled-agent-tasks.md deleted file mode 100644 index 54b6586d..00000000 --- a/docs/backlog/2026-05-26-scheduled-agent-tasks.md +++ /dev/null @@ -1,13 +0,0 @@ -# Scheduled Agent Tasks - -## Confirmed Need - -Users should be able to create scheduled tasks through conversation. - -When a scheduled task is triggered, it should run an Agent workflow. - -## Notes - -- The task creation entry point is conversational. -- The scheduled trigger is responsible for starting the Agent workflow. -- Superseded for implementation details: see `docs/plans/2026-05-26-18-34-CST-scheduled-actions-agent-execution.md`, which is the current source of truth. diff --git a/docs/backlog/2026-05-26-scriptable-agent-pipelines.md b/docs/backlog/2026-05-26-scriptable-agent-pipelines.md deleted file mode 100644 index 7b087f37..00000000 --- a/docs/backlog/2026-05-26-scriptable-agent-pipelines.md +++ /dev/null @@ -1,18 +0,0 @@ -# Scriptable Agent Pipelines - -## Confirmed Need - -Bricks should support scriptable Agent pipelines that can run around a user message or an Agent response. - -The pipeline should be able to execute code, call tools, transform input, and then continue the Agent flow. - -## Example Use Cases - -- Before sending a message, check grammar, rewrite the query in English, and send the improved message to the AI. -- Before sending a message, optimize or enrich the user query. -- Use a framework such as Genkit as a possible way to define AI workflows in TypeScript. - -## Notes - -- The feature is about scriptable hooks or flows, not only prompt templates. -- Exact trigger points, permissions, storage, and runtime model are not decided yet. diff --git a/docs/code_maps/logic_map.yaml b/docs/code_maps/logic_map.yaml index 16e7ae0c..f272ed97 100644 --- a/docs/code_maps/logic_map.yaml +++ b/docs/code_maps/logic_map.yaml @@ -23,10 +23,10 @@ index: - tools/evidence/channel_dropdown_height/run.sh - tools/evidence/channel_dropdown_height/channel_dropdown_flow.mjs doc_index: - - docs/plans/2026-05-20-11-13-CST-local-cloud-db-debug.md - - docs/plans/2026-05-21-10-41-CST-channel-new-ui-harness.md - - docs/plans/2026-05-21-12-03-CST-evidence-checkpoint-testing-docs.md - - docs/plans/2026-05-21-12-38-CST-local-manual-ai-test-env-docs.md + - docs/tasks/done/2026-05-20-11-13-CST-local-cloud-db-debug.md + - docs/tasks/done/2026-05-21-10-41-CST-channel-new-ui-harness.md + - docs/tasks/done/2026-05-21-12-03-CST-evidence-checkpoint-testing-docs.md + - docs/tasks/done/2026-05-21-12-38-CST-local-manual-ai-test-env-docs.md - docs/testing/local-chat-fixture-db.md - docs/testing/evidence-checkpoint-browser-harness.md - docs/testing/local-manual-ai-test-env.md @@ -127,26 +127,26 @@ index: - docs/architecture.md - docs/architecture/system-overview.md - openspec/changes/multi-agent-participation/design.md - - docs/plans/2026-04-18-00-20-UTC-openclaw-message-routing-current-state.md - - docs/plans/2026-04-23-17-13-WIB-openclaw-agents-capture-display.md - - docs/plans/2026-04-23-15-33-openclaw-selected-node-routing.md - - docs/plans/2026-04-24-12-15-dispatch-avatar-placeholder.md - - docs/plans/2026-04-26-02-45-UTC-jump-to-latest-message.md - - docs/plans/2026-04-26-02-45-dark-theme-semantic-token-refactor.md + - docs/tasks/done/2026-04-18-00-20-UTC-openclaw-message-routing-current-state.md + - docs/tasks/done/2026-04-23-17-13-WIB-openclaw-agents-capture-display.md + - docs/tasks/done/2026-04-23-15-33-openclaw-selected-node-routing.md + - docs/tasks/done/2026-04-24-12-15-dispatch-avatar-placeholder.md + - docs/tasks/done/2026-04-26-02-45-UTC-jump-to-latest-message.md + - docs/tasks/done/2026-04-26-02-45-dark-theme-semantic-token-refactor.md - docs/kb/color-theme-architecture.md - .codex/skills/bricks-design-system/SKILL.md - - docs/plans/2026-04-26-23-24-+08-chat-dark-colors.md - - docs/plans/2026-04-27-00-19-+08-design-system-token-alignment.md - - docs/plans/2026-04-27-00-27-+08-design-system-skill.md - - docs/plans/2026-04-27-17-28-UTC-markdown-table-rendering.md - - docs/plans/2026-05-15-16-21-+08-chat-scroll-position-gwt.md - - docs/plans/2026-05-15-16-54-+08-local-chat-fixture-db.md - - docs/plans/2026-05-16-17-58-+08-mobile-agents-directory-setup-fix.md - - docs/plans/2026-05-16-23-21-+08-ios-sandbox-path-without-home.md - - docs/plans/2026-05-19-13-02-CST-highlight-list-and-auth-api-architecture.md - - docs/plans/2026-05-19-14-27-CST-tool-call-thinking-group.md - - docs/plans/2026-05-19-17-38-CST-subsection-rename-menu.md - - docs/plans/2026-05-21-10-41-CST-channel-new-ui-harness.md + - docs/tasks/done/2026-04-26-23-24-+08-chat-dark-colors.md + - docs/tasks/done/2026-04-27-00-19-+08-design-system-token-alignment.md + - docs/tasks/done/2026-04-27-00-27-+08-design-system-skill.md + - docs/tasks/done/2026-04-27-17-28-UTC-markdown-table-rendering.md + - docs/tasks/done/2026-05-15-16-21-+08-chat-scroll-position-gwt.md + - docs/tasks/done/2026-05-15-16-54-+08-local-chat-fixture-db.md + - docs/tasks/done/2026-05-16-17-58-+08-mobile-agents-directory-setup-fix.md + - docs/tasks/done/2026-05-16-23-21-+08-ios-sandbox-path-without-home.md + - docs/tasks/done/2026-05-19-13-02-CST-highlight-list-and-auth-api-architecture.md + - docs/tasks/done/2026-05-19-14-27-CST-tool-call-thinking-group.md + - docs/tasks/done/2026-05-19-17-38-CST-subsection-rename-menu.md + - docs/tasks/done/2026-05-21-10-41-CST-channel-new-ui-harness.md - docs/testing/local-chat-fixture-db.md test_index: - apps/mobile_chat_app/test/chat_arbitration_engine_test.dart @@ -285,8 +285,8 @@ index: - apps/node_backend/src/services/chatRouterService.ts - apps/node_backend/src/db/migrations/013_add_instructions_to_chat_scope_settings.sql doc_index: - - docs/plans/2026-04-30-17-51-+08-scope-instructions-config.md - - docs/plans/2026-05-19-13-02-CST-highlight-list-and-auth-api-architecture.md + - docs/tasks/done/2026-04-30-17-51-+08-scope-instructions-config.md + - docs/tasks/done/2026-05-19-13-02-CST-highlight-list-and-auth-api-architecture.md test_index: - apps/mobile_chat_app/test/chat_history_api_service_test.dart - apps/node_backend/src/services/chatRouterService.test.ts @@ -320,7 +320,7 @@ index: - apps/node_backend/src/services/chatRouterService.ts - apps/node_backend/src/db/migrations/013_add_instructions_to_chat_scope_settings.sql doc_index: - - docs/plans/2026-04-30-17-51-+08-scope-instructions-config.md + - docs/tasks/done/2026-04-30-17-51-+08-scope-instructions-config.md test_index: - apps/mobile_chat_app/test/chat_history_api_service_test.dart - apps/node_backend/src/services/chatRouterService.test.ts @@ -357,7 +357,7 @@ index: - docs/model_settings_plan.md - docs/architecture.md - docs/openclaw_pull_only_integration_dev_doc.md - - docs/plans/2026-05-19-13-02-CST-highlight-list-and-auth-api-architecture.md + - docs/tasks/done/2026-05-19-13-02-CST-highlight-list-and-auth-api-architecture.md test_index: - apps/mobile_chat_app/test/model_settings_screen_test.dart - apps/mobile_chat_app/test/llm_config_service_test.dart @@ -391,7 +391,7 @@ index: doc_index: - docs/product/overview.md - docs/integrations/openclaw-plugin.md - - docs/plans/2026-04-21-00-12-UTC-multi-node-plugin-implementation.md + - docs/tasks/done/2026-04-21-00-12-UTC-multi-node-plugin-implementation.md test_index: - apps/mobile_chat_app/test/node_settings_screen_test.dart - apps/node_backend/src/routes/config.test.ts @@ -427,7 +427,7 @@ index: - docs/product/overview.md - docs/faq/common-issues.md - docs/architecture.md - - docs/plans/2026-05-19-17-06-CST-navigation-resources-highlights.md + - docs/tasks/done/2026-05-19-17-06-CST-navigation-resources-highlights.md test_index: - apps/mobile_chat_app/test/app_test.dart - apps/mobile_chat_app/test/chat_navigation_page_test.dart @@ -502,11 +502,11 @@ index: - docs/integrations/openclaw-plugin.md - docs/architecture.md - docs/openclaw_pull_only_integration_dev_doc.md - - docs/plans/2026-04-18-00-20-UTC-openclaw-message-routing-current-state.md - - docs/plans/2026-04-23-17-13-WIB-openclaw-agents-capture-display.md - - docs/plans/2026-04-23-15-33-openclaw-selected-node-routing.md - - docs/plans/2026-05-06-22-42-+08-router-strategy-rename.md - - docs/plans/2026-05-06-22-44-+08-post-router-refactor-followup.md + - docs/tasks/done/2026-04-18-00-20-UTC-openclaw-message-routing-current-state.md + - docs/tasks/done/2026-04-23-17-13-WIB-openclaw-agents-capture-display.md + - docs/tasks/done/2026-04-23-15-33-openclaw-selected-node-routing.md + - docs/tasks/done/2026-05-06-22-42-+08-router-strategy-rename.md + - docs/tasks/done/2026-05-06-22-44-+08-post-router-refactor-followup.md - apps/node_backend/src/routes/platform.test.ts - apps/node_backend/src/routes/config.test.ts - apps/node_backend/src/services/chatRouterService.test.ts @@ -565,7 +565,7 @@ index: - docs/faq/common-issues.md - docs/openclaw_pull_only_integration_dev_doc.md - docs/plugin_development_architecture.md - - docs/plans/2026-04-23-17-13-WIB-openclaw-agents-capture-display.md + - docs/tasks/done/2026-04-23-17-13-WIB-openclaw-agents-capture-display.md - apps/node_openclaw_plugin/README.md - apps/node_openclaw_plugin/openclaw.plugin.json test_index: @@ -601,7 +601,7 @@ index: - docs/intro.md - docs/get-started/quickstart.md - docs/product/overview.md - - docs/plans/2026-04-26-12-19-docs-subpath-vercel-integration.md + - docs/tasks/done/2026-04-26-12-19-docs-subpath-vercel-integration.md test_index: - tools/vercel-build.sh keywords: @@ -629,8 +629,8 @@ index: - apps/mobile_chat_app/lib/features/chat/todo_api_service.dart - apps/mobile_chat_app/lib/services/authenticated_api_client.dart doc_index: - - docs/plans/2026-05-16-02-20-+00-todos-tables-highlights-tools.md - - docs/plans/2026-05-19-13-02-CST-highlight-list-and-auth-api-architecture.md + - docs/tasks/done/2026-05-16-02-20-+00-todos-tables-highlights-tools.md + - docs/tasks/done/2026-05-19-13-02-CST-highlight-list-and-auth-api-architecture.md test_index: - apps/node_backend/src/services/localAgentLoopService.test.ts keywords: @@ -663,7 +663,7 @@ index: - apps/mobile_chat_app/lib/features/chat/chat_message.dart - apps/mobile_chat_app/lib/services/authenticated_api_client.dart doc_index: - - docs/plans/2026-06-16-15-32-+08-note-content-type-implementation.md + - docs/tasks/done/2026-06-16-15-32-+08-note-content-type-implementation.md - docs/tasks/done/2026-06-12-14-00-+08-note-content-type.md test_index: - apps/node_backend/src/routes/resources.test.ts @@ -703,9 +703,9 @@ index: - apps/mobile_chat_app/lib/features/chat/asset_table_api_service.dart - apps/mobile_chat_app/lib/services/authenticated_api_client.dart doc_index: - - docs/plans/2026-05-16-02-20-+00-todos-tables-highlights-tools.md - - docs/plans/2026-05-17-00-04-+08-table-tool-error-and-turso-compatibility.md - - docs/plans/2026-05-19-13-02-CST-highlight-list-and-auth-api-architecture.md + - docs/tasks/done/2026-05-16-02-20-+00-todos-tables-highlights-tools.md + - docs/tasks/done/2026-05-17-00-04-+08-table-tool-error-and-turso-compatibility.md + - docs/tasks/done/2026-05-19-13-02-CST-highlight-list-and-auth-api-architecture.md - docs/kb/turso-sqlite-compatibility.md test_index: - apps/node_backend/src/services/localAgentLoopService.test.ts @@ -745,12 +745,12 @@ index: - apps/mobile_chat_app/lib/features/chat/chat_navigation_page.dart - apps/mobile_chat_app/lib/features/chat/widgets/message_list.dart doc_index: - - docs/plans/2026-05-16-02-20-+00-todos-tables-highlights-tools.md - - docs/plans/2026-05-18-22-22-+08-highlight-selection-offsets.md - - docs/plans/2026-05-19-10-50-+08-unified-authenticated-api-client.md - - docs/plans/2026-05-19-11-55-+08-highlight-toolbar-union-ranges.md - - docs/plans/2026-05-19-13-02-CST-highlight-list-and-auth-api-architecture.md - - docs/plans/2026-05-19-17-06-CST-navigation-resources-highlights.md + - docs/tasks/done/2026-05-16-02-20-+00-todos-tables-highlights-tools.md + - docs/tasks/done/2026-05-18-22-22-+08-highlight-selection-offsets.md + - docs/tasks/done/2026-05-19-10-50-+08-unified-authenticated-api-client.md + - docs/tasks/done/2026-05-19-11-55-+08-highlight-toolbar-union-ranges.md + - docs/tasks/done/2026-05-19-13-02-CST-highlight-list-and-auth-api-architecture.md + - docs/tasks/done/2026-05-19-17-06-CST-navigation-resources-highlights.md - docs/design/text-highlight-floating-toolbar.design.md test_index: - apps/mobile_chat_app/test/message_list_test.dart @@ -806,7 +806,7 @@ index: - apps/node_backend/src/app.ts - vercel.json doc_index: - - docs/plans/2026-05-26-18-34-CST-scheduled-actions-agent-execution.md + - docs/tasks/backlog/2026-05-26-18-34-CST-scheduled-actions-agent-execution.md test_index: - apps/node_backend/src/services/localAgentLoopService.test.ts keywords: diff --git a/docs/plans/2026-04-01-07-04-UTC-model-settings-save-fix.md b/docs/plans/2026-04-01-07-04-UTC-model-settings-save-fix.md deleted file mode 100644 index 9e9abd89..00000000 --- a/docs/plans/2026-04-01-07-04-UTC-model-settings-save-fix.md +++ /dev/null @@ -1,24 +0,0 @@ -# Background -The model settings page currently shows a generic "Failed to save model settings" snackbar when saving some valid backend responses. The sample API response includes `is_default: 1` (numeric), while the Flutter parser expects a boolean and can throw at runtime during response hydration. Additionally, users expect the saved config slot name (`config.slot_id`) to track the chosen model name. - -# Goals -1. Make Save succeed for backend responses that represent booleans as numeric values. -2. Ensure persisted `config.slot_id` stays aligned with the selected default model name after save. -3. Keep UI labels for config chips consistent with current model naming. - -# Implementation Plan (phased) -## Phase 1: Robust API response parsing -- Update model config deserialization in `LlmConfigService` to accept boolean-like values (`true/false`, `1/0`, and string variants) for `is_default`. - -## Phase 2: Slot/model alignment -- Add a slot-id normalization helper derived from the selected model name. -- Use normalized slot id in save payload and in local state updates after save. -- Keep fallback behavior stable for malformed/empty model names. - -## Phase 3: Validation -- Run formatting and targeted Flutter checks after bootstrapping via `./tools/init_dev_env.sh`. - -# Acceptance Criteria -- Saving settings with a backend response like `"is_default": 1` no longer triggers the failure snackbar. -- After save, the stored config `slot_id` matches a normalized form of the selected default model (e.g., `gemini-flash-latest` remains the same). -- Config selector labels continue to reflect model names and remain stable after save/reload. diff --git a/docs/plans/2026-04-01-07-06-sidebar-navigation-page.md b/docs/plans/2026-04-01-07-06-sidebar-navigation-page.md deleted file mode 100644 index 5cfcdbdb..00000000 --- a/docs/plans/2026-04-01-07-06-sidebar-navigation-page.md +++ /dev/null @@ -1,28 +0,0 @@ -# Background -The current chat navigation uses a narrow slide-in Drawer. The requested behavior is a page-level navigation experience so users can focus on navigation options in a full page, while still being able to return to the chat screen with a back action. - -# Goals -- Replace the narrow Drawer interaction with a full-page navigation screen. -- Keep existing navigation actions (manage agents, session settings, app settings). -- Ensure users can return to the chat conversation via a back button. - -# Implementation Plan (phased) -## Phase 1: Build a dedicated navigation page -- Add a new screen under the chat feature that renders navigation options in a full-page Scaffold. -- Provide a standard AppBar back button for returning to chat. -- Return a typed action result from the page when users pick an actionable menu item. - -## Phase 2: Integrate with chat screen -- Remove Drawer usage from `ChatScreen`. -- Add an AppBar menu button that pushes the new navigation page. -- Handle returned actions in `ChatScreen` and route to existing destination screens. - -## Phase 3: Validate behavior -- Run Flutter workspace bootstrap script. -- Run targeted static analysis/tests for the mobile chat app to confirm no regressions. - -# Acceptance Criteria -- Tapping the chat AppBar menu opens a full-page navigation screen instead of a narrow drawer. -- The navigation page has a back button that returns to the chat conversation. -- Selecting Manage Agents, Session Settings, and Settings still opens the same destination screens. -- `flutter analyze` for the mobile chat app succeeds after the change. diff --git a/docs/plans/2026-04-01-07-10-UTC-is-default-number-style.md b/docs/plans/2026-04-01-07-10-UTC-is-default-number-style.md deleted file mode 100644 index 0991f8f3..00000000 --- a/docs/plans/2026-04-01-07-10-UTC-is-default-number-style.md +++ /dev/null @@ -1,23 +0,0 @@ -# Background -Review feedback requested avoiding broad compatibility coercion for `is_default` and instead using a clear API contract. Current backend responses are number-style (`1`/`0`), so client-side conversion should align with that representation. - -# Goals -1. Use number-style `is_default` in request payloads. -2. Parse `is_default` from number-style response values with strict semantics. -3. Keep internal UI/domain behavior unchanged (still boolean in app state). - -# Implementation Plan (phased) -## Phase 1: Contract alignment in serialization -- Update save payload mapping to send `is_default` as `1` or `0`. - -## Phase 2: Strict deserialization -- Replace permissive boolean parser with a number-style parser dedicated to API contract values. -- Keep a narrow fallback for bool only if required by runtime typing differences. - -## Phase 3: Validation -- Run formatter and app test command after environment bootstrap. - -# Acceptance Criteria -- Save requests send `is_default` as number-style values (`1` or `0`). -- Response parsing no longer relies on generic string coercion for `is_default`. -- Existing app test command still passes. diff --git a/docs/plans/2026-04-01-07-19-UTC-pr77-inline-comments-followup.md b/docs/plans/2026-04-01-07-19-UTC-pr77-inline-comments-followup.md deleted file mode 100644 index c96965ea..00000000 --- a/docs/plans/2026-04-01-07-19-UTC-pr77-inline-comments-followup.md +++ /dev/null @@ -1,25 +0,0 @@ -# Background -PR #77 review comments flagged edge cases in slot-id churn and `is_default` parsing strictness. - -# Goals -1. Avoid slot id churn when default model input is empty. -2. Parse `is_default` strictly as 0/1 for number-style contract. -3. Keep UI hint and save behavior aligned for empty model names. - -# Implementation Plan (phased) -## Phase 1: Save-path slot handling -- Preserve existing slot id if default model is blank during save. - -## Phase 2: Strict is_default parsing -- Accept numeric 0/1 only; treat unexpected numeric values as false and log. - -## Phase 3: UI slot synchronization -- Update model `onChanged` logic to keep existing slot when model is blank. - -## Phase 4: Validation -- Run init, format, and targeted app test. - -# Acceptance Criteria -- Clearing model input does not generate timestamp-based slot ids in local state. -- Save uses existing slot id when model input is blank. -- `is_default` parser only treats 1 as true and 0 as false for numeric values. diff --git a/docs/plans/2026-04-01-07-23-UTC-strict-is-default-number-only.md b/docs/plans/2026-04-01-07-23-UTC-strict-is-default-number-only.md deleted file mode 100644 index d6bd29e3..00000000 --- a/docs/plans/2026-04-01-07-23-UTC-strict-is-default-number-only.md +++ /dev/null @@ -1,20 +0,0 @@ -# Background -Follow-up review asks to ensure PR #77 inline comments are fully satisfied with a strict number-style `is_default` contract and no permissive fallback behavior. - -# Goals -1. Enforce number-only parsing for `is_default` (0/1). -2. Keep previously fixed slot-id churn protections intact. -3. Validate and commit the follow-up patch. - -# Implementation Plan (phased) -## Phase 1: Parser strictness -- Remove bool fallback from `_parseIsDefaultNumber`. -- Keep explicit 0/1 mapping and debug logging for invalid values. - -## Phase 2: Validation -- Run bootstrap, formatter, and targeted Flutter test. - -# Acceptance Criteria -- `_parseIsDefaultNumber` accepts only numeric values and maps strictly: `1 => true`, `0 => false`. -- Non-0/1 numeric and non-numeric values are treated as false with debug logs. -- Existing app test command passes. diff --git a/docs/plans/2026-04-01-08-11-UTC-navigation-page-animation-direction.md b/docs/plans/2026-04-01-08-11-UTC-navigation-page-animation-direction.md deleted file mode 100644 index e68ed94f..00000000 --- a/docs/plans/2026-04-01-08-11-UTC-navigation-page-animation-direction.md +++ /dev/null @@ -1,18 +0,0 @@ -# Background -Users reported that tapping the top-left button on the chat page opens the `Navigation` page with a right-to-left push animation. They want this specific transition to enter from left to right instead. - -# Goals -- Make the `Navigation` page transition in from the left edge when opened from chat. -- Keep existing destination behavior and returned action handling unchanged. -- Validate that the app still analyzes cleanly after the route change. - -# Implementation Plan (phased) -1. Locate the chat screen route push code that opens `ChatNavigationPage`. -2. Replace the default `MaterialPageRoute` with a custom route transition that slides from `Offset(-1, 0)` to `Offset.zero`. -3. Keep route result typing (`ChatNavigationAction`) intact. -4. Run environment bootstrap and a focused Flutter/Dart check to verify no analyzer regressions. - -# Acceptance Criteria -- When the user taps the top-left navigation button on chat, the `Navigation` page visibly enters from left to right. -- Selecting actions on the `Navigation` page still returns the same `ChatNavigationAction` results and behavior. -- `flutter analyze` for the mobile chat app completes without new issues. diff --git a/docs/plans/2026-04-01-08-13-UTC-model-save-failure-bool-fix.md b/docs/plans/2026-04-01-08-13-UTC-model-save-failure-bool-fix.md deleted file mode 100644 index 2dcb23e5..00000000 --- a/docs/plans/2026-04-01-08-13-UTC-model-save-failure-bool-fix.md +++ /dev/null @@ -1,33 +0,0 @@ -# Background -On the Model Settings screen, changing the default model name and pressing Save currently shows "Failed to save model settings". The mobile client sends `is_default` as numeric `1/0`, while the Node backend route and DB update path expect a boolean value. - -# API Conventions -**Boolean fields at the API level use numeric `0/1`, not JSON `true/false`.** -- Callers (mobile, other clients) MUST send boolean-semantics fields as integer `1` (true) or `0` (false). -- The backend normalizes incoming JSON booleans and string representations (`"true"/"false"`) for backward compatibility, but the canonical wire format is numeric. -- Any new boolean-like fields added to the API should follow the same `0/1` numeric convention. - -# Goals -1. Make model settings save succeed when editing model names. -2. Keep compatibility when reading `is_default` from either boolean or numeric payloads. -3. Add focused test coverage for request payload shape and response parsing. - -> **Note:** Phase 1 below was superseded by follow-up feedback. The chosen wire format is numeric `1/0` (not boolean). The backend now normalizes both formats; see `docs/plans/2026-04-01-08-56-UTC-model-is-default-number-format.md` and `docs/plans/2026-04-01-09-22-UTC-backend-is-default-normalization.md`. - -# Implementation Plan (phased) -## Phase 1: Save payload contract alignment (superseded) -- ~~Update Flutter `LlmConfigService.save` to send `is_default` as a boolean.~~ -- **Actual decision:** Keep mobile sending numeric `1/0`; add route-level normalization on the backend to accept boolean, numeric, and string representations. - -## Phase 2: Defensive response parsing -- Update `is_default` parsing helper to accept booleans and numeric/string equivalents without throwing. - -## Phase 3: Validation -- Add unit tests around `LlmConfigService` save payload and parse behavior. -- Run repository bootstrap and targeted Flutter tests. - -# Acceptance Criteria -- Saving model settings after changing the model name no longer shows the failure snackbar for valid requests. -- Save request payload sends `is_default` as `1` or `0` (numeric). -- Backend accepts `is_default` as boolean, `0/1`, or `"true"`/`"false"` for backward compatibility. -- Parsing handles backend responses with either `true/false` or `1/0` safely. diff --git a/docs/plans/2026-04-01-08-14-UTC-model-settings-delete-button.md b/docs/plans/2026-04-01-08-14-UTC-model-settings-delete-button.md deleted file mode 100644 index 9d4248cf..00000000 --- a/docs/plans/2026-04-01-08-14-UTC-model-settings-delete-button.md +++ /dev/null @@ -1,29 +0,0 @@ -# Background -The model settings page currently provides only a Save action, which makes it impossible to remove an existing model configuration directly from the form actions. - -# Goals -- Add a Delete button on the model settings page. -- Ensure Delete appears to the left and Save appears to the right. -- Implement delete behavior for persisted configs, with safe handling for unsaved configs. -- Keep validation and save behavior unchanged. - -# Implementation Plan (phased) -1. **Service support** - - Add a `deleteConfig` method in `LlmConfigService` that sends `DELETE /api/config/{id}` with auth headers. -2. **UI and interaction** - - Add `_deleteCurrentConfig` method in `ModelSettingsScreen`. - - For persisted configs (`id != null`), prompt for confirmation and call the service delete API. - - For unsaved local configs (`id == null`), remove locally without API call. - - Keep at least one editable config by creating a blank default if all configs are removed. -3. **Action layout** - - Replace the single Save button with a horizontal row: - - Delete button on the left. - - Save button on the right. -4. **Validation checks** - - Run environment bootstrap and at least one Dart/Flutter check command. - -# Acceptance Criteria -- Model settings page shows both actions with Delete on the left and Save on the right. -- Tapping Delete removes the active config (with confirmation for persisted configs). -- Save continues to persist the active config as before. -- Validation command(s) complete successfully (for example: `flutter analyze`, `flutter test`). diff --git a/docs/plans/2026-04-01-08-17-UTC-chat-real-model-integration.md b/docs/plans/2026-04-01-08-17-UTC-chat-real-model-integration.md deleted file mode 100644 index a8043ec9..00000000 --- a/docs/plans/2026-04-01-08-17-UTC-chat-real-model-integration.md +++ /dev/null @@ -1,25 +0,0 @@ -# Background -The current chat flow in `agent_core` returns a local stub response (`(agent_core stub) Received: ...`) instead of invoking a real LLM provider. This blocks end-to-end conversational behavior in the app. - -# Goals -- Replace the stub path in `AgentSessionImpl` with real provider HTTP calls. -- Support current configured providers (`anthropic`, `gemini`) with model selection from `AgentSettings`. -- Preserve existing `AgentSessionEvent` behavior (`TextDeltaEvent`, `MessageCompleteEvent`, `AgentErrorEvent`, `RunCompleteEvent`). -- Keep tests passing and add coverage for provider/error handling. - -# Implementation Plan (phased) -1. Add a small provider gateway inside `agent_core` that maps `AgentSettings.provider` to real HTTP APIs. -2. Read provider credentials and endpoints from environment (`String.fromEnvironment`) and fail fast with actionable errors when missing. -3. Update `AgentSessionImpl` to call the gateway and emit deltas + completion text from provider output. -4. Add unit tests for: - - successful provider response mapping - - unsupported provider handling - - missing credentials handling -5. Run project bootstrap and package tests (`./tools/init_dev_env.sh`, `melos exec --scope=agent_core -- dart test`). - -# Acceptance Criteria -- Sending a message through `AgentSessionImpl` attempts a real LLM API call for supported providers instead of echoing the stub text. -- On success, exactly one `MessageCompleteEvent` is emitted with model output text. -- On configuration/provider failures, `AgentErrorEvent` is emitted with useful diagnostic text. -- `RunCompleteEvent` is always emitted and session lifecycle semantics remain unchanged. -- `melos exec --scope=agent_core -- dart test` passes. diff --git a/docs/plans/2026-04-01-08-56-UTC-model-is-default-number-format.md b/docs/plans/2026-04-01-08-56-UTC-model-is-default-number-format.md deleted file mode 100644 index a730d04c..00000000 --- a/docs/plans/2026-04-01-08-56-UTC-model-is-default-number-format.md +++ /dev/null @@ -1,22 +0,0 @@ -# Background -Follow-up feedback requires `is_default` to use number-style values (`0/1`) instead of booleans (`true/false`) in model settings payloads. - -# Goals -1. Restore `is_default` request payload format to `0/1`. -2. Keep save behavior stable for model name edits. -3. Validate the mobile app tests still pass. - -# Implementation Plan (phased) -## Phase 1: Payload format correction -- Update `LlmConfigService.save` to encode `is_default` as numeric `1` or `0`. - -## Phase 2: Parser alignment -- Keep response parsing aligned with number-style contract and avoid boolean-first behavior. - -## Phase 3: Validation -- Run bootstrap and mobile Flutter test suite. - -# Acceptance Criteria -- Save payload sends `is_default` as `1` or `0`. -- Editing model name and saving no longer fails due to payload type mismatch. -- `flutter test` for `apps/mobile_chat_app` passes. diff --git a/docs/plans/2026-04-01-09-00-UTC-local-env-gaps-and-validation.md b/docs/plans/2026-04-01-09-00-UTC-local-env-gaps-and-validation.md deleted file mode 100644 index 566850cc..00000000 --- a/docs/plans/2026-04-01-09-00-UTC-local-env-gaps-and-validation.md +++ /dev/null @@ -1,19 +0,0 @@ -# Background -The previous iteration exposed a local tooling gap: workspace guidance and verification used a scoped `melos run ... --scope=...` pattern that is not supported by the installed Melos CLI behavior in this environment. This made reproducible local verification harder. - -# Goals -- Remove local verification friction caused by Melos command mismatch. -- Make init/bootstrap flow provide a reliable `melos` command in common shell paths. -- Document and verify the supported scoped test workflow. - -# Implementation Plan (phased) -1. Update `tools/init_dev_env.sh` to create/update a `melos` shim in `~/.local/bin` (same pattern as `flutter`/`dart`). -2. Update script output to show the supported scoped test command (`melos exec --scope= -- `). -3. Run initialization and verify: - - `melos` is invokable in-session after init. - - scoped package tests succeed via supported Melos syntax. - -# Acceptance Criteria -- Running `./tools/init_dev_env.sh` leaves a working `melos` command accessible via standard shim path setup in the current shell. -- The script’s completion instructions include a valid scoped test invocation pattern. -- Scoped `agent_core` tests pass using the documented command. diff --git a/docs/plans/2026-04-01-09-22-UTC-backend-is-default-normalization.md b/docs/plans/2026-04-01-09-22-UTC-backend-is-default-normalization.md deleted file mode 100644 index 9789abfd..00000000 --- a/docs/plans/2026-04-01-09-22-UTC-backend-is-default-normalization.md +++ /dev/null @@ -1,28 +0,0 @@ -# Background -The client is using number-style `is_default` values (`0/1`) per product expectation. The backend route layer currently forwards `is_default` directly, and the service/database layer expects a boolean, which can still cause save failures depending on runtime coercion. - -# API Conventions -**Boolean fields at the API level use numeric `0/1`, not JSON `true/false`.** -- Callers MUST send `is_default` (and any future boolean fields) as integer `1` or `0`. -- The backend normalizes JSON booleans and string forms for backward compatibility only; new clients should always use `0/1`. - -# Goals -1. Ensure backend accepts `0/1` for `is_default` without save failures. -2. Preserve compatibility with boolean payloads. -3. Return clear 400 errors for invalid `is_default` values. - -# Implementation Plan (phased) -## Phase 1: Route-level normalization -- Add a route helper to normalize `is_default` values to boolean. -- Apply normalization in both POST and PUT config handlers. - -## Phase 2: Validation behavior -- Reject unsupported values with HTTP 400 and an explicit error message. - -## Phase 3: Validation -- Run backend type-check and backend tests. - -# Acceptance Criteria -- Requests with `is_default: 1` or `is_default: 0` save successfully. -- Requests with `is_default: true/false` remain supported. -- Invalid values like `is_default: 2` return HTTP 400. diff --git a/docs/plans/2026-04-01-09-45-UTC.md b/docs/plans/2026-04-01-09-45-UTC.md deleted file mode 100644 index 071fe430..00000000 --- a/docs/plans/2026-04-01-09-45-UTC.md +++ /dev/null @@ -1,26 +0,0 @@ -# Plan: PR #82 Copilot-trigger comment workflow (merged) - -## Background -Multiple short-lived plan files were created while validating GitHub token permissions and posting trigger comments to `askman-dev/bricks` PR #82. A single consolidated plan is preferred. - -## Goals -- Keep one merged plan document for the PR #82 comment workflow. -- Provide a Codex skill that teaches how to post a PR comment that triggers Copilot to address review comments. -- Ensure the skill follows Codex skill directory and file conventions. - -## Implementation Plan (phased) -1. Consolidate plan artifacts - - Merge previous plan intent/history into this single file. - - Remove older task-specific plan files. -2. Add Codex skill - - Create a new skill under `.codex/skills/` with required `SKILL.md`. - - Add a reusable script in the skill's `scripts/` folder to post the trigger comment. - - Document exact command usage and fallback from `GH_TOKEN` to `GITHUB_TOKEN`. -3. Validate - - Run skill validation (`quick_validate.py`) against the new skill. - - Run the bundled script help or syntax check to verify command wiring. - -## Acceptance Criteria -- Only one plan file remains for this PR #82 comment workflow. -- A new skill exists under `.codex/skills/` with valid frontmatter and actionable instructions. -- The bundled script can post the Copilot-trigger comment when a valid token is supplied. diff --git a/docs/plans/2026-04-01-10-52-UTC-libsql-now-fix.md b/docs/plans/2026-04-01-10-52-UTC-libsql-now-fix.md deleted file mode 100644 index 787d33ec..00000000 --- a/docs/plans/2026-04-01-10-52-UTC-libsql-now-fix.md +++ /dev/null @@ -1,18 +0,0 @@ -# Background -A production error occurs when updating API config in the Node backend with LibSQL/SQLite: `no such function: NOW`. The update statement in `configService.ts` still uses PostgreSQL-style `NOW()`. - -# Goals -- Replace the non-portable timestamp function with SQLite-compatible SQL. -- Ensure the config update flow succeeds on LibSQL. -- Validate backend code still compiles/tests for the touched area. - -# Implementation Plan (phased) -1. Locate the `updateApiConfig` SQL update statement using `updated_at = NOW()`. -2. Replace `NOW()` with `CURRENT_TIMESTAMP` in that query. -3. Run focused backend validation (`vitest` and/or type-check) to catch regressions. -4. Commit the plan and code change. - -# Acceptance Criteria -- Updating API config no longer fails with `SQL_INPUT_ERROR: no such function: NOW`. -- `apps/node_backend/src/services/configService.ts` uses SQLite-compatible timestamp SQL for `updated_at`. -- Relevant backend validation command(s) complete successfully. diff --git a/docs/plans/2026-04-01-10-59-UTC-vercel-log-retrieval.md b/docs/plans/2026-04-01-10-59-UTC-vercel-log-retrieval.md deleted file mode 100644 index 147c61db..00000000 --- a/docs/plans/2026-04-01-10-59-UTC-vercel-log-retrieval.md +++ /dev/null @@ -1,28 +0,0 @@ -# Background -用户提供了一个 Vercel 日志页面链接(`/askman-dev/bricks/logs?...selectedLogId=...`),希望在容器环境中直接拿到对应日志内容。 - -# Goals -1. 尝试通过可用凭据和 API 访问该日志。 -2. 若无法直接按 `selectedLogId` 精确提取,至少拿到同项目的最新可访问构建日志。 -3. 记录可复现命令,便于后续继续排查。 - -# Implementation Plan (phased) -## Phase 1: 识别访问方式与鉴权 -- 验证日志页面是否需要登录重定向。 -- 检查环境变量是否存在 `VERCEL_TOKEN`。 - -## Phase 2: 通过 Vercel API 枚举团队/项目/部署 -- 查询 team(slug: `askman-dev`)。 -- 查询项目 `bricks` 的 `projectId`。 -- 枚举最新部署并提取 deployment uid。 - -## Phase 3: 拉取部署构建日志并检索目标标识 -- 调用 `GET /v3/deployments/{uid}/events` 获取构建日志流。 -- 在最近部署事件中检索 `selectedLogId` 前缀(`qpksf`)。 -- 若无命中,输出可访问日志样本与下一步建议(在 dashboard 中按时间定位 runtime log)。 - -# Acceptance Criteria -- 能确认页面是否登录受限(有明确 HTTP 重定向证据)。 -- 能用 `VERCEL_TOKEN` 成功调用 Vercel API 并返回 `askman-dev/bricks` 项目信息。 -- 能成功获取至少一个 deployment 的 build events 日志。 -- 提供可复现命令(`curl`/`jq`)用于后续继续定位。 diff --git a/docs/plans/2026-04-01-11-07-UTC-vercel-api-last10-logs-and-skill.md b/docs/plans/2026-04-01-11-07-UTC-vercel-api-last10-logs-and-skill.md deleted file mode 100644 index 143c8753..00000000 --- a/docs/plans/2026-04-01-11-07-UTC-vercel-api-last10-logs-and-skill.md +++ /dev/null @@ -1,30 +0,0 @@ -# Background -用户要求我继续执行: -1) 实际拿到按时间排序的最后 10 条 Vercel 日志; -2) 总结一套可复用的方法,用于后续前端报错时快速通过日志补充上下文; -3) 将方法沉淀为一个可触发的 skill。 - -# Goals -1. 使用 `VERCEL_TOKEN` + API 获取 `askman-dev/bricks` 最新部署日志,并按时间倒序提取最后 10 条。 -2. 把可复用步骤脚本化,降低重复操作成本。 -3. 新增 skill,明确触发条件、标准流程和输出格式,支持后续排障。 - -# Implementation Plan (phased) -## Phase 1: 拉取最后 10 条日志 -- 查询 teamId / projectId / 最新 deployment uid。 -- 调用 `GET /v3/deployments/{uid}/events`。 -- 用 `jq` 进行 `sort_by(.created) | reverse | .[:10]` 输出。 - -## Phase 2: 沉淀脚本 -- 新增 `tools/vercel/fetch_latest_deployment_logs.sh`,支持参数化 team/project/limit。 -- 默认输出最近 10 条,时间字段转成 UTC 字符串。 - -## Phase 3: 沉淀 skill -- 新增 `.codex/skills/vercel-api-log-context/SKILL.md`。 -- 写明前端报错场景下的日志排查流程、关键命令、上下文提取规范。 -- 运行 skill 校验工具检查 frontmatter 合法性。 - -# Acceptance Criteria -- 命令能返回 `askman-dev/bricks` 最新 deployment 的最后 10 条日志(按时间倒序)。 -- 新脚本可独立执行并输出结构化 JSON。 -- 新 skill 能描述触发条件、操作步骤、产出模板,且通过基础校验。 diff --git a/docs/plans/2026-04-01-12-39-UTC-agentsmd-api-debug-skill-routing.md b/docs/plans/2026-04-01-12-39-UTC-agentsmd-api-debug-skill-routing.md deleted file mode 100644 index 3d6fc936..00000000 --- a/docs/plans/2026-04-01-12-39-UTC-agentsmd-api-debug-skill-routing.md +++ /dev/null @@ -1,20 +0,0 @@ -# Background -The user asked to update the repository-level AGENTS.md guidance so that, when debugging API errors, the agent explicitly attempts to use the `vercel-api-log-context` skill to gather log context. - -# Goals -1. Add a clear instruction in `AGENTS.md` for API/interface error debugging workflows. -2. Ensure the instruction references the existing `vercel-api-log-context` skill by name. -3. Keep the change minimal and easy to discover for future agent runs. - -# Implementation Plan (phased) -## Phase 1: Update repository guidance -- Add a dedicated section in `AGENTS.md` for API debugging behavior. -- Insert an instruction that says to try `vercel-api-log-context` when troubleshooting API errors. - -## Phase 2: Verify formatting and placement -- Confirm the new section is visible in the top-level guidance. -- Confirm wording is imperative and unambiguous. - -# Acceptance Criteria -- `AGENTS.md` includes a section that explicitly instructs agents to try the `vercel-api-log-context` skill during API error debugging. -- The instruction is in the repository root `AGENTS.md` and applies to the full repo scope. diff --git a/docs/plans/2026-04-01-12-55-UTC-chat-send-no-request.md b/docs/plans/2026-04-01-12-55-UTC-chat-send-no-request.md deleted file mode 100644 index 9e719fb8..00000000 --- a/docs/plans/2026-04-01-12-55-UTC-chat-send-no-request.md +++ /dev/null @@ -1,21 +0,0 @@ -# Background -User reports that typing and sending a chat message in the web UI does not produce any corresponding network request in DevTools and no assistant reply is received. During review, test-status notes also needed correction because `flutter test apps/mobile_chat_app` from repo root failed for command-context reasons while package tests pass from the app directory. - -# Goals -- Reproduce or identify the code path that handles chat send actions. -- Determine why no outbound request is being issued. -- Implement a fix so sending a message reliably triggers the backend request. -- Clarify the correct test invocation for `mobile_chat_app` and document accurate validation status. - -# Implementation Plan (phased) -1. Inspect chat UI send handler wiring and state guards in Flutter widgets/view-models. -2. Trace the request pipeline from send action to API client invocation. -3. Fix any gating, early-return, or misconfigured endpoint/client logic preventing request dispatch. -4. Validate package test dependencies in `apps/mobile_chat_app/pubspec.yaml` and re-run tests with both invocation styles (`flutter test apps/mobile_chat_app` and `cd apps/mobile_chat_app && flutter test`). -5. Update PR/reviewer-facing notes to describe the real failure mode and reproducible passing command. - -# Acceptance Criteria -- Sending a message from the chat input invokes the request pipeline (observable in code flow and testable behavior). -- No unconditional/incorrect early-return prevents network dispatch when valid input is provided. -- Validation notes accurately describe root-command failure mode and include the passing package-directory command. -- Relevant checks are listed explicitly (for example: `flutter analyze apps/mobile_chat_app`, `cd apps/mobile_chat_app && flutter test`). diff --git a/docs/plans/2026-04-01-13-46-UTC-chat-send-session-error-handling.md b/docs/plans/2026-04-01-13-46-UTC-chat-send-session-error-handling.md deleted file mode 100644 index de00401d..00000000 --- a/docs/plans/2026-04-01-13-46-UTC-chat-send-session-error-handling.md +++ /dev/null @@ -1,18 +0,0 @@ -# Background -After deploying the prior fix, the user still reports that sending messages yields no response. This indicates session initialization or stream start failures may still be uncaught, leaving the UI without assistant output. - -# Goals -- Ensure chat send flow handles session creation/start errors and surfaces them to the user. -- Prevent the UI from getting stuck in sending/streaming state when async setup fails. -- Keep the behavior observable with clear error text in the assistant message bubble. - -# Implementation Plan (phased) -1. Inspect `_sendMessage` asynchronous flow for unhandled errors in `_sessionForAgent(...).then(...)`. -2. Add explicit error handling (`catchError`) to convert setup failures into assistant-visible error content. -3. Reset `_isSending`/`_isStreaming` flags in that failure path. -4. Run `flutter analyze apps/mobile_chat_app` and `cd apps/mobile_chat_app && flutter test`. - -# Acceptance Criteria -- If session creation/start fails before stream subscription, the assistant placeholder shows `Error: ...`. -- The composer is re-enabled because `_isSending` and `_isStreaming` are reset. -- Static analysis and package tests pass for the touched app package. diff --git a/docs/plans/2026-04-01-14-52-UTC-platform-environment-web-fix.md b/docs/plans/2026-04-01-14-52-UTC-platform-environment-web-fix.md deleted file mode 100644 index 43cee746..00000000 --- a/docs/plans/2026-04-01-14-52-UTC-platform-environment-web-fix.md +++ /dev/null @@ -1,18 +0,0 @@ -# Background -A runtime error still occurs in deployed web builds: `Unsupported operation: Platform._environment`. This indicates `dart:io` environment access is being used on a platform where it is unsupported. - -# Goals -- Remove crash behavior caused by direct `Platform.environment` access on web. -- Keep support for local/server environment variables where available. -- Preserve existing API key/base URL configuration behavior via compile-time defines and runtime environment. - -# Implementation Plan (phased) -1. Refactor `RealModelGateway` default environment initialization to avoid unconditional `Platform.environment` access. -2. Add compile-time (`String.fromEnvironment`) fallbacks for supported config keys. -3. Merge runtime `Platform.environment` values only when available, catching unsupported platform exceptions. -4. Run package analysis/tests to validate no regressions. - -# Acceptance Criteria -- Web runtime no longer throws `Unsupported operation: Platform._environment` when sending messages. -- API key/base URL lookup still works for both compile-time defines and runtime environment maps. -- Relevant analysis/tests pass for touched packages. diff --git a/docs/plans/2026-04-01-15-37-UTC-session-runtime-config-selection.md b/docs/plans/2026-04-01-15-37-UTC-session-runtime-config-selection.md deleted file mode 100644 index d632e9b7..00000000 --- a/docs/plans/2026-04-01-15-37-UTC-session-runtime-config-selection.md +++ /dev/null @@ -1,19 +0,0 @@ -# Background -Users need session-scoped model/config selection that can differ from default configuration, especially for parallel conversations and temporary model switches. - -# Goals -- Add session-level (in-memory) runtime selection for config + model in chat. -- Support one config exposing multiple model options. -- Ensure temporary session selection does not mutate persisted default config. - -# Implementation Plan (phased) -1. Extend `LlmConfig` parsing to include available model options from config payload. -2. Add chat runtime state for selected config/model, loaded from default config at startup. -3. Add a chat UI control to switch runtime config/model for the current session only. -4. Rebuild agent sessions when runtime selection changes so subsequent sends use new runtime settings. -5. Run analysis and app tests. - -# Acceptance Criteria -- User can choose config and model in chat without editing saved defaults. -- Selection applies to subsequent messages in current session. -- Reloading app continues to use persisted default unless user switches again. diff --git a/docs/plans/2026-04-01-15-55-UTC-github-review-thread-resolver-and-init-env.md b/docs/plans/2026-04-01-15-55-UTC-github-review-thread-resolver-and-init-env.md deleted file mode 100644 index 7c9e14c5..00000000 --- a/docs/plans/2026-04-01-15-55-UTC-github-review-thread-resolver-and-init-env.md +++ /dev/null @@ -1,39 +0,0 @@ -# Background -We introduced a GitHub PR review-thread resolver skill to support REVIEW_FIXED workflows and avoid redundant Copilot trigger comments. During iteration we also aligned implementation with repository constraints (no Python) and improved environment bootstrap so required CLI dependencies are prepared automatically. - -# Goals -- Provide a reusable skill that can resolve GitHub PR review threads safely. -- Use repository-native tooling (shell script with `curl` + `jq`) instead of Python. -- Ensure bootstrap (`tools/init_dev_env.sh`) installs or validates required commands (`curl`, `jq`) instead of failing late. -- Keep operations auditable via dry-run-first workflow. - -# Implementation Plan (phased) -## Phase 1: Validate GitHub review-thread API behavior -- Query review threads and inspect `id`, `isResolved`, `isOutdated`. -- Validate thread resolution flow via GraphQL mutations (`resolveReviewThread`, and reversibility checks where needed). - -## Phase 2: Implement reusable skill (shell) -- Create `.codex/skills/github-pr-review-thread-resolver/`. -- Add executable script using `curl` + `jq` to: - - page `reviewThreads`, - - filter by mode (`outdated`, `unresolved`, `all`), - - support `--dry-run`, - - resolve selected thread IDs. -- Document usage, safety constraints, and token requirements in `SKILL.md`. - -## Phase 3: Bootstrap dependency hardening -- Update `tools/init_dev_env.sh` to handle `curl`/`jq` as required dependencies. -- Add package-manager detection and automatic installation attempts (`apt-get`, `dnf`, `yum`, `apk`, `pacman`, `brew`). -- Keep explicit manual remediation guidance when auto-install is not possible. - -## Phase 4: Validation and rollout checks -- Run syntax checks for changed shell scripts. -- Execute resolver script in dry-run mode on a real PR for selection visibility. -- Verify init script help output and command-flow integrity. - -# Acceptance Criteria -- A single local skill exists for review-thread resolution in REVIEW_FIXED workflows. -- Skill implementation uses shell tooling only (no Python runtime dependency). -- Resolver supports `--dry-run` and mode-based selection with clear summary output. -- `init_dev_env.sh` proactively attempts to install missing `curl`/`jq` and only fails with actionable guidance if installation cannot be completed. -- Documentation and operational steps are consolidated in one plan file for this change stream. diff --git a/docs/plans/2026-04-01-15-56-UTC-pr91-review-comments.md b/docs/plans/2026-04-01-15-56-UTC-pr91-review-comments.md deleted file mode 100644 index 334f8cd4..00000000 --- a/docs/plans/2026-04-01-15-56-UTC-pr91-review-comments.md +++ /dev/null @@ -1,19 +0,0 @@ -# Background -PR #91 received review comments around async error handling, type safety, stream cancellation ordering, and model list normalization. - -# Goals -- Resolve all inline review comments with robust, typed implementations. -- Prevent spinner/deadlock behavior when loading configs fails. -- Normalize and validate model preference parsing to avoid malformed entries. - -# Implementation Plan (phased) -1. Refactor `_loadAgents` to avoid untyped `Future.wait` casts. -2. Add `try/catch` around startup loading and ensure loading flags are cleared on all paths. -3. Await stream subscription cancellation during session reset. -4. Trim/default/normalize/dedupe model values while protecting against empty entries. -5. Run analysis and mobile app tests. - -# Acceptance Criteria -- Review comments on the referenced lines are addressed in code. -- Startup gracefully handles config fetch failures without indefinite loading state. -- Model list parsing does not emit empty/duplicate malformed entries. diff --git a/docs/plans/2026-04-01-16-03-UTC-pr92-review-comment-followup.md b/docs/plans/2026-04-01-16-03-UTC-pr92-review-comment-followup.md deleted file mode 100644 index c07a3e04..00000000 --- a/docs/plans/2026-04-01-16-03-UTC-pr92-review-comment-followup.md +++ /dev/null @@ -1,32 +0,0 @@ -# Background -PR #92 received Copilot review comments covering shell argument parsing, runtime dependency checks, GraphQL error handling, package manager safety/privilege behavior, and skill documentation clarity. - -# Goals -- Address valid review findings in code and skill docs. -- Keep bash-based implementation (non-Python) while improving robustness. -- Resolve review threads after fixes are in place. - -# Implementation Plan (phased) -## Phase 1: Fix `resolve_review_threads.sh` -- Validate `--mode` has a non-empty value before shifting. -- Add explicit `curl` dependency check. -- Harden `graphql_call` with `curl -fsS` and JSON validation. - -## Phase 2: Fix `tools/init_dev_env.sh` -- Handle package installs with root/sudo/doas fallback. -- Improve error guidance when privilege escalation is unavailable. -- Switch pacman install path to `-Syu` to avoid partial-upgrade pattern. - -## Phase 3: Docs alignment -- Update skill prerequisites to mention both `jq` and `curl`. -- Clarify script is bash-based in skill docs. - -## Phase 4: Validate + resolve threads -- Run syntax checks and dry-run command checks. -- Resolve addressed review threads on PR #92. - -# Acceptance Criteria -- All actionable PR #92 review comments are addressed in code/docs. -- Script behavior is more robust for missing args and API failures. -- Init script install behavior handles non-root environments safely. -- Review threads are resolved after updates. diff --git a/docs/plans/2026-04-01-16-07-UTC-backend-llm-fallback-for-user-config.md b/docs/plans/2026-04-01-16-07-UTC-backend-llm-fallback-for-user-config.md deleted file mode 100644 index 6a46536a..00000000 --- a/docs/plans/2026-04-01-16-07-UTC-backend-llm-fallback-for-user-config.md +++ /dev/null @@ -1,19 +0,0 @@ -# Background -Users still hit `Missing BRICKS_GEMINI_API_KEY` in chat because direct provider calls depend on process-level environment keys, while user-specific keys are stored in backend configs. - -# Goals -- Allow chat sessions to run through backend user-configured LLM settings when available. -- Remove hard dependency on frontend process environment API keys for normal authenticated chat usage. -- Support session-scoped `configId` + `model` overrides without persisting session state. - -# Implementation Plan (phased) -1. Extend `AgentSettings` with optional backend routing fields (`apiBaseUrl`, `authToken`, `configId`). -2. Add backend chat path in `RealModelGateway` that calls `/api/llm/chat` with auth and optional provider/model/config override. -3. Update mobile chat screen to populate backend routing fields (base URL, token, selected config slot). -4. Update Node backend `/api/llm/chat` + LLM service to accept optional `configId` and resolve runtime config by ID first. -5. Run analyze and tests for affected packages/apps. - -# Acceptance Criteria -- Authenticated chat can succeed without `BRICKS_GEMINI_API_KEY` set in frontend runtime. -- Session model/config overrides are forwarded to backend and do not mutate default config. -- Existing app/package checks pass. diff --git a/docs/plans/2026-04-01-16-15-UTC-bash-not-posix-clarification.md b/docs/plans/2026-04-01-16-15-UTC-bash-not-posix-clarification.md deleted file mode 100644 index f3e9de45..00000000 --- a/docs/plans/2026-04-01-16-15-UTC-bash-not-posix-clarification.md +++ /dev/null @@ -1,22 +0,0 @@ -# Background -A review comment flagged wording mismatch: implementation is bash-specific but some PR narrative used "POSIX shell" language. - -# Goals -- Make repository docs explicit that the resolver script requires bash. -- Avoid implying POSIX `sh` compatibility. - -# Implementation Plan (phased) -## Phase 1: Clarify skill docs -- Add an explicit note in SKILL.md that resolver script requires bash and is not POSIX `sh` compatible. - -## Phase 2: Clarify script header -- Add a short comment near shebang highlighting bash-only features. - -## Phase 3: Validate -- Run `bash -n` on the resolver script. -- Search for remaining "POSIX" references in relevant skill/plan files. - -# Acceptance Criteria -- Skill docs state bash requirement clearly. -- Script itself warns readers that it is bash-only. -- No contradictory POSIX wording remains in skill/plan docs touched by this workflow. diff --git a/docs/plans/2026-04-01-16-26-UTC-pr-review-findings-auto-trigger.md b/docs/plans/2026-04-01-16-26-UTC-pr-review-findings-auto-trigger.md deleted file mode 100644 index c74ea41f..00000000 --- a/docs/plans/2026-04-01-16-26-UTC-pr-review-findings-auto-trigger.md +++ /dev/null @@ -1,24 +0,0 @@ -# Background -PR authors currently need to manually add an `@copilot` follow-up comment after a review is submitted with actionable findings. This creates repetitive work and inconsistent timing for starting Copilot-based review-fix workflows. - -# Goals -- Automatically detect `pull_request_review` submissions that should trigger Copilot follow-up. -- Post a single trigger comment per submitted review when that review has comments. -- Prevent duplicate comments during reruns and concurrent executions. -- Emit clear run-time telemetry and actionable error messages. - -# Implementation Plan (phased) -1. Add a dedicated GitHub Actions workflow triggered by `pull_request_review` `submitted` events. -2. Enforce PR-scoped workflow concurrency with `cancel-in-progress: true`. -3. Implement a decision step that: - - validates review state (`commented` or `changes_requested`), - - counts comments for the current review, - - checks existing PR comments for a per-review idempotency marker. -4. Post the default Copilot trigger comment only when all conditions pass, appending an HTML marker keyed by `review_id`. -5. Print structured observability output (PR number, review id/state, comments count, triggered flag, skip reason), and provide permission remediation hints on API failures. - -# Acceptance Criteria -- A single review submission with multiple review comments creates exactly one `@copilot` trigger comment. -- Re-running the same workflow for the same `review_id` does not add a second trigger comment. -- Reviews with zero comments do not create trigger comments. -- If token permissions are insufficient, logs include both the API error and a remediation hint indicating required scopes (`pull_requests: read`, `issues: write`). diff --git a/docs/plans/2026-04-01-16-59-UTC-pr93-conflict-free-branch.md b/docs/plans/2026-04-01-16-59-UTC-pr93-conflict-free-branch.md deleted file mode 100644 index a9bad3c2..00000000 --- a/docs/plans/2026-04-01-16-59-UTC-pr93-conflict-free-branch.md +++ /dev/null @@ -1,20 +0,0 @@ -# Background -PR #93 branch has merge conflicts against the current `main` branch, so a conflict-free replacement branch is needed. - -# Goals -- Recreate the PR #93 feature changes on top of the latest `origin/main`. -- Resolve conflicts once in a clean linear history. -- Publish a new branch that can be used to open a fresh no-conflict PR. - -# Implementation Plan (phased) -1. Fetch remote refs and inspect PR #93 commit history. -2. Create a new branch from `origin/main`. -3. Cherry-pick the feature commits from PR #93 (excluding merge commits) and resolve any conflicts. -4. Run targeted tests/checks for changed areas. -5. Commit any conflict-resolution adjustments and push the new branch. - -# Acceptance Criteria -- A new branch exists on remote containing PR #93 functionality. -- `git status` is clean and branch is based on latest `origin/main`. -- Relevant tests for the touched package(s) complete successfully. -- New branch can be used to open a PR without conflicts. diff --git a/docs/plans/2026-04-01-18-00-UTC-llm-chat-root-cause-and-key-handling.md b/docs/plans/2026-04-01-18-00-UTC-llm-chat-root-cause-and-key-handling.md deleted file mode 100644 index 8cd3b306..00000000 --- a/docs/plans/2026-04-01-18-00-UTC-llm-chat-root-cause-and-key-handling.md +++ /dev/null @@ -1,23 +0,0 @@ -# Background -Chat requests failed with opaque backend errors when stored provider keys were not usable at runtime. Earlier iterations introduced multiple plan documents and one defensive runtime rule that could overfit to current provider key shapes. - -# Goals -- Make this the canonical technical plan for this session while acknowledging that earlier same-day plan drafts may remain for historical context. -- Preserve the root-cause fix for decryption mismatch without imposing provider-specific API key pattern assumptions. -- Make encrypted-key detection robust and future-compatible. - -# Implementation Plan (phased) -1. Clearly document the current, canonical plan for this session in this file. -2. Remove runtime rejection logic that guesses whether a provider key is encrypted ciphertext. -3. Version encrypted payload format (`enc:v1:`) and keep backward-compatible legacy decryption. -4. Add explicit `api_key_encrypted` metadata on write paths so reads do not rely on provider key shape assumptions. -5. Enforce a single encryption key source (`ENCRYPTION_KEY`) and fail fast at backend startup when it is missing/empty. -6. Validate via targeted tests and backend type-check. - -# Acceptance Criteria -- This document is clearly marked and used as the canonical plan for this session, even if earlier drafts from the same date remain under `docs/plans/`. -- No provider key prefix/shape assumptions are used to reject runtime config in LLM service. -- Encrypted keys can be detected via explicit metadata and versioned format. -- Legacy encrypted values continue to decrypt. -- Backend startup fails when `ENCRYPTION_KEY` is missing/empty, preventing bad deployments. -- Tests and type-check pass. diff --git a/docs/plans/2026-04-01-18-32-UTC-pr-review-thread-auto-resolver-comment.md b/docs/plans/2026-04-01-18-32-UTC-pr-review-thread-auto-resolver-comment.md deleted file mode 100644 index de033e99..00000000 --- a/docs/plans/2026-04-01-18-32-UTC-pr-review-thread-auto-resolver-comment.md +++ /dev/null @@ -1,34 +0,0 @@ -# Background -The repository has two GitHub workflows related to pull request review automation. The `pr_review_thread_auto_resolver.yml` workflow resolves outdated review threads automatically, but it currently does not leave a human-visible comment after successful resolution. Maintainers want explicit visibility when the automation runs and resolves threads, while avoiding noise when no action is taken. - -# Goals -1. Update the auto resolver workflow so it posts an English PR comment when it successfully resolves one or more review threads. -2. Ensure the comment clearly indicates that the resolutions were performed by automation (robot) using the same personal token that performs the resolve mutations. -3. Ensure no comment is posted when the workflow executes but resolves zero threads. - -# Implementation Plan (phased) -## Phase 1: Review current resolver output contract -- Read `.github/workflows/pr_review_thread_auto_resolver.yml` and the resolver script used by the workflow. -- Confirm what structured output is available for detecting resolved count and listing resolved thread references. - -## Phase 2: Extend workflow with parse + conditional comment -- Capture resolver script output to a log file while keeping console output. -- Parse: - - `resolved_threads=` for resolved count. - - Per-thread lines for `url=` values to surface links to threads resolved by automation. -- Expose parsed values via step outputs. -- Add a conditional `actions/github-script` step that posts a PR comment only when resolved count is greater than zero. -- Use `${{ secrets.GH_TOKEN }}` for that comment action to match the token used for thread resolution. - -## Phase 3: Validate syntax and summarize -- Run YAML lint/inspection check (or targeted sanity check command) to verify workflow file integrity. -- Review diff and ensure behavior matches requirements: comment only on actual resolution actions. - -# Acceptance Criteria -- On runs where at least one review thread is resolved, a PR comment is added in English and includes: - - confirmation that auto resolver succeeded, - - explicit note that resolved status was set by automation, - - references to resolved thread comment URLs when available. -- On runs where zero threads are resolved, no PR comment is created. -- Comment creation uses `${{ secrets.GH_TOKEN }}` (same token identity used for resolve actions). -- Workflow remains valid and runnable in GitHub Actions. diff --git a/docs/plans/2026-04-01-19-24-UTC-chat-ui-navigation-adjustments.md b/docs/plans/2026-04-01-19-24-UTC-chat-ui-navigation-adjustments.md deleted file mode 100644 index 60ea18bb..00000000 --- a/docs/plans/2026-04-01-19-24-UTC-chat-ui-navigation-adjustments.md +++ /dev/null @@ -1,27 +0,0 @@ -# Background -The chat UI currently has a microphone button in the composer row, session settings in the sidebar navigation, and model selection in the app bar. On mobile, swiping in from the left edge can also invoke browser back navigation instead of opening in-app navigation. - -# Goals -1. Remove the voice/microphone button from the composer input row. -2. Add a secondary action row under the input: - - Left: an adjustment button that opens a menu containing: - - 新上下文 (no-op) - - 模型 (opens the existing model selection dialog) - - Agents (no-op) - - Right: send/stop button. -3. Remove "Session Settings" from the sidebar navigation and remove the corresponding page entrypoint from chat navigation flow. -4. Improve left-edge interaction so opening in-app sidebar is preferred and browser-style back navigation is suppressed from the chat root route. - -# Implementation Plan (phased) -1. Update `ComposerBar` to remove the mic button, add a new action row/menu, and move send/stop button to the new row. -2. Wire a new callback from `ChatScreen` into `ComposerBar` to launch the existing runtime model configuration dialog. -3. Replace push-based navigation page with a `Drawer`-based sidebar in `ChatScreen` (opened by AppBar menu button and edge drag), and add `PopScope(canPop: false)` at the chat root to suppress back pop on this screen. -4. Simplify `ChatNavigationPage` action enum and menu list by removing session settings. -5. Remove `SessionSettingsPage` source and its dedicated widget test now that it is no longer reachable from the app navigation. - -# Acceptance Criteria -1. Composer input no longer shows a mic icon; a separate action row appears below input with adjustment-menu left and send/stop right. -2. Tapping adjustment -> 模型 opens the existing session model dialog. -3. Sidebar no longer contains "Session Settings". -4. Chat root route does not pop on back gesture/action (preventing unintended browser-back behavior from app route pop). -5. Flutter widget tests pass for touched chat/navigation components (`flutter test` in `apps/mobile_chat_app`). diff --git a/docs/plans/2026-04-02-02-56-UTC-oauth-dynamic-preview-return.md b/docs/plans/2026-04-02-02-56-UTC-oauth-dynamic-preview-return.md deleted file mode 100644 index 98c3056f..00000000 --- a/docs/plans/2026-04-02-02-56-UTC-oauth-dynamic-preview-return.md +++ /dev/null @@ -1,35 +0,0 @@ -# Background -GitHub OAuth callback URL must stay fixed, but preview deployments run on dynamic Vercel hostnames. Current login flow always redirects to `/` after callback, so users starting on preview URLs cannot reliably return to their originating deployment. - -# Goals -1. Keep existing callback path unchanged. -2. Allow login initiated from preview domains matching `bricks--askman-dev.vercel.app`. -3. Validate the requested start/return URL before redirecting to GitHub. -4. Return users to the validated originating URL after callback. - -# Implementation Plan (phased) -## Phase 1: OAuth return URL validation and state transport -- Add a backend helper that validates `return_to` values using: - - `https` protocol - - host pattern `^bricks-[A-Za-z0-9]+-askman-dev\.vercel\.app$` - - optional allowlist from env for stable first-party domains - - localhost allowances for dev -- On `/auth/github`, accept `return_to` query and validate it. -- Put `{ nonce, returnTo }` inside OAuth `state` payload while continuing CSRF cookie nonce checking. - -## Phase 2: Callback redirect behavior -- Decode `state` in callback. -- Verify nonce against cookie and extract validated `returnTo`. -- Keep callback path unchanged and only alter query/state behavior. -- Update redirect response HTML to navigate to `returnTo` instead of always `/`. - -## Phase 3: Docs and validation -- Update backend API docs and README to document `return_to` behavior and domain constraints. -- Run backend type-check and tests. - -# Acceptance Criteria -1. `GET /api/auth/github?return_to=https://bricks-abc123-askman-dev.vercel.app/` redirects to GitHub with encoded state that includes that return URL. -2. `GET /api/auth/github?return_to=https://evil.example.com/` is rejected with HTTP 400. -3. Callback continues to use the existing path and succeeds when `state.nonce` matches cookie nonce. -4. Successful callback redirects browser to the validated `returnTo` URL. -5. Backend checks (`npm run type-check`, `npm test`) pass. diff --git a/docs/plans/2026-04-02-04-38-UTC-github-return-to-validation.md b/docs/plans/2026-04-02-04-38-UTC-github-return-to-validation.md deleted file mode 100644 index fa76e014..00000000 --- a/docs/plans/2026-04-02-04-38-UTC-github-return-to-validation.md +++ /dev/null @@ -1,19 +0,0 @@ -# Background -Users hitting `/api/auth/github` with `return_to=https://bricks.askman.dev/` can receive `{"error":"Invalid return_to URL"}` when `OAUTH_ALLOWED_RETURN_ORIGINS` does not include the production app origin. - -# Goals -- Accept secure first-party `return_to` values that match the configured OAuth callback origin. -- Preserve existing security checks for localhost/protocol restrictions. -- Add regression tests for return URL validation. - -# Implementation Plan (phased) -1. Update `isAllowedReturnTo` in `apps/node_backend/src/routes/auth_return_to.ts` to allow same-origin redirects derived from `GITHUB_CALLBACK_URL`. -2. Keep existing preview-domain and explicit allowlist checks intact. -3. Add unit tests in `apps/node_backend/src/routes/auth_return_to.test.ts` for callback-origin allow, HTTP rejection on non-localhost, and explicit allowlist behavior. -4. Run backend test/lint/type-check commands. - -# Acceptance Criteria -- A `return_to` URL that is HTTPS and matches the origin of `GITHUB_CALLBACK_URL` is accepted. -- Non-HTTPS non-localhost return URLs remain rejected. -- Existing allowlist behavior via `OAUTH_ALLOWED_RETURN_ORIGINS` still works. -- `npm test`, `npm run lint`, and `npm run type-check` pass in `apps/node_backend`. diff --git a/docs/plans/2026-04-02-06-06-UTC.md b/docs/plans/2026-04-02-06-06-UTC.md deleted file mode 100644 index e9b9ada6..00000000 --- a/docs/plans/2026-04-02-06-06-UTC.md +++ /dev/null @@ -1,31 +0,0 @@ -# OAuth state validation fix for preview-domain login callback - -## Background -Users starting the web app from a Vercel preview URL are being redirected to the production OAuth callback host, and the callback can fail with `{"error":"Invalid or missing OAuth state parameter"}` when the nonce cookie is not available on that host. - -## Goals -- Keep CSRF protections for standard same-host OAuth flows. -- Allow callback validation to succeed for cross-domain preview → production callback flows. -- Preserve backwards compatibility for in-flight/legacy OAuth state values. - -## Implementation Plan - -1. Add a short-lived server-side nonce store (`oauth_states` DB table) to hold the OAuth state nonce and `returnTo` URL recorded at flow initiation, so that state is bound to the server rather than a client-side cookie alone. -2. Keep cookie-based nonce validation as the primary check when the cookie is present. -3. Update callback validation logic (`validateOAuthCallbackState`) to accept either: - - cookie nonce match (same-host flows), or - - successful consumption of the nonce from the server-side store when the cookie is absent (cross-domain preview → production flows). -4. When the cookie is absent but a valid DB record is found, use the `returnTo` URL from the database record (not from the state parameter) to prevent open-redirect attacks. -5. Explicitly reject states that are missing from the DB store or whose cookie nonce does not match, to prevent replay/cross-use across accounts or hosts. -6. Add unit tests covering all validation paths: cookie match, cookie mismatch, DB hit, DB miss, legacy hex state, and empty-cookie edge case. - -## Security Notes -- A purely stateless signed-state approach (HMAC without a server-side record) does **not** provide CSRF protection: an attacker can obtain a valid signed state by initiating OAuth themselves, then trick a victim into visiting the callback URL with a different authorization code, logging the victim into the attacker's account. -- The server-side nonce store avoids this because the nonce is one-time use and can only be consumed once at the shared production backend. -- Preview and production deployments share the same Turso database, so a nonce stored by the preview backend is visible to the production callback. - -## Acceptance Criteria -- Starting OAuth from a preview domain and returning to production callback no longer fails solely due to missing `oauth_state` cookie (nonce is found in the shared DB store). -- Same-host OAuth still validates nonce against the cookie (cookie match path). -- CSRF protections are preserved: nonces are one-time use and server-side; an attacker cannot replay a signed-but-stateless state. -- Existing tests pass and new tests cover all validation branches. diff --git a/docs/plans/2026-04-02-06-19-UTC-composer-unified-border.md b/docs/plans/2026-04-02-06-19-UTC-composer-unified-border.md deleted file mode 100644 index 157cdea6..00000000 --- a/docs/plans/2026-04-02-06-19-UTC-composer-unified-border.md +++ /dev/null @@ -1,18 +0,0 @@ -# Background -The current mobile chat composer renders the text input and the tool row (menu + send/stop controls) in separate visual blocks. Product feedback requests a Gemini-like treatment where both regions sit inside one shared rounded border while preserving all current behaviors. - -# Goals -- Keep all existing composer functionality unchanged (menu actions, send/stop logic, mention behavior). -- Update only visual/layout styling so the input and tool row appear inside one unified container border. -- Ensure widget tests continue to pass after layout refactoring. - -# Implementation Plan (phased) -1. Inspect `ComposerBar` structure and identify where the input and tool row are currently split. -2. Refactor `ComposerBar` layout so input + controls are wrapped by a single bordered `Container` with rounded corners. -3. Remove duplicate inner text-field outline styling, replacing it with a borderless field that sits inside the shared container. -4. Run mobile app tests focused on `composer_bar_test.dart` to validate no functional regressions. - -# Acceptance Criteria -- The composer input and bottom tool row render within one rounded rectangular border in the mobile chat UI. -- Menu, send, stop, and mention features behave exactly as before. -- `cd apps/mobile_chat_app && flutter test test/composer_bar_test.dart` passes. diff --git a/docs/plans/2026-04-02-07-09-UTC-drawer-top-alignment.md b/docs/plans/2026-04-02-07-09-UTC-drawer-top-alignment.md deleted file mode 100644 index 5fc30a5b..00000000 --- a/docs/plans/2026-04-02-07-09-UTC-drawer-top-alignment.md +++ /dev/null @@ -1,21 +0,0 @@ -# Background -用户反馈侧边栏展开后,导航内容在竖直方向看起来偏中间,不符合“顶部对齐”的预期。当前 `ChatNavigationPage` 使用 `DrawerHeader`,其默认高度与底部对齐文本会造成顶部留白。 - -# Goals -- 让侧边栏展开后可见内容从顶部开始布局。 -- 保持现有导航项与交互行为不变。 -- 通过现有 Flutter 测试验证无回归。 - -# Implementation Plan (phased) -1. Inspect - - 检查 `ChatNavigationPage` 的布局结构,定位导致顶部大留白的组件。 -2. Implement - - 将默认高度较大的 `DrawerHeader` 替换为更紧凑的顶部标题容器(或调整其布局),确保列表内容从顶部开始。 -3. Validate - - 运行仓库初始化脚本。 - - 在移动端包目录运行相关 Flutter 测试,确认导航项文本与交互测试通过。 - -# Acceptance Criteria -- 打开 Drawer 后,顶部不会出现不必要的大面积空白,导航内容视觉上从顶部开始。 -- `Current Chat`、`Sessions`、`Manage Agents`、`Settings` 仍然可见并保持原有行为。 -- 相关 Flutter 测试通过(例如 `chat_navigation_page_test.dart`)。 diff --git a/docs/plans/2026-04-02-07-11-UTC-composer-bar-button-style.md b/docs/plans/2026-04-02-07-11-UTC-composer-bar-button-style.md deleted file mode 100644 index f85df7b7..00000000 --- a/docs/plans/2026-04-02-07-11-UTC-composer-bar-button-style.md +++ /dev/null @@ -1,20 +0,0 @@ -# Background -The chat composer currently renders a horizontal divider line between the text input area and the button row, and the send button uses a larger filled style than the left-side action button. This creates an imbalanced visual hierarchy in the input area. - -# Goals -- Remove the divider above the button row in the composer. -- Make the send button visual size match the left action button. -- Preserve existing send/loading/disabled behavior. - -# Implementation Plan (phased) -1. Inspect `ComposerBar` widget layout and identify the divider and send button implementations. -2. Remove the divider widget between text field and action row. -3. Replace the oversized filled send icon button with a standard `IconButton` so it matches the action button's visual scale. -4. Keep spinner behavior for sending state and keep stop button behavior unchanged. -5. Run focused Flutter tests for the mobile app package. - -# Acceptance Criteria -- In the composer UI, there is no visible dividing line above the action row. -- The send button appears the same size class as the left action icon button. -- Message send action still triggers on send button tap and Enter/send submit. -- Validation command succeeds: `cd apps/mobile_chat_app && flutter test`. diff --git a/docs/plans/2026-04-02-07-51-UTC-ai-provider-smoke-test-secret-if-fix.md b/docs/plans/2026-04-02-07-51-UTC-ai-provider-smoke-test-secret-if-fix.md deleted file mode 100644 index b7418938..00000000 --- a/docs/plans/2026-04-02-07-51-UTC-ai-provider-smoke-test-secret-if-fix.md +++ /dev/null @@ -1,21 +0,0 @@ -# Background -A pull request workflow run reported `.github/workflows/ai_provider_smoke_test.yml` as invalid because the integration test step used `secrets.*` directly in an `if` expression. GitHub Actions does not allow directly referencing `secrets` in `if` conditionals. - -# Goals -- Fix workflow syntax so the file validates. -- Preserve current behavior: only run real-API integration tests when at least one test API key is present. -- Keep the smoke test job stable for pull requests and manual dispatch. - -# Implementation Plan (phased) -## Phase 1: Update workflow conditions -- Add job-level environment variables that map to existing secret values. -- Replace step-level `if` expression to use `env.*` variables instead of `secrets.*`. - -## Phase 2: Validate changes -- Run a local YAML sanity check (structure/format). -- Review the updated workflow lines to ensure no behavior regressions in test execution. - -# Acceptance Criteria -- `.github/workflows/ai_provider_smoke_test.yml` no longer references `secrets.*` directly in an `if` condition. -- Integration step condition evaluates based on environment variables derived from those secrets. -- Workflow remains readable and consistent with existing test setup. diff --git a/docs/plans/2026-04-02-07-52-UTC-node24-actions-migration-plan.md b/docs/plans/2026-04-02-07-52-UTC-node24-actions-migration-plan.md deleted file mode 100644 index 0aa28817..00000000 --- a/docs/plans/2026-04-02-07-52-UTC-node24-actions-migration-plan.md +++ /dev/null @@ -1,24 +0,0 @@ -# Background -GitHub Actions is deprecating Node.js 20 for JavaScript-based actions and will default to Node.js 24 starting June 2, 2026. This repository still references `actions/checkout@v4` in multiple workflow files. - -# Goals -- Replace deprecated `actions/checkout@v4` usage with a Node.js 24-compatible release. -- Keep workflow behavior unchanged outside of the action version bump. -- Validate workflow references after the update. - -# Implementation Plan (phased) -## Phase 1: Locate deprecated action references -- Scan `.github/workflows` for `actions/checkout@v4` and record impacted files. - -## Phase 2: Update action versions -- Replace each `actions/checkout@v4` reference with `actions/checkout@v5` in workflow YAML files. -- Leave already-updated or SHA-pinned modern references unchanged. - -## Phase 3: Validate -- Re-scan workflows to ensure no `actions/checkout@v4` references remain. -- Run `git diff --stat` for a concise change summary. - -# Acceptance Criteria -- No workflow file under `.github/workflows` contains `actions/checkout@v4`. -- Updated workflows use `actions/checkout@v5` where `v4` was previously used. -- A repository scan confirms all deprecated references were removed. diff --git a/docs/plans/2026-04-02-08-34-UTC-cloudflare-github-app-review-thread-resolver.md b/docs/plans/2026-04-02-08-34-UTC-cloudflare-github-app-review-thread-resolver.md deleted file mode 100644 index 49f552ff..00000000 --- a/docs/plans/2026-04-02-08-34-UTC-cloudflare-github-app-review-thread-resolver.md +++ /dev/null @@ -1,52 +0,0 @@ -# Plan: Cloudflare GitHub App review-thread resolver - -## Problem - -GitHub Actions workflow runs triggered by `copilot-swe-agent[bot]` remain gated by Copilot workflow approval, so repository-native automation cannot reliably resolve outdated review threads after Copilot finishes a PR iteration. - -## Approach - -Move the resolver outside GitHub Actions and into a GitHub App webhook hosted on Cloudflare Workers. The worker will: - -1. Receive `issue_comment` webhooks directly from GitHub -2. Verify the webhook signature -3. Filter only PR comments created by `copilot-swe-agent[bot]` -4. Use the webhook installation ID to mint an installation token -5. Query review threads and resolve only `isOutdated && !isResolved` threads -6. Optionally post a summary comment back to the PR - -## Files - -| File | Purpose | -|---|---| -| `.github/cloudflare/copilot-review-thread-resolver-worker.mjs` | Single-file Cloudflare Worker to copy into a Worker project | -| `docs/plans/2026-04-02-08-34-UTC-cloudflare-github-app-review-thread-resolver.md` | This plan | - -## GitHub App setup - -- Webhook events: `issue_comment` -- Permissions: - - `Metadata: Read` - - `Pull requests: Write` - - `Issues: Write` - -## Worker secrets - -Required: - -- `GITHUB_WEBHOOK_SECRET` - -Choose one auth mode: - -- GitHub App mode: - - `GITHUB_APP_ID` - - `GITHUB_APP_PRIVATE_KEY` -- Personal token mode: - - `GITHUB_TOKEN` (or `GH_TOKEN`) - -## Notes - -- No filtering by comment body text; identity-only filtering keeps the trigger robust. -- The resolve operation is idempotent because already-resolved or non-outdated threads are skipped. -- The Worker file is intentionally dependency-free so it can be pasted directly into Cloudflare. -- Personal token mode is the fastest path when GitHub App setup is too cumbersome; GitHub App mode remains the cleaner long-term option. diff --git a/docs/plans/2026-04-02-09-09-UTC-sidebar-back-button.md b/docs/plans/2026-04-02-09-09-UTC-sidebar-back-button.md deleted file mode 100644 index ac80f48f..00000000 --- a/docs/plans/2026-04-02-09-09-UTC-sidebar-back-button.md +++ /dev/null @@ -1,20 +0,0 @@ -# Background -The chat left sidebar currently shows a plain "Navigation" label at the top. Users expect a back-style control (left arrow) on the left side of this header so they can explicitly close the sidebar panel. - -# Goals -- Add a visible back/close affordance to the left of the "Navigation" title. -- Ensure tapping the control closes the sidebar reliably. -- Keep existing navigation actions and static tiles unchanged. - -# Implementation Plan (phased) -1. Update `ChatNavigationPage` header layout from a single text widget to a row containing: - - a left-arrow `IconButton` for closing, - - the existing "Navigation" title text. -2. Implement a small internal close handler that calls `Navigator.maybePop(context)` so the panel can dismiss without requiring callback wiring. -3. Extend widget tests to verify the back button is rendered and that tapping it pops the route when the page is pushed. - -# Acceptance Criteria -- A left-arrow back button is visible at the top-left of the sidebar, immediately before "Navigation". -- Tapping the back button closes the sidebar/page. -- Existing taps on "Manage Agents" and "Settings" still behave as before. -- Validation command succeeds: `cd apps/mobile_chat_app && flutter test test/chat_navigation_page_test.dart`. diff --git a/docs/plans/2026-04-02-09-40-UTC-github-webhook-cloudflare-skill.md b/docs/plans/2026-04-02-09-40-UTC-github-webhook-cloudflare-skill.md deleted file mode 100644 index 0e4ab53c..00000000 --- a/docs/plans/2026-04-02-09-40-UTC-github-webhook-cloudflare-skill.md +++ /dev/null @@ -1,35 +0,0 @@ -# Plan: GitHub webhook / Cloudflare worker skill - -## Background - -This repository now has an external Cloudflare Worker path for GitHub PR automation, but future agents may not discover that capability unless it is documented in the repository's skill system and surfaced in the main agent instructions. - -## Goals - -- Add a Codex skill that explains the repository's GitHub webhook to Cloudflare Worker integration. -- Document where the Worker code lives, how it is deployed, and how the GitHub webhook links to it. -- Make future agents more likely to choose this path when working on Copilot PR automation or external webhook debugging. - -## Implementation Plan - -### Phase 1: Add a repository skill - -- Create `.codex/skills/github-webhook-cloudflare-worker/SKILL.md`. -- Describe the current Worker source, Wrangler project, deployed URL, webhook endpoint, auth modes, and debugging commands. -- Explain when to use `issue_comment`, `pull_request:synchronize`, and `push`. - -### Phase 2: Surface the skill in repository instructions - -- Update `AGENTS.md` with a short note telling agents to use the new skill first when working on Copilot PR automation that must avoid GitHub Actions approval gates. - -### Phase 3: Validate repository references - -- Confirm the skill points to the real repository paths under `.github/cloudflare/`. -- Confirm the documented commands are consistent with the Wrangler project layout. - -## Acceptance Criteria - -- A new skill exists at `.codex/skills/github-webhook-cloudflare-worker/SKILL.md`. -- `AGENTS.md` explicitly points future agents to the new skill. -- The skill explains the relationship between the GitHub repository webhook and the Cloudflare Worker endpoints. -- The skill includes concrete operational commands for checking, deploying, and debugging the Worker. diff --git a/docs/plans/2026-04-02-13-25-UTC-sidebar-close-on-back-button.md b/docs/plans/2026-04-02-13-25-UTC-sidebar-close-on-back-button.md deleted file mode 100644 index 86bba810..00000000 --- a/docs/plans/2026-04-02-13-25-UTC-sidebar-close-on-back-button.md +++ /dev/null @@ -1,21 +0,0 @@ -# Sidebar Close on Back Button - -## Background -In the chat screen drawer navigation, tapping the top-left back arrow currently plays the tap animation but does not close the sidebar in drawer usage. - -## Goals -- Ensure tapping the back arrow closes the open drawer when `ChatNavigationPage` is rendered inside a `Scaffold.drawer`. -- Add widget test coverage for drawer-close behavior. - -> **Note:** An initial approach considered preserving a `Navigator.maybePop` fallback for standalone (non-drawer) usage. After further review this was superseded by the drawer-only cleanup plan (`2026-04-02-13-30-UTC-chat-navigation-drawer-only-cleanup.md`), which removes standalone-route fallback entirely. The implementation reflects the drawer-only direction. - -## Implementation Plan -1. Update `ChatNavigationPage._closeNavigation` to close the drawer via `ScaffoldState.closeDrawer()` (no route-pop fallback). -2. Make `onActionSelected` a required callback; remove `Navigator.pop` fallback for action taps. -3. Add/adjust widget tests in `chat_navigation_page_test.dart` to verify drawer closes after tapping the back arrow and remove obsolete standalone-route tests. -4. Run bootstrap and targeted Flutter tests. - -## Acceptance Criteria -- When the sidebar is open from `Scaffold.drawer`, tapping the back arrow closes the drawer. -- `ChatNavigationPage` no longer contains standalone-route fallback logic. -- `flutter test` for `chat_navigation_page_test.dart` passes. diff --git a/docs/plans/2026-04-02-13-30-UTC-chat-navigation-drawer-only-cleanup.md b/docs/plans/2026-04-02-13-30-UTC-chat-navigation-drawer-only-cleanup.md deleted file mode 100644 index b465aafb..00000000 --- a/docs/plans/2026-04-02-13-30-UTC-chat-navigation-drawer-only-cleanup.md +++ /dev/null @@ -1,21 +0,0 @@ -# Chat Navigation Drawer-Only Cleanup - -## Background -`ChatNavigationPage` historically supported both drawer usage and standalone route usage. Current product interaction is drawer-based from `ChatScreen`, and dual-mode fallback logic introduces ambiguity and legacy behavior. - -## Goals -- Confirm and align `ChatNavigationPage` with current drawer-only interaction. -- Remove standalone-route fallback behavior from navigation page actions. -- Keep current UX behavior unchanged for users in the drawer flow. - -## Implementation Plan (phased) -1. Update `ChatNavigationPage` to require an action callback and remove `Navigator.pop` fallback behavior. -2. Change back-button close logic to drawer-only close behavior (no route-pop fallback). -3. Update widget tests to validate drawer-centric behavior and remove obsolete standalone route tests. -4. Run targeted Flutter tests for the navigation page. - -## Acceptance Criteria -- `ChatNavigationPage` no longer contains standalone-route fallback logic. -- Back button closes the open drawer. -- Action taps still emit expected callbacks. -- `flutter test test/chat_navigation_page_test.dart` passes. diff --git a/docs/plans/2026-04-02-15-20-UTC-vercel-oauth-log-analysis.md b/docs/plans/2026-04-02-15-20-UTC-vercel-oauth-log-analysis.md deleted file mode 100644 index 32920a91..00000000 --- a/docs/plans/2026-04-02-15-20-UTC-vercel-oauth-log-analysis.md +++ /dev/null @@ -1,33 +0,0 @@ -# Background -The user reported a GitHub OAuth login loop in the frontend: the app redirects to GitHub and back, but the user remains unauthenticated. - -A Vercel logs URL was provided: -`https://vercel.com/askman-dev/bricks/logs?selectedLogId=f69tr-1775142530706-f3f1fa322565` - -The task is to inspect the last 10 logs and identify whether the redirect/login issue is visible from logs. - -# Goals -1. Pull recent Vercel deployment logs via API. -2. Check the last 10 events for OAuth/auth callback failures. -3. Correlate findings with the reported frontend behavior. -4. Propose concrete next debugging steps. - -# Implementation Plan (phased) -## Phase 1: Collect logs -- Use `tools/vercel/fetch_latest_deployment_logs.sh 10` to fetch the most recent 10 deployment events. -- Confirm deployment ID and timestamps. - -## Phase 2: Correlate with reported symptom -- Check whether last 10 logs include GitHub OAuth callback entries, token exchange failures, or cookie/session errors. -- If not present, verify if logs are build-time only and identify any build issues affecting auth endpoints. - -## Phase 3: Recommend remediation path -- If auth runtime logs are missing, fetch runtime logs for the target deployment/time window. -- Validate backend callback implementation and session persistence configuration (domain/SameSite/Secure). -- Re-test login flow and verify user state after callback. - -# Acceptance Criteria -- The latest 10 logs are inspected and summarized with UTC timestamps. -- A clear statement is made on whether redirect-loop evidence is visible in those logs. -- At least one plausible root cause is identified from observed log lines. -- Next-step commands are provided to gather missing runtime evidence. diff --git a/docs/plans/2026-04-02-15-30-UTC-oauth-cross-origin-token-loss.md b/docs/plans/2026-04-02-15-30-UTC-oauth-cross-origin-token-loss.md deleted file mode 100644 index 54c7497f..00000000 --- a/docs/plans/2026-04-02-15-30-UTC-oauth-cross-origin-token-loss.md +++ /dev/null @@ -1,45 +0,0 @@ -# Background -The user suspects this sequence in GitHub OAuth web login: -1. Browser starts on `https://bricks.askman.dev`. -2. Redirects to GitHub and back to `https://bricks.askman.dev` callback. -3. Then redirects to `https://bricks-aoqjuy2sr-askman-dev.vercel.app` but appears to lose parameters and lands unauthenticated. - -The task is to validate this hypothesis and identify where auth state is lost. - -# Goals -1. Verify whether `return_to` is preserved in OAuth state. -2. Verify how callback passes auth token back to Flutter web app. -3. Determine whether cross-origin redirects (`bricks.askman.dev` -> `*.vercel.app`) can lose token persistence. -4. Propose a concrete next-step fix strategy. - -# Implementation Plan (phased) -## Phase 1: Confirm return_to propagation -- Inspect backend `/api/auth/github` behavior. -- Trigger a real redirect response and decode `state` payload to confirm `returnTo` value. - -## Phase 2: Confirm token handoff mechanism -- Inspect callback response builder in backend. -- Check whether token is returned in URL params vs browser storage. - -## Phase 3: Cross-origin risk assessment -- Compare callback origin and `returnTo` origin behavior. -- Validate whether `localStorage` write happens on callback origin only. -- Determine impact when final redirect target is a different origin. - -## Phase 4: Remediation path -- Prefer same-origin callback and return target for web login. -- If cross-origin is required, implement explicit token transfer (e.g., fragment/postMessage + one-time exchange), then clear token artifact. -- Add integration checks for preview-domain and custom-domain flows. - -# Acceptance Criteria -- Evidence shows whether state contains the expected `returnTo` URL. -- Evidence shows whether token is delivered via localStorage or URL. -- A clear conclusion states whether cross-origin redirect can explain "returned but still not logged in". -- Next implementation step is actionable and testable. - -# Findings (current investigation) -- Backend includes `returnTo` in OAuth `state` and stores nonce server-side. -- Live redirect test confirmed `state.returnTo = https://bricks-aoqjuy2sr-askman-dev.vercel.app/`. -- Callback success page writes token to `localStorage` key `flutter.auth_token` and then `window.location.replace(redirectTo)`. -- `localStorage` is origin-scoped; therefore token written on `https://bricks.askman.dev` is not available on `https://bricks-aoqjuy2sr-askman-dev.vercel.app`. -- This explains why "no URL params + returned unauthenticated" can happen even when OAuth callback is successful. diff --git a/docs/plans/2026-04-03-00-10-UTC-oauth-cross-domain-login-fix.md b/docs/plans/2026-04-03-00-10-UTC-oauth-cross-domain-login-fix.md deleted file mode 100644 index 140815c2..00000000 --- a/docs/plans/2026-04-03-00-10-UTC-oauth-cross-domain-login-fix.md +++ /dev/null @@ -1,33 +0,0 @@ -# Background -Preview environments use dynamic Vercel subdomains that differ from the custom production domain. -The OAuth flow intentionally redirects across domains, but users can return unauthenticated after GitHub login. - -Current callback behavior writes JWT to callback-origin localStorage and then redirects to `return_to`. -For cross-origin redirects, the destination origin cannot read callback-origin localStorage. - -# Goals -1. Preserve successful login across cross-origin callback → return_to redirects. -2. Keep same-origin behavior unchanged. -3. Avoid leaking token in query parameters sent to servers. -4. Add automated tests for redirect-target generation behavior. - -# Implementation Plan (phased) -## Phase 1: Backend cross-origin redirect token handoff -- Add a helper that builds post-login redirect URLs. -- If callback origin differs from return_to origin, attach JWT in URL fragment (`#auth_token=...`). -- Keep same-origin redirects unchanged. - -## Phase 2: Frontend fragment token consumption -- Add web-only fragment parser to read `auth_token` from location hash. -- Persist token through AuthService startup path. -- Remove consumed token from URL via history.replaceState. - -## Phase 3: Validation -- Add backend unit tests covering same-origin, cross-origin, and existing-fragment cases. -- Run Node backend test suite. - -# Acceptance Criteria -- Cross-origin OAuth redirect includes `#auth_token` fragment. -- Web startup consumes fragment token and login state becomes authenticated. -- Same-origin flow does not append token to URL. -- Backend tests pass with new redirect-target cases. diff --git a/docs/plans/2026-04-03-03-38-UTC-sidebar-back-button-alignment.md b/docs/plans/2026-04-03-03-38-UTC-sidebar-back-button-alignment.md deleted file mode 100644 index b40d9313..00000000 --- a/docs/plans/2026-04-03-03-38-UTC-sidebar-back-button-alignment.md +++ /dev/null @@ -1,26 +0,0 @@ -# Background -The sidebar back button row in the mobile chat app does not align precisely with the parent chat page app bar menu button row. The goal is to make the drawer-open header layout visually and structurally match the parent page header alignment. - -# Goals -- Align the sidebar back button hit area and icon placement to the same horizontal/vertical geometry used by the chat app bar leading menu button. -- Align the sidebar header title baseline and spacing with app bar title conventions. -- Keep behavior unchanged (back button still closes the drawer). - -# Implementation Plan (phased) -## Phase 1: Inspect and normalize header structure -- Update `ChatNavigationPage` header row to mirror app bar-like dimensions (`kToolbarHeight`) and leading slot width. -- Replace ad-hoc paddings with deterministic spacing that matches Material app bar leading/title layout. - -## Phase 2: Style and semantics parity -- Use a back icon style that matches app bar icon sizing and visual weight. -- Apply title text style from `Theme.of(context).textTheme.titleLarge` to align with app bar typography. - -## Phase 3: Validate -- Run formatter for modified Dart file. -- Run targeted Flutter widget test(s) for chat navigation UI behavior. - -# Acceptance Criteria -- When the drawer is open, the back button row appears aligned to the same top-row geometry as the parent chat app bar menu row. -- Back button remains tappable and closes the drawer. -- Existing chat navigation tests pass after the change. -- Validation commands are executed: `./tools/init_dev_env.sh` and `cd apps/mobile_chat_app && flutter test test/chat_navigation_page_test.dart`. diff --git a/docs/plans/2026-04-03-04-10-UTC-api-key-visual-hint-merged.md b/docs/plans/2026-04-03-04-10-UTC-api-key-visual-hint-merged.md deleted file mode 100644 index e9b358b8..00000000 --- a/docs/plans/2026-04-03-04-10-UTC-api-key-visual-hint-merged.md +++ /dev/null @@ -1,26 +0,0 @@ -# Background -The Model Settings screen intentionally leaves the API key input empty for persisted configurations so users can save changes without overwriting secrets. This behavior is correct, but users can misread the empty field as "not configured". - -Earlier iterations explored a dedicated `hasStoredApiKey` flag and then a simpler approach using masked `api_key` returned by the backend. The final direction is to keep the simpler model and provide a clear visual cue only. - -# Goals -- Keep existing save semantics unchanged (blank API key input means do not update key). -- Show an in-field parenthesized gray hint when a server-side key exists but is hidden for safety. -- Keep implementation minimal and maintainable. - -# Implementation Plan (phased) -1. Use parsed `config.api_key` (masked from backend) as a display-state indicator. -2. Keep API key text controller empty in form hydration so masked value is never prefilled/submitted. -3. Render `hintText` in API key input when the active config has non-empty `apiKey`. -4. Keep helper text (`Leave blank to keep your existing key`) unchanged. -5. Cover behavior with widget test for persisted config with masked key. - -# Acceptance Criteria -- Persisted configs with a stored key show a one-line parenthesized hint in the API key input. -- No secret value is displayed in the input field. -- Leaving API key blank while editing still preserves existing key. -- Widget tests for model settings screen pass. - -## Validation commands -- `./tools/init_dev_env.sh` -- `cd apps/mobile_chat_app && flutter test test/model_settings_screen_test.dart` diff --git a/docs/plans/2026-04-03-05-03-UTC-multi-agent-channel-arbitration.md b/docs/plans/2026-04-03-05-03-UTC-multi-agent-channel-arbitration.md deleted file mode 100644 index c3ea92d2..00000000 --- a/docs/plans/2026-04-03-05-03-UTC-multi-agent-channel-arbitration.md +++ /dev/null @@ -1,86 +0,0 @@ -# Multi-Bot Channel Arbitration Plan - -## Background -We keep concept layers explicit and non-overlapping: -- **Conversation layer**: users interact with Bots. -- **Runtime layer**: Bots run on Agent execution loops. -- **Capability layer**: Skills are callable capabilities/prompts used by agents; Skills are **not** conversation participants. - -Each Bot can be associated with one or more Skills, and one default Skill profile can define the bot's baseline prompt/capability posture. -By baseline, the two default bots are `ask` and `image_generation`. - -This plan stays intentionally narrow: arbitration policy only. -It assumes Channel/Thread/Session identities and asynchronous task transport are defined by companion plans (including optional thread mode where sessions can be channel-scoped). - -## Goals -1. Define a clear arbitration model for selecting one bot per user turn within a channel context. -2. Support both direct mode (single active bot) and arbitration mode (multiple active bots) under one routing pipeline. -3. Use a single LLM judge pass to score all candidate bots for the current message. -4. Route ties and invalid judge output to a configured default bot deterministically. -5. Ensure decisions are observable, auditable, and testable. - -## Implementation Plan (phased) - -### Phase 1: Domain and protocol definition -- Define bot identity schema: - - `bot_id` - - `display_name` - - `default_skill_id` (default prompt/capability profile for this bot) -- Define LLM scoring output schema for each candidate bot: - - `bot_id` - - `score` (0-1) - - `confidence` (0-1) - - `reason` -- Define arbitration result schema: - - `selected_bot_id` - - `selected_score` - - `tie_detected` (boolean) - - `tie_bot_ids` - - `fallback_to_default_bot` (boolean) - - `decision_reason` - - `trace_id` - -### Phase 2: Routing engine behavior -- Implement unified message routing decision stage: - 1. ingest message context, - 2. load active bots for the current channel/thread/session, - 3. run mode check: - - if one active bot => direct dispatch, - - if many active bots => LLM judge arbitration. -- Build one structured LLM prompt that receives: - - user message, - - channel/thread/session context summary, - - candidate bot profiles (including default skill references). -- Parse structured judge output and rank bots by score/confidence. - -### Phase 3: Decision policy -- Select the bot with the highest score/confidence. -- If two or more bots share the same top score, route to `default_bot_id`. -- If judge output is invalid/incomplete, route to `default_bot_id` and mark as fallback. -- Persist per-bot scoring and final decision trace for replay/debugging. - -### Phase 4: Operations and quality -- Add structured logs/metrics: - - arbitration latency, - - average score by bot, - - win rate by bot, - - tie rate, - - default-bot fallback rate. -- Add acceptance tests for: - - single-bot direct routing, - - multi-bot highest-score selection, - - tie => default bot, - - invalid judge output => default bot. - -## Acceptance Criteria -1. In a channel/thread/session with exactly one active bot, each user message is directly dispatched to that bot without arbitration. -2. In a channel/thread/session with two or more active bots, each user message triggers one judge decision that evaluates all candidate bots. -3. The selected responder is always the candidate bot with the highest score. -4. If the top score is tied, the system always routes to the configured default bot. -5. Every arbitration stores a machine-readable decision record with per-bot scores, winner, and trace ID. -6. If arbitration output cannot be parsed, the system falls back to the default bot and records the fallback reason. - -## Validation Commands -- `npm run type-check` -- `npm run test` -- `flutter analyze` (if client integration changes are involved) diff --git a/docs/plans/2026-04-03-09-26-UTC-async-task-channel-thread-session-and-default-skills.md b/docs/plans/2026-04-03-09-26-UTC-async-task-channel-thread-session-and-default-skills.md deleted file mode 100644 index 87b49f15..00000000 --- a/docs/plans/2026-04-03-09-26-UTC-async-task-channel-thread-session-and-default-skills.md +++ /dev/null @@ -1,97 +0,0 @@ -# Async Task Transport + Channel/Thread/Session + Default Bots Baseline Plan - -## Background -Current chat interaction is mostly request/stream-response oriented and does not provide a durable asynchronous task lifecycle under unstable networks. -At the same time, routing concepts need stronger identity boundaries: Channel, Thread, and Session must be first-class entities across backend and client, with Thread created on demand. - -We align semantics as follows: -- users converse with **bots**, -- bots are conversation participants and routable responders, -- each bot can reference one or more **skills**, -- skills provide prompt/capability profiles and callable functions inside agent loops, -- skills are **not** conversation participants. - -This baseline introduces two default bots shared by backend and client: -- `ask` -- `image_generation` - -This plan is complementary to `2026-04-03-05-03-UTC-multi-agent-channel-arbitration.md`: -- This document defines transport, entity identity, and default bot baseline. -- The arbitration document defines how one bot is selected when multiple bots are active. - -## Goals -1. Define an asynchronous task lifecycle contract that survives transient client/network interruptions. -2. Define Channel/Thread/Session entities and stable IDs used by both backend and client. -3. Define a default bot baseline (`ask`, `image_generation`) and how bots bind to skills. -4. Keep scope minimal and implementation-ready without introducing non-essential orchestration features. - -## Implementation Plan (phased) - -### Phase 1: Core entity model (Channel / Thread / Session / Bot) -- Define entity responsibilities and ownership: - - `channel_id`: top-level collaboration/routing scope. - - `thread_id`: optional sub-conversation scope under a channel (created on demand). - - `session_id`: runtime interaction instance bound to either a channel directly or to a thread. - - `bot_id`: conversation participant identity for routing and attribution. -- Define required relationships: - - one channel contains many threads, - - one thread contains many sessions over time when thread mode is used, - - one channel may also own sessions directly without thread creation, - - each user-visible message and task references `channel_id` + `session_id`, and optionally `thread_id`, plus resolved `bot_id`. -- Define default resolution rules: - - if no channel is specified, route to `default_channel_id`. - - if no thread is specified, the session may run at channel scope; create a thread only when thread mode is explicitly requested. - -### Phase 2: Async task lifecycle contract -- Introduce task envelope fields: - - `task_id`, `channel_id`, `session_id`, - - optional `thread_id`, - - `bot_id`, `resolved_skill_id`, - - `created_at`. -- Define minimal state machine: - - `accepted` -> `dispatched` -> (`completed` | `failed` | `cancelled`). -- Define reliability semantics: - - idempotent submission key for safe retries, - - backend acknowledgement on acceptance, - - resumable client sync by cursor/checkpoint for missed updates. -- Define transport behavior: - - pull-based state sync is source of truth, - - push/stream events are optional acceleration only. - -### Phase 3: Default bots baseline and bot-skill binding (backend + client unified) -- Define canonical default bots: - - `ask` - - `image_generation` -- Define bot-skill binding contract (skills are not participants): - - each bot has `default_skill_id`, - - each bot may have additional callable skills, - - skill invocation happens inside the selected bot's agent loop. -- Define default selection policy: - - if bot is selected and no skill override is provided, use bot's `default_skill_id`, - - if requested skill is unavailable/disabled for that bot, fallback to `ask` and record reason. -- Define shared metadata: - - Bot metadata: `bot_id`, `display_name`, `default_skill_id`. - - Skill metadata: `skill_id`, `description`, `input_mode`, `output_mode`, `is_default`. -- Define client UX contract: - - bot switcher controls conversation participants, - - message/task record persists resolved `bot_id` and `resolved_skill_id` for replay and audit. - -### Phase 4: Integration boundaries with arbitration plan -- Keep arbitration as a separate decision stage; do not merge into transport logic. -- Ensure arbitration consumes the same entity IDs and bot registry defined here. -- Ensure fallback semantics are aligned: - - this plan: transport-level/default-skill resolution within selected bot, - - arbitration plan: decision-level default bot fallback. - -## Acceptance Criteria -1. A client can submit a message/task and receive an `accepted` acknowledgement with a durable `task_id`, even if response generation is not finished yet. -2. After simulated network interruption and reconnect, the client can recover missed task/message updates using cursor/checkpoint sync without data loss. -3. Every stored task/message record includes `channel_id`, `session_id`, resolved `bot_id`, and optional `thread_id` when thread mode is used. -4. If no skill override is specified for a selected bot, backend resolves to that bot's `default_skill_id` deterministically. -5. If `image_generation` is requested and available for the selected bot, the task is routed accordingly; if unavailable, fallback to `ask` is recorded. -6. Skills are not treated as conversation participants in routing decisions; bot selection remains the conversation-level decision unit. - -## Validation Commands -- `npm run type-check` -- `npm run test` -- `flutter analyze` diff --git a/docs/plans/2026-04-04-00-41-UTC-chat-ui-channel-thread-session-and-arbitration-ux.md b/docs/plans/2026-04-04-00-41-UTC-chat-ui-channel-thread-session-and-arbitration-ux.md deleted file mode 100644 index a6adbf68..00000000 --- a/docs/plans/2026-04-04-00-41-UTC-chat-ui-channel-thread-session-and-arbitration-ux.md +++ /dev/null @@ -1,37 +0,0 @@ -# Chat UI Channel/Thread/Session + Arbitration UX Plan - -## Background -The chat surface needs to expose channel and sub-thread context in the UI while preserving the existing component structure and minimizing implementation cost. The sidebar navigation should support channel management and the top app bar should support sub-section switching. In parallel, the UI should surface asynchronous task lifecycle and arbitration/direct routing context aligned with the existing architecture plans. - -## Goals -1. Adjust sidebar behavior to full-width drawer and place settings/sessions/new-channel controls on the navigation header row. -2. Show channel list under "Manage Agents" with a default channel and in-list create-channel affordance. -3. Add top-right sub-section selector with "新建子区" and "主区" fixed entries plus user-created entries. -4. Surface channel/thread/session IDs and direct vs arbitration mode in the chat UI. -5. Expose task lifecycle status and routing metadata in message rendering. - -## Implementation Plan (phased) -### Phase 1: Navigation and channel controls -- Expand drawer width to 100% viewport. -- Update `ChatNavigationPage` to include right-side action icons for settings/sessions/new channel. -- Add channel list model and selected channel rendering under "Manage Agents". -- Add "新建频道" button and timestamp-based naming when creating channels. - -### Phase 2: Chat header and sub-section switching -- Add app bar action for sub-section dropdown. -- Include fixed entries (`新建子区`, `主区`) and dynamic user-created sub-sections per channel. -- Maintain active channel + sub-section state locally in chat screen state. - -### Phase 3: Async/arbitration UX alignment -- Add context bar displaying channel/thread/session IDs and direct/arbitration mode. -- Extend chat message view model with task lifecycle fields and routing metadata. -- Render task status, IDs, fallback markers, and recovered-sync markers in message list. -- Add a lightweight reconnect-sync simulation action for UI verification. - -## Acceptance Criteria -1. Sidebar opens as full-width drawer and shows settings/sessions/new-channel actions on the same row. -2. Channel list is visible under "Manage Agents", includes default channel, and supports timestamp channel creation. -3. App bar contains sub-section dropdown with required fixed options and dynamic user-created entries. -4. Message/task UI includes task state (`accepted`, `dispatched`, `completed`, `failed`, `cancelled`) and task IDs. -5. Context bar shows channel/thread/session and routing mode. -6. Existing chat navigation widget tests pass with updated API and channel list expectations. diff --git a/docs/plans/2026-04-04-07-34-UTC-pr117-unimplemented-bot-registry.md b/docs/plans/2026-04-04-07-34-UTC-pr117-unimplemented-bot-registry.md deleted file mode 100644 index 26e1a4e0..00000000 --- a/docs/plans/2026-04-04-07-34-UTC-pr117-unimplemented-bot-registry.md +++ /dev/null @@ -1,28 +0,0 @@ -# Background -PR #117 的评论指出:当前实现里 bot/skill 仍是硬编码字符串,缺少类型化的 bot registry 与默认回退规则,属于已识别但未完成的功能。 - -# Goals -1. 为聊天模块补齐类型化 Bot/Skill 元数据模型。 -2. 引入 Bot Registry,统一处理 bot 与默认 skill 解析。 -3. 当请求的 bot 不存在或不可用时,显式回退到默认 `ask` bot。 -4. 将该解析结果接入消息元数据(`resolvedBotId` / `resolvedSkillId` / fallback 标记)。 - -# Implementation Plan (phased) -## Phase 1: Domain model -- 新增 `ChatBot`、`ChatSkill`、`ChatBotRegistry` 与 `ResolvedChatDispatch` 类型。 -- 内置默认 bot:`ask` 与 `image_generation`。 - -## Phase 2: Routing integration -- 在 `ChatScreen` 中用 registry 解析当前请求 bot。 -- 去掉发送流程里对 skill id 的硬编码分支。 - -## Phase 3: Validation -- 新增单元测试覆盖: - - 已注册 bot 的默认 skill 解析; - - 未注册 bot 的默认回退; - - bot 默认 skill 缺失时的安全回退。 - -# Acceptance Criteria -- 当 active agent 是 `image_generation` 时,消息包含 `resolvedSkillId=image_generation.default`。 -- 当 active agent 未注册时,消息落到 `resolvedBotId=ask` 且 `fallbackToDefaultBot=true`。 -- `flutter test` 通过新增与现有测试。 diff --git a/docs/plans/2026-04-04-08-10-UTC-pr117-review-comment-gap-implementation.md b/docs/plans/2026-04-04-08-10-UTC-pr117-review-comment-gap-implementation.md deleted file mode 100644 index f2a6093e..00000000 --- a/docs/plans/2026-04-04-08-10-UTC-pr117-review-comment-gap-implementation.md +++ /dev/null @@ -1,30 +0,0 @@ -# Background -PR #117 的评论指出多项“计划文档已写但代码未实现”的缺口,尤其集中在 bot arbitration、channel/thread/session 语义和 async task 生命周期。需要统一补齐可运行的基础实现,而非零散修补。 - -# Goals -1. 建立可复用的类型化协议:Bot/Arbitration、Channel/Thread/Session、Task/Ack/Checkpoint。 -2. 将上述协议接入 ChatScreen 主流程(发送、路由、恢复同步、上下文展示)。 -3. 增加回归测试,覆盖 tie fallback、default channel 解析和 task ack 状态推进。 - -# Implementation Plan (phased) -## Phase 1: 协议与实体建模 -- 新增 `chat_arbitration.dart`:候选评分、决策结果、tie 检测和默认回退。 -- 新增 `chat_topology.dart`:`ChatChannel`/`ChatThread`/`ChatSessionScope` 及 default channel 解析。 -- 新增 `chat_task_protocol.dart`:task envelope、ack、checkpoint 以及 message 状态推进。 -- 扩展 `ChatMessage` 元数据字段,覆盖 idempotency、acknowledged_at、cursor、tie 相关字段。 - -## Phase 2: 发送链路整合 -- `ChatScreen` 使用 arbitration engine 做统一决策,不再只靠单点布尔分支。 -- 发送时生成 envelope + ack,落盘到消息元数据并展示 cursor。 -- 引入 thread mode 显式开关,仅开启后允许创建子区。 -- 断线恢复模拟改为 checkpoint-aware 行为。 - -## Phase 3: 测试 -- 新增 arbitration engine 测试(最高分选择、tie 回退默认 bot)。 -- 新增 topology + task protocol 测试(default channel、ack 状态推进)。 - -# Acceptance Criteria -- 多候选 bot 场景可输出结构化 arbitration 结果并在 tie 时回退默认 bot。 -- 缺失/非法 channel 请求会解析到默认 channel。 -- 消息在 ack 后具备 `dispatched` 状态与 checkpoint cursor。 -- `flutter test` 覆盖新增测试并通过。 diff --git a/docs/plans/2026-04-04-08-28-UTC-pr117-fullstack-review-and-hardening.md b/docs/plans/2026-04-04-08-28-UTC-pr117-fullstack-review-and-hardening.md deleted file mode 100644 index 3616a3ac..00000000 --- a/docs/plans/2026-04-04-08-28-UTC-pr117-fullstack-review-and-hardening.md +++ /dev/null @@ -1,23 +0,0 @@ -# Background -用户要求基于 PR #117 的需求做一次从需求到实现的全栈审查(后端、架构、数据库、客户端),并把不合理实现做实质修正。 - -# Goals -1. 形成一份可执行的全栈审查结论(需求、后端、架构、数据库、客户端)。 -2. 修复本轮实现中的关键技术债:仲裁打分逻辑不能依赖伪随机/字符串哈希。 -3. 保持当前代码可运行并通过现有测试。 - -# Implementation Plan (phased) -## Phase 1: 评审产物 -- 新增全栈审查文档,按层次列出现状、风险、已修复项、待办项。 - -## Phase 2: 代码硬化 -- 将 `ChatArbitrationEngine` 从“botId 派生分数”改为“参与者概率权重”打分。 -- 更新 ChatScreen 调用契约与测试用例。 - -## Phase 3: 验证 -- 执行格式化与 `flutter test`,确保修改不会破坏现有行为。 - -# Acceptance Criteria -- 审查文档覆盖需求、后端、架构、数据库、客户端五个维度。 -- 仲裁引擎不再使用 botId 推导分数。 -- `flutter test` 全量通过。 diff --git a/docs/plans/2026-04-07-03-01-UTC-navigation-sidebar-buttons.md b/docs/plans/2026-04-07-03-01-UTC-navigation-sidebar-buttons.md deleted file mode 100644 index 41609a7c..00000000 --- a/docs/plans/2026-04-07-03-01-UTC-navigation-sidebar-buttons.md +++ /dev/null @@ -1,25 +0,0 @@ -# Background -The chat navigation drawer currently exposes settings and channel creation actions in multiple places, including a three-button row and additional duplicated entries further down the panel. This causes visual clutter and inconsistent action placement. - -# Goals -- Move the Settings entry into the same row as the Navigation title, aligned to the right side. -- Place the New Channel action on that same title row, to the right of Settings, making it the rightmost action. -- Remove the old three-button action row (Settings / Sessions / New Channel). -- Remove the extra New Channel button below “Manage Agents”. -- Keep a section header labeled “频道” in the same vertical area where the removed lower New Channel button used to be, and treat it as the header for the channel list. -- Ensure only one Settings entry remains in the drawer. - -# Implementation Plan (phased) -1. Update `ChatNavigationPage` header layout to include title-row action buttons (Settings + New Channel) on the right. -2. Delete the legacy action row containing Settings / Sessions / New Channel. -3. Remove duplicated lower controls (extra New Channel button and trailing Settings list tile). -4. Add a localized channels section label (`频道`) above the channel list where the lower New Channel button was. -5. Update widget tests to reflect the new UI structure and verify deduplication. - -# Acceptance Criteria -- The drawer shows a single Settings entry, rendered as an icon button on the Navigation title row. -- The drawer title row contains New Channel to the right of Settings (rightmost action). -- No three-button action row is present. -- No New Channel button appears under “Manage Agents”. -- A “频道” section heading appears above the channel list. -- Relevant Flutter widget tests pass. diff --git a/docs/plans/2026-04-07-03-22-UTC-channel-button-relocation.md b/docs/plans/2026-04-07-03-22-UTC-channel-button-relocation.md deleted file mode 100644 index cf74a6fa..00000000 --- a/docs/plans/2026-04-07-03-22-UTC-channel-button-relocation.md +++ /dev/null @@ -1,24 +0,0 @@ -# Background -The chat navigation drawer currently places the channel creation action in the top toolbar as an icon-only control. The requested UX is to move this action into the "频道" group header so the action is contextually grouped with channel management and rendered as an icon+text button. - -# Goals -- Move the "新建频道" action from the top-right navigation header into the "频道" section header row. -- Render the relocated action with an icon and text label. -- Keep existing action wiring (`ChatNavigationAction.createChannel`) intact. -- Update widget tests to validate the new location/presentation. - -# Implementation Plan (phased) -1. Update `ChatNavigationPage` layout: - - Remove the top app bar `IconButton` for new-channel. - - Replace the simple "频道" header text with a row containing the section title and a trailing icon+text button. - - Keep spacing and visual hierarchy appropriate for drawer content. -2. Update tests in `chat_navigation_page_test.dart`: - - Replace tooltip assertion for old icon button with assertions for the new text/icon button. - - Ensure expectations still cover the section title and static navigation elements. -3. Run targeted Flutter widget tests for the affected file. - -# Acceptance Criteria -- In the navigation drawer, "新建频道" appears on the same line as the "频道" group title, aligned to the right. -- The "新建频道" control uses icon+text presentation instead of icon-only presentation. -- Tapping "新建频道" still emits `ChatNavigationAction.createChannel`. -- Updated widget tests pass via `cd apps/mobile_chat_app && flutter test test/chat_navigation_page_test.dart`. diff --git a/docs/plans/2026-04-07-03-50-UTC-navigation-agents-sidebar.md b/docs/plans/2026-04-07-03-50-UTC-navigation-agents-sidebar.md deleted file mode 100644 index 0d436259..00000000 --- a/docs/plans/2026-04-07-03-50-UTC-navigation-agents-sidebar.md +++ /dev/null @@ -1,48 +0,0 @@ -# Background - -The chat drawer currently exposes a standalone `Manage Agents` action and only supports a non-collapsible channel section. The requested UX requires agent management to move into Settings, and the drawer should show an `Agents` group with collapsible behavior and fallback empty-state guidance. - -# Goals - -- Move `Manage Agents` entry from drawer navigation into Settings, directly below Model Settings. -- Add a collapsible `Agents` section above the channel section in the drawer. -- Show existing agents in the new drawer section, or show `在设置中新建 Agents` when empty. -- Add a config icon+text button on the `Agents` section header that currently shows a toast saying `未开发的功能`. -- Make the channel section collapsible by tapping the section title. - -# Implementation Plan (phased) - -## Phase 1: Drawer data model and UI structure - -1. Extend chat navigation drawer input model to accept agent items. -2. Convert drawer widget to stateful to track expanded/collapsed state for `Agents` and `频道` groups. -3. Remove drawer `Manage Agents` list tile. -4. Render `Agents` section above channel section, with: - - tappable header title to toggle expand/collapse, - - right-side config icon+text button, - - toast on config button tap with `未开发的功能`. -5. Keep channel create button and list behavior under a collapsible channel header. - -## Phase 2: Screen integration - -1. Pass current loaded agent definitions from `ChatScreen` into drawer agent items. -2. Update Settings screen to add `Manage Agents` list tile under `Model Settings`, navigating to `AgentsScreen`. - -## Phase 3: Validation and regression checks - -1. Update/extend widget tests for drawer behavior: - - no standalone `Manage Agents` tile, - - agents header and empty-state text, - - expand/collapse behavior for agents and channels, - - config button shows expected toast. -2. Run targeted Flutter tests. - -# Acceptance Criteria - -- In drawer, `Manage Agents` no longer appears as a standalone list item. -- Drawer shows an `Agents` section above `频道` and displays loaded agents by name. -- When no agents exist, drawer shows `在设置中新建 Agents` in Agents section. -- Tapping `Agents` title toggles section expansion/collapse. -- Agents header config icon+button exists; tapping it shows a snackbar/toast with `未开发的功能`. -- Tapping `频道` title toggles section expansion/collapse. -- Settings screen contains `Manage Agents` item immediately below `Model Settings` and navigates to Agents screen. diff --git a/docs/plans/2026-04-07-04-27-UTC-research-reconnect-sync-button.md b/docs/plans/2026-04-07-04-27-UTC-research-reconnect-sync-button.md deleted file mode 100644 index 69f2c328..00000000 --- a/docs/plans/2026-04-07-04-27-UTC-research-reconnect-sync-button.md +++ /dev/null @@ -1,24 +0,0 @@ -# Background -用户询问移动端聊天页面中“模拟断线恢复同步”按钮的用途,需要基于代码路径确认其真实行为与触发条件。 - -# Goals -1. 定位该按钮在 UI 中的挂载位置与点击事件。 -2. 明确按钮触发后的状态变化、同步模拟时序与消息标记效果。 -3. 输出可直接给产品/测试使用的简明说明。 - -# Implementation Plan (phased) -## Phase 1: Code discovery -- 在 `apps/mobile_chat_app/lib/features/chat/chat_screen.dart` 中定位“模拟断线恢复同步”文案与回调函数。 -- 在同目录与 `widgets/message_list.dart` 中追踪恢复标记字段的展示逻辑。 - -## Phase 2: Behavior verification -- 阅读 `_simulateReconnectSync` 具体实现,确认空消息保护、同步中状态、延时、checkpoint 光标回填和 recovered 标记更新逻辑。 -- 对照上下文栏(Context Bar)确认“Syncing…”状态可见性。 - -## Phase 3: Response drafting -- 汇总按钮用途、限制条件与用户可见反馈,形成中文说明。 - -# Acceptance Criteria -- 能说明该按钮是“模拟”用途而非真实网络重连。 -- 能说明仅在已有消息时生效。 -- 能说明点击后可见反馈(顶部 Syncing、消息 Recovered 标记、checkpoint 光标行为)。 diff --git a/docs/plans/2026-04-07-04-31-UTC-remove-reconnect-sync-simulator.md b/docs/plans/2026-04-07-04-31-UTC-remove-reconnect-sync-simulator.md deleted file mode 100644 index c3863c05..00000000 --- a/docs/plans/2026-04-07-04-31-UTC-remove-reconnect-sync-simulator.md +++ /dev/null @@ -1,25 +0,0 @@ -# Background -用户要求移除聊天页面“模拟断线恢复同步”按钮,并删除对应模拟恢复能力,避免在正式交互流程中保留调试入口。 - -# Goals -1. 从聊天页面 UI 中移除“模拟断线恢复同步”按钮。 -2. 删除 `_simulateReconnectSync` 相关状态与逻辑实现。 -3. 保持聊天主流程(发送、展示、线程等)不受影响并通过静态检查。 - -# Implementation Plan (phased) -## Phase 1: Remove UI entry -- 删除 `chat_screen.dart` 中底部 `TextButton.icon`(sync 图标 + “模拟断线恢复同步”文案)。 -- 删除仅为该能力服务的同步进度条展示。 - -## Phase 2: Remove simulation logic -- 删除 `_syncingAfterReconnect` 状态字段。 -- 删除 `_simulateReconnectSync()` 方法。 -- 删除 Context Bar 中 `Syncing…` 的条件展示。 - -## Phase 3: Validate -- 运行仓库初始化脚本并执行 Flutter 分析命令,确认无编译/静态检查错误。 - -# Acceptance Criteria -- 界面中不再出现“模拟断线恢复同步”按钮。 -- 代码中不再包含 `_simulateReconnectSync` 与 `_syncingAfterReconnect`。 -- `flutter analyze` 对相关文件通过。 diff --git a/docs/plans/2026-04-07-04-35-UTC-chat-history-persistence-fix.md b/docs/plans/2026-04-07-04-35-UTC-chat-history-persistence-fix.md deleted file mode 100644 index 1c5e039b..00000000 --- a/docs/plans/2026-04-07-04-35-UTC-chat-history-persistence-fix.md +++ /dev/null @@ -1,19 +0,0 @@ -# Background -用户反馈在频道里发送消息并收到 AI 回复后,刷新页面会丢失历史对话,导致无法继续在同一上下文聊天。当前 `ChatScreen` 仅将消息保存在内存 `_messages`,没有持久化或启动时恢复流程。 - -# Goals -- 将用户消息和 AI 回复持久化存储。 -- 页面刷新/重启后恢复之前的会话消息。 -- 继续兼容当前频道/线程会话模型,不破坏发送与流式更新行为。 - -# Implementation Plan (phased) -1. 在 `ChatScreen` 引入会话持久化服务(基于 `SharedPreferences`),按当前 scope(channel + thread)保存消息列表。 -2. 在初始化流程完成后加载持久化消息并恢复 `_messages`,同时恢复最近 checkpoint cursor(如有)。 -3. 在消息增删改(append/update/stream completion/error/cancel)后自动触发持久化。 -4. 在切换频道、切换子区、创建频道/子区后,加载目标 scope 对应历史消息。 -5. 添加单元测试覆盖:消息序列化 round-trip、scope key 生成、持久化读写基本行为。 - -# Acceptance Criteria -- 发送消息并收到 AI 回复后刷新页面,消息仍显示,且可继续发送新消息。 -- 切换到不同频道/子区时,能加载对应历史,不与其他 scope 串线。 -- `flutter test`(至少针对 `apps/mobile_chat_app`)通过。 diff --git a/docs/plans/2026-04-07-05-11-UTC-backend-chat-history-persistence.md b/docs/plans/2026-04-07-05-11-UTC-backend-chat-history-persistence.md deleted file mode 100644 index e305ab7e..00000000 --- a/docs/plans/2026-04-07-05-11-UTC-backend-chat-history-persistence.md +++ /dev/null @@ -1,22 +0,0 @@ -# Background -当前刷新后聊天历史丢失问题,上一版通过前端 `SharedPreferences` 持久化修复,但这不满足“后端数据库持久化 + 跨平台加载”的要求。并且频道支持多 Agent 接话,历史必须由服务端统一存储,避免端侧隔离导致上下文不一致。 - -# Goals -- 移除前端本地聊天历史持久化实现。 -- 新增后端数据库会话历史持久化接口(按用户 + 会话 scope)。 -- 聊天页改为从后端加载/保存历史,刷新后可恢复并继续聊天。 -- 保留多 Agent 消息链路(用户消息 + 多 assistant 消息)完整存储。 - -# Implementation Plan (phased) -1. 在 `apps/node_backend` 新增 DB migration 建立 `chat_sessions`(user_id + session_id 唯一,messages JSON,checkpoint cursor,channel/thread metadata)。 -2. 新增 `chatHistoryService` 读写服务与 `routes/chat.ts`,提供鉴权后的 `GET /api/chat/history/:sessionId` 与 `PUT /api/chat/history/:sessionId`。 -3. 在 `app.ts` 注册 `/api/chat` 路由。 -4. 在移动端新增 `ChatHistoryApiService`,通过后端 API 读写历史;删除 `ChatHistoryStore`(shared_preferences)及其测试。 -5. 更新 `ChatScreen`:初始化/切换 scope 时从后端加载;消息变更后上传快照。 -6. 增加后端路由单测与前端 API 服务单测,验证跨平台依赖后端存储。 - -# Acceptance Criteria -- 聊天消息(用户 + 多 Agent 回复)会保存到后端数据库。 -- 刷新页面后可恢复历史并继续聊天,且不依赖前端本地缓存。 -- 同一账号在不同平台登录后,可加载同一 session scope 的历史。 -- `apps/node_backend` 与 `apps/mobile_chat_app` 相关测试通过。 diff --git a/docs/plans/2026-04-07-06-07-UTC-async-chat-transport-rearchitecture.md b/docs/plans/2026-04-07-06-07-UTC-async-chat-transport-rearchitecture.md deleted file mode 100644 index 3f3032c2..00000000 --- a/docs/plans/2026-04-07-06-07-UTC-async-chat-transport-rearchitecture.md +++ /dev/null @@ -1,28 +0,0 @@ -# Background -现有实现已经把历史放到后端,但仍是“整段会话快照覆盖”模型,不满足异步对话运输层要求(任务状态机、幂等提交、增量同步、断线恢复 replay)。这会在多 Agent 接话和多端并发写入时带来覆盖风险。 - -# Goals -- 设计并落地面向异步对话的后端权威传输层。 -- 用任务(task)+ 消息日志(message log)替代会话快照覆盖。 -- 支持幂等 task 接收、消息 upsert、按序号增量 sync。 -- 给出可执行迁移方案并在客户端接入新接口。 - -# Implementation Plan (phased) -1. 新增数据库表:`chat_tasks`、`chat_messages`、`chat_sync_checkpoints`,形成 task lifecycle + append/update log + session checkpoint 三层模型。 -2. 后端新增服务与路由: - - `POST /api/chat/tasks/accept`(幂等接收) - - `PUT /api/chat/messages/batch`(消息批量 upsert) - - `GET /api/chat/sync/:sessionId?afterSeq=`(增量拉取) -3. 客户端新增异步聊天 API 服务,接入 `acceptTask` + `upsertMessages` + `sync`。 -4. `ChatScreen` 改造为:发送时上报 task accepted;消息变化时批量 upsert;初始化/切换 scope 时通过 sync 拉取并重建状态。 -5. 迁移策略: - - 先上线双写(保留旧 `chat_sessions` 读路径兼容); - - 稳定后将读切到 `chat_messages`; - - 运行离线 backfill(把 `chat_sessions.messages` 展平成 `chat_messages`); - - 验证完成后下线旧快照写入。 - -# Acceptance Criteria -- 后端具备 task 接收幂等能力和消息日志增量同步能力。 -- 刷新/重连后可用 `afterSeq` 拉取缺失消息,不依赖全量覆盖。 -- 多 Agent 接话消息按服务端序列可重放,历史不会被并发覆盖。 -- `apps/node_backend` 类型检查/测试与 `apps/mobile_chat_app` 相关测试通过。 diff --git a/docs/plans/2026-04-07-09-04-UTC-fix-chat-session-migration-sqlite-cast.md b/docs/plans/2026-04-07-09-04-UTC-fix-chat-session-migration-sqlite-cast.md deleted file mode 100644 index 55d4aa5b..00000000 --- a/docs/plans/2026-04-07-09-04-UTC-fix-chat-session-migration-sqlite-cast.md +++ /dev/null @@ -1,25 +0,0 @@ -# Background -A production migration (`006_create_chat_sessions.sql`) fails on Turso/libSQL with `SQL_PARSE_ERROR: bad variable name`. The failing statement includes PostgreSQL cast syntax in a default value (`'[]'::jsonb`), which survives adaptation as `'[]'::TEXT` and is still invalid SQLite syntax. - -# Goals -1. Make migration adaptation robust for PostgreSQL cast syntax (`::type`) when running against SQLite/Turso. -2. Add regression coverage to prevent future migration failures from cast expressions. -3. Validate that migration adapter tests pass. - -# Implementation Plan (phased) -## Phase 1: Adapter fix -- Update `adaptMigrationForSqlite` in `apps/node_backend/src/db/migrate.ts` to remove PostgreSQL `::type` cast fragments during SQLite adaptation. -- Keep existing replacements (`JSONB -> TEXT`, `NOW() -> CURRENT_TIMESTAMP`, etc.) unchanged. - -## Phase 2: Regression tests -- Add a focused unit test in `apps/node_backend/src/db/migrate.test.ts` asserting cast removal behavior for expressions like `'[]'::jsonb`. -- Add/extend integration-like migration fixture test to ensure `006_create_chat_sessions.sql` style SQL is adapted to SQLite-safe output. - -## Phase 3: Verification -- Run the node backend migration adapter unit tests. -- Confirm the adapted SQL no longer contains `::` casts. - -# Acceptance Criteria -- `adaptMigrationForSqlite` output never includes PostgreSQL `::type` casts. -- A test explicitly verifies cast removal for `'[]'::jsonb`-style defaults. -- Relevant tests pass locally with Vitest. diff --git a/docs/plans/2026-04-07-09-28-UTC-chat-history-context-and-persistence-fix.md b/docs/plans/2026-04-07-09-28-UTC-chat-history-context-and-persistence-fix.md deleted file mode 100644 index 6fa65d23..00000000 --- a/docs/plans/2026-04-07-09-28-UTC-chat-history-context-and-persistence-fix.md +++ /dev/null @@ -1,37 +0,0 @@ -# Background -用户反馈了两个问题: -1. 多轮对话中,第二轮推理似乎没有带上前文(`1+3` 后再发 `+5` 未得到 `9`)。 -2. 消息在刷新时可能出现“空气泡”并丢失 AI 最终回复,尤其在 AI 回复完成前刷新。 - -当前实现里,Agent 会在本地会话中维护上下文,但请求模型时只发送了“当前用户消息”;同时消息持久化采用 500ms 防抖,且会先落库一个空内容的流式 assistant 占位消息,导致刷新后看到空泡。 - -# Goals -1. 修复多轮对话上下文组装:模型请求应携带历史消息而非仅当前轮。 -2. 修复刷新导致的消息持久化异常: - - 关键消息(尤其用户消息)立即持久化,减少刷新窗口内丢失。 - - 避免将“空内容 + 流式中”的 assistant 占位消息持久化到历史。 -3. 补充测试覆盖,防止回归。 - -# Implementation Plan (phased) -## Phase 1: 多轮上下文修复 -- 在 `RealModelGateway` 中新增/扩展生成接口,支持传入完整历史消息。 -- 在 `AgentSessionImpl` 中调用网关时传入 `ContextManager.messages`。 -- 后端 `/api/llm/chat` 请求体改为完整 `messages`(包含 system + 历史 user/assistant)。 - -## Phase 2: 持久化时序修复 -- 将聊天持久化分为: - - 立即持久化(用于用户发送、流结束、错误、停止等关键时刻)。 - - 防抖持久化(用于流式增量内容更新)。 -- 持久化前过滤“空内容且仍在 streaming 的 assistant 占位消息”。 -- 在页面 `dispose` 时尝试触发一次立即持久化(best effort)。 - -## Phase 3: 测试与验证 -- 为 `agent_core` 增加测试:验证 `sendMessage` 时网关拿到完整上下文。 -- 为 `mobile_chat_app` 的 `ChatHistoryApiService` 增加测试:验证占位 assistant 消息过滤规则。 -- 运行受影响测试套件。 - -# Acceptance Criteria -1. 在同一会话中发送 `1+3` 后再发送 `+5`,请求模型的 messages 中包含上一轮 user/assistant 历史。 -2. 用户发送后立即刷新,不会出现仅有空内容 assistant 气泡的持久化记录。 -3. 正常完成的 assistant 回复仍可持久化并在刷新后可见。 -4. 新增测试全部通过(例如 `flutter test` 相关用例、`npm test` 相关用例)。 diff --git a/docs/plans/2026-04-07-09-54-UTC-backend-owned-chat-persistence-and-context.md b/docs/plans/2026-04-07-09-54-UTC-backend-owned-chat-persistence-and-context.md deleted file mode 100644 index 248d2d4a..00000000 --- a/docs/plans/2026-04-07-09-54-UTC-backend-owned-chat-persistence-and-context.md +++ /dev/null @@ -1,30 +0,0 @@ -# Background -用户对上一版修复提出进一步要求: -1) 发送后可关闭客户端,后续再次进入仍可看到完整回复; -2) 历史拼接应在后端,便于统一裁剪策略; -3) 存储应由后端完成,不应依赖客户端再次回传整段历史; -4) 评估 accept 与 chat 并行是否必要。 - -# Goals -1. 将单轮问答的落库与生成尽量收敛到后端一次编排中完成。 -2. 在后端按可控策略读取/裁剪历史上下文并用于模型调用。 -3. 前端发送路径改为调用后端编排接口,减少“先客户端算、再客户端回传存储”的依赖。 -4. 保持兼容现有接口,同时为后续精简 `accept` 铺路。 - -# Implementation Plan (phased) -## Phase 1 -- 后端新增 `POST /api/chat/respond`:接收任务信息 + 用户消息,完成:accept(幂等)-> 用户消息落库 -> 读取并裁剪会话历史 -> LLM 生成 -> assistant 落库。 -- 服务层新增按 session 拉取历史消息并裁剪的函数(条数/字符上限)。 - -## Phase 2 -- 前端新增 `ChatHistoryApiService.respond`。 -- 主发送流程 `_sendMessage` 改为调用 `respond` 获取最终文本并更新 UI;保留历史同步机制。 - -## Phase 3 -- 回归执行 Node type-check、Node tests、Flutter 相关测试。 - -# Acceptance Criteria -1. 用户发送消息后,只要请求已被后端接收,即使客户端关闭,后端仍可完成回复并持久化。 -2. 模型请求历史由后端从数据库读取并裁剪,不依赖前端拼接完整历史。 -3. assistant 最终内容由后端写库,刷新后可直接从 history/sync 读到。 -4. 现有测试通过,新增改动无编译错误。 diff --git a/docs/plans/2026-04-07-12-18-UTC-remove-chat-context-bar-impact-assessment.md b/docs/plans/2026-04-07-12-18-UTC-remove-chat-context-bar-impact-assessment.md deleted file mode 100644 index 815316c3..00000000 --- a/docs/plans/2026-04-07-12-18-UTC-remove-chat-context-bar-impact-assessment.md +++ /dev/null @@ -1,47 +0,0 @@ -# Background - -对话页顶部目前展示一排 `Chip`(Channel / Thread / Session / ThreadMode / Mode / Cursor / Seq)。这些信息主要用于调试会话作用域、同步状态和多代理仲裁状态,不属于核心聊天输入输出流程。目标是在真正删除前,先确认影响面与依赖逻辑,避免误伤同步与线程切换能力。 - -> **注意**:`Syncing` Chip 及其关联的内联线性进度条与"模拟断线恢复同步"按钮已在本 PR 中一并移除(同步指示器和调试触发入口均属于调试辅助 UI,不属于核心 context bar 展示信息)。当前剩余 Chip 如上所列。 - -相关实现位于 `apps/mobile_chat_app/lib/features/chat/chat_screen.dart` 的 `_buildContextBar()`,并在 `build()` 的 `body` 中固定插入。同步与游标数据由 `ChatHistoryApiService` 与消息流处理逻辑提供。 - -# Goals - -1. 明确顶部按钮(Chip)各自语义及关联状态字段。 -2. 区分“仅 UI 展示”与“业务逻辑实际依赖”。 -3. 给出删除该排按钮前后需要回归验证的场景。 -4. 识别潜在连带影响(调试可观测性、文案、测试)。 - -# Implementation Plan (phased) - -## Phase 1 - 结构梳理 - -- 定位 `_buildContextBar()` 的调用位置与渲染条件。 -- 追踪各 Chip 绑定字段的写入来源(channel 切换、thread 切换、历史加载、reconnect sync、task ack)。 -- 确认这些字段是否被其他逻辑分支读取,而不只是展示。 - -## Phase 2 - 影响面判定 - -- 将字段按“强依赖逻辑字段”与“纯展示字段”分组: - - 强依赖:`_activeChannelId`、`_activeSubSection`、`_threadModeEnabled`、`_sessionIdForScope`、`_lastSyncedSeq`、`_latestCheckpointCursor`。 - - 纯展示:`_buildContextBar()` 及其 `Chip` 文案本身。 -- 识别和删除动作相邻的 UI:`_syncingAfterReconnect` 线性进度条、`模拟断线恢复同步` 按钮。 -- 明确删除按钮不应影响的路径:发送消息、流式更新、线程切换、历史同步。 - -## Phase 3 - 删除前验证清单(建议) - -- 手动回归: - 1. 主区发送消息并接收流式回复。 - 2. 开启 Thread 模式并切换子区。 - 3. 切换频道后确认 session scope 仍正确重载。 - 4. 通过真实断网/恢复网络(或应用切后台后恢复连接)触发 reconnect sync,确认 `afterSeq` 增量同步仍生效,缺失消息会补齐且不会重复。 -- 自动化补强(当前仓库无直接覆盖): - - 为 `chat_screen` 添加 widget test,至少验证 context bar 可配置隐藏后不影响消息列表与 composer 渲染。 - -# Acceptance Criteria - -1. 影响面说明能逐项映射每个顶部 Chip 对应的状态与写入路径。 -2. 结论明确:删除顶部 Chip 属于 UI 层变更,不应删除其背后状态维护逻辑。 -3. 删除方案包含回归项,且覆盖 channel/thread/sync 三类高风险路径。 -4. 若后续实施删除,需确保页面仍保留必要调试入口(例如开发模式日志或可折叠诊断面板)。 diff --git a/docs/plans/2026-04-07-15-39-UTC-remove-simulated-reconnect-entry.md b/docs/plans/2026-04-07-15-39-UTC-remove-simulated-reconnect-entry.md deleted file mode 100644 index 85a59f9c..00000000 --- a/docs/plans/2026-04-07-15-39-UTC-remove-simulated-reconnect-entry.md +++ /dev/null @@ -1,29 +0,0 @@ -# Background - -聊天页底部存在“模拟断线恢复同步”按钮,用于手动调用增量同步流程。该入口是调试性质功能,不属于用户核心对话流程。 - -本任务根据评审反馈,要求删除该入口,且不保留相关测试代码。 - -# Goals - -1. 删除聊天页中的“模拟断线恢复同步”按钮。 -2. 清理仅为该按钮服务的页面状态与方法。 -3. 不新增、不保留与该模拟入口绑定的测试代码。 - -# Implementation Plan (phased) - -## Phase 1 - UI 与状态清理 - -- 从 `chat_screen.dart` 移除按钮渲染节点。 -- 移除 `_simulateReconnectSync()` 方法。 -- 移除 `_syncingAfterReconnect` 状态及其 UI 绑定(context bar 的 `Syncing…` chip、线性进度条)。 - -## Phase 2 - 编译层验证 - -- 运行静态检查,确认删除后无未使用引用或类型错误。 - -# Acceptance Criteria - -1. 聊天页不再显示“模拟断线恢复同步”按钮。 -2. 页面中不存在 `_simulateReconnectSync` 与 `_syncingAfterReconnect` 的残留引用。 -3. 本次提交不新增与该入口相关的测试代码。 diff --git a/docs/plans/2026-04-08-02-34-UTC-channel-subsections.md b/docs/plans/2026-04-08-02-34-UTC-channel-subsections.md deleted file mode 100644 index 9aa8d9c1..00000000 --- a/docs/plans/2026-04-08-02-34-UTC-channel-subsections.md +++ /dev/null @@ -1,73 +0,0 @@ -# Channel Sub-sections (replace Thread Mode) Plan - -## Background -- The chat entry flow routes authenticated users into `ChatScreen`, where conversation scope is currently encoded by `(channelId, threadId)` and persisted as `session::`. -- In current UI behavior, child conversation areas (子区) are guarded behind a runtime `Thread 模式` toggle (`_threadModeEnabled`). Without enabling it, users cannot create sub-sections. -- The backend and API already carry `threadId` fields, so scope-level message isolation exists at transport/persistence level; however, domain language and UI behavior still expose this as optional "thread mode" instead of always-available channel sub-sections. -- Product direction is to remove mode switching entirely and treat sub-sections as first-class, optional children of a channel (0..N children). - -## Goals -1. Remove all user-facing and stateful "Thread mode" behavior. -2. Keep channel-level chatting available when there are zero sub-sections. -3. Support creating and entering sub-sections, each with independent chat history. -4. Make parent-child relation explicit in client topology: channel knows children; sub-section knows parent. -5. In the chat top-right sub-section menu, render two functional groups: - - Management group (e.g., 回到主区, 新建子区) - - Sub-section list group (navigable) -6. Sort sub-section list by each sub-section’s latest message timestamp descending. - -## Implementation Plan (phased) - -### Phase 1 — Topology/domain model cleanup -- Replace toggle-driven state with explicit scope model: - - main scope is always present per channel; - - child scopes are optional and always creatable. -- Introduce/normalize data structures so: - - channel keeps `subSections` collection; - - sub-section includes `parentChannelId`. -- Keep persistence identity stable (`session::`) while renaming UI/domain terms from thread -> sub-section where possible. - -### Phase 2 — ChatScreen behavior migration -- Remove `_threadModeEnabled` and all toggle branches. -- Update active scope resolution to always use currently selected section (`main` or child). -- Allow creating sub-section regardless of mode. -- Ensure switching channel defaults to `main` unless previous active child remains valid for that channel. - -### Phase 3 — Top-right menu UX restructuring -- Keep existing menu entry point in app bar right action. -- Render menu in two groups: - 1) Management actions (回到主区, 新建子区) - 2) Sub-section navigable items. -- Sort child list by latest message timestamp desc. - - Maintain `lastMessageAt` index per `(channelId, subSectionId)` from in-memory messages and/or loaded history metadata. - - Unmessaged sub-sections fall to the bottom with deterministic tie-breaker (e.g., createdAt desc then id). - -### Phase 4 — Data loading/persistence alignment -- Ensure load/persist calls continue to use scope-derived `sessionId` so each sub-section history is isolated. -- Validate that creating/switching sub-sections clears current list then loads that scope’s history snapshot. -- Confirm backend payload remains compatible (`threadId` can remain wire field short-term, with UI terminology migrated). - -### Phase 5 — Test updates -- Update and/or add widget tests for: - - no-sub-section channel can still send/receive; - - create child sub-section and navigate; - - management group/actions visible; - - list sorted by latest message time desc. -- Update unit tests around topology/scope resolution accordingly. - -## Acceptance Criteria -1. **Zero-child baseline**: In any channel with no child sub-sections, user can directly chat in 主区 without enabling any mode or switch. -2. **Create + isolate history**: After creating a child sub-section and entering it, message history starts as a new isolated scope (does not show 主区 history); returning to 主区 restores 主区 history. -3. **Parent-child awareness**: Channel data can enumerate its child sub-sections; each child sub-section carries its parent channel identity. -4. **Menu grouping**: Top-right sub-section menu shows exactly two functional groups: - - Group A management actions: 回到主区、新建子区 - - Group B child sub-section navigation list -5. **Menu ordering**: Group B sub-section list is ordered by latest message time descending (most recently active first). -6. **No thread toggle**: UI does not expose "开启/关闭 Thread 模式" and logic has no mode-gated behavior for sub-section creation/navigation. -7. **Backward compatibility (short-term)**: Existing backend request contracts continue functioning while client product language reflects 子区 rather than thread mode. -8. **Regression safety**: Existing channel creation/switching and message send flow still work after migration. - -## Validation Commands -- `./tools/init_dev_env.sh` -- `cd apps/mobile_chat_app && flutter test` -- `cd apps/mobile_chat_app && flutter analyze` diff --git a/docs/plans/2026-04-08-02-40-UTC-code-map-foundation.md b/docs/plans/2026-04-08-02-40-UTC-code-map-foundation.md deleted file mode 100644 index 871b6494..00000000 --- a/docs/plans/2026-04-08-02-40-UTC-code-map-foundation.md +++ /dev/null @@ -1,41 +0,0 @@ -# Background - -As the repository grows, AI agents can miss related logic when modifying code, which can leave behind stale code or cause functional regressions. We need a "code map" as a unified entry point that helps human testers, AI testers, and AI engineers quickly locate feature paths and documentation indexes. - -# Goals - -1. Add a feature map YAML file that records the feature list and entry paths. -2. Add a logic map YAML file that records feature-to-code/document indexes and keyword mappings. -3. Add a reusable Codex Skill to guide future maintenance of the code maps. -4. Add a working rule to AGENTS memory requiring code maps to be updated after code changes. - -# Implementation Plan (phased) - -## Phase 1 - Create the code map files - -- Create `docs/code_maps/feature_map.yaml`. -- Create `docs/code_maps/logic_map.yaml`. -- Define a shared schema so both humans and AI can parse the files. - -## Phase 2 - Standardize the maintenance prompt - -- Create `.codex/skills/code-map-maintainer/SKILL.md`. -- Document the trigger conditions, maintenance steps, post-change checkpoints, and output format. - -## Phase 3 - Integrate with Agent memory - -- Update the root `AGENTS.md`: - - Register `code-map-maintainer` in the list of available skills. - - Add a standing rule that code maps must be checked and maintained after code changes. - -## Phase 4 - Basic validation - -- Run YAML parsing validation to ensure the new map files are correctly formatted. - -# Acceptance Criteria - -1. The current phase includes at least two code map YAML files: a feature map (`feature_map.yaml`) and a logic map (`logic_map.yaml`). Additional map files may be added in future phases. -2. The feature map exposes the entry path and validation entry for each feature. -3. The logic map exposes the corresponding code indexes, document indexes, and keywords for each feature, with `feature_id` values that are consistent with the feature map. -4. The repository contains a dedicated skill document for maintaining code maps, and it can be referenced by future tasks. -5. The AGENTS documentation includes an explicit memory rule requiring code map maintenance after code changes. diff --git a/docs/plans/2026-04-08-02-52-UTC-chat-history-load-window-and-auto-scroll.md b/docs/plans/2026-04-08-02-52-UTC-chat-history-load-window-and-auto-scroll.md deleted file mode 100644 index c0613993..00000000 --- a/docs/plans/2026-04-08-02-52-UTC-chat-history-load-window-and-auto-scroll.md +++ /dev/null @@ -1,28 +0,0 @@ -# Background -After page refresh, the chat view currently loads the full channel/session history and does not automatically scroll to the latest message. This causes heavy initial rendering for long sessions and makes users manually drag the scroll bar to reach the newest content. - -# Goals -- Limit initial history load to only the latest 100 messages for the active session/channel. -- Ensure the message list defaults to showing the newest message (bottom) after refresh/load. -- Keep behavior compatible with existing incremental sync and persistence paths. - -# Implementation Plan (phased) -## Phase 1: Backend and API client load window -1. Add optional `limit` support to `GET /api/chat/history/:sessionId`. -2. Add optional `limit` support to `syncMessages` service, applying it safely only for full-history requests (`afterSeq == 0`) by selecting the newest N messages then restoring chronological order. -3. Update Flutter `ChatHistoryApiService.load` to send `?limit=100` by default. - -## Phase 2: UI default-to-latest behavior -1. Convert message list widget to stateful with an internal `ScrollController`. -2. Auto-scroll to bottom after first frame and when message count changes. -3. Keep behavior smooth and safe when list is not yet attached. - -## Phase 3: Validation -1. Add/adjust unit test(s) to verify client history request includes the load window. -2. Run repository bootstrap and targeted tests. - -# Acceptance Criteria -- On refresh, history endpoint is requested with a limit of 100 messages. -- UI initially lands at the latest message without manual scrolling. -- Existing chat history API service tests pass after change. -- Validation commands are run and documented (`./tools/init_dev_env.sh`, `cd apps/mobile_chat_app && flutter test test/chat_history_api_service_test.dart`). diff --git a/docs/plans/2026-04-09-02-15-UTC-move-chat-debug-info-into-composer-menu.md b/docs/plans/2026-04-09-02-15-UTC-move-chat-debug-info-into-composer-menu.md deleted file mode 100644 index cb7e0446..00000000 --- a/docs/plans/2026-04-09-02-15-UTC-move-chat-debug-info-into-composer-menu.md +++ /dev/null @@ -1,35 +0,0 @@ -# Background - -用户希望移除聊天页顶部的调试 `Chip`(Channel/子区/Session/Mode/Cursor/Seq)行,避免占用对话主视图空间;同时希望在输入框左下角菜单中新增“信息”入口,点击后以模态窗口展示同一批调试信息。 - -# Goals - -1. 从聊天主界面移除顶部调试 context bar。 -2. 在 Composer 菜单增加“信息”动作项。 -3. 点击“信息”后弹出模态,展示当前 scope 与同步诊断信息。 -4. 不区分环境(开发/生产一致可见菜单入口)。 - -# Implementation Plan (phased) - -## Phase 1 - Composer 菜单扩展 - -- 在 `ComposerMenuAction` 中新增 `info` 枚举值。 -- 在菜单项中新增“信息”按钮。 -- 为 `ComposerBar` 增加 `onShowInfo` 回调并在点击后触发。 - -## Phase 2 - ChatScreen 调整 - -- 移除 `ChatScreen` 中顶部 `_buildContextBar()` 渲染入口。 -- 在 `ChatScreen` 增加 `_showDebugInfoDialog()`,将原 context bar 展示的关键字段以弹窗内容呈现。 -- 通过 `ComposerBar(onShowInfo: ...)` 连接入口与弹窗。 - -## Phase 3 - 校验 - -- 运行 Flutter 测试,确认编译与行为未回归。 - -# Acceptance Criteria - -1. 聊天页不再显示顶部 Channel/子区/Session/Mode/Cursor/Seq 标签行。 -2. 输入框左下角菜单出现“信息”项。 -3. 点击“信息”会弹出模态窗口,并展示当前 Channel、子区、Session、Mode、Cursor、Seq。 -4. 变更后项目测试可通过(至少执行移动端 Flutter tests)。 diff --git a/docs/plans/2026-04-09-03-47-UTC-chat-open-auto-scroll-latest.md b/docs/plans/2026-04-09-03-47-UTC-chat-open-auto-scroll-latest.md deleted file mode 100644 index f9bdd5bf..00000000 --- a/docs/plans/2026-04-09-03-47-UTC-chat-open-auto-scroll-latest.md +++ /dev/null @@ -1,21 +0,0 @@ -# Background -在聊天页面已有历史消息时,用户打开对话后仍需要手动滑动才能看到最新消息,影响阅读与继续对话体验。 - -# Goals -- 进入已有历史的对话后自动定位到底部(最新一条消息)。 -- 当消息列表内容替换但条数不变时,也能触发自动滚动到底部。 -- 保持现有发送与增量更新流程不受影响。 - -# Implementation Plan (phased) -## Phase 1: MessageList 自动滚动触发条件增强 -1. 在 `MessageList` 中新增“最后一条消息签名”对比逻辑,不仅比较 `length`,还比较尾消息内容/时间等关键字段。 -2. 当签名变化时触发 `_scrollToBottom()`,覆盖“长度不变但历史内容已切换”的场景。 - -## Phase 2: 回归验证 -1. 新增/更新 widget 测试,验证列表初次渲染时会定位到最新消息。 -2. 运行 Flutter 相关测试确保改动稳定。 - -# Acceptance Criteria -- 打开有历史消息的对话后,无需手动拖动即可看到最新消息。 -- 在消息总数不变但内容切换后,仍会自动滚到末尾。 -- 相关测试通过(`./tools/init_dev_env.sh`、`cd apps/mobile_chat_app && flutter test test/message_list_test.dart`)。 diff --git a/docs/plans/2026-04-09-04-02-UTC-sidebar-channel-click-white-screen.md b/docs/plans/2026-04-09-04-02-UTC-sidebar-channel-click-white-screen.md deleted file mode 100644 index 70ee5392..00000000 --- a/docs/plans/2026-04-09-04-02-UTC-sidebar-channel-click-white-screen.md +++ /dev/null @@ -1,32 +0,0 @@ -# Sidebar channel click causes white screen investigation and fix - -## Background -点击侧边栏任意频道后页面出现白屏,导航栏、输入框、侧边栏全部消失,说明前端可能触发了未捕获运行时异常,导致根组件渲染中断。 - -## Goals -- 复现“点击侧边栏频道后白屏”的问题。 -- 定位触发白屏的具体代码路径和根因。 -- 提供最小且可靠的修复,并验证关键交互恢复正常。 -- 检查并按需更新代码地图文件。 - -## Implementation Plan (phased) -1. **复现与日志采集** - - 启动应用并稳定复现问题。 - - 采集浏览器控制台/测试输出中的异常堆栈,确定报错文件与组件。 -2. **根因定位** - - 从频道点击事件、路由切换、状态同步链路入手,逐步缩小范围。 - - 审查相关状态字段是否存在空值/类型不一致/越界访问。 -3. **修复实现** - - 在最靠近根因处修复,避免仅在外围做“吞错误”处理。 - - 保证当前频道与其他频道点击路径都可安全渲染。 -4. **验证与回归检查** - - 运行针对性测试/静态检查。 - - 手动验证侧边栏频道切换、页面布局与输入区域可见性。 -5. **文档与代码地图** - - 如涉及功能入口或逻辑索引变化,更新 `docs/code_maps/feature_map.yaml` 与 `docs/code_maps/logic_map.yaml`。 - -## Acceptance Criteria -- 点击侧边栏当前频道与其他频道时,页面不再白屏且主界面元素持续可见。 -- 无新增未捕获运行时异常。 -- 相关测试或检查命令通过。 -- 若变更影响代码地图索引,代码地图已同步更新;若未更新,在总结中说明理由。 diff --git a/docs/plans/2026-04-09-05-48-UTC-chat-interaction-simplify.md b/docs/plans/2026-04-09-05-48-UTC-chat-interaction-simplify.md deleted file mode 100644 index 5124dcd2..00000000 --- a/docs/plans/2026-04-09-05-48-UTC-chat-interaction-simplify.md +++ /dev/null @@ -1,32 +0,0 @@ -# Background -- 当前聊天信息流默认在初始化、切换会话和消息更新时倾向滚动到底部。 -- 该行为会导致发送后用户消息不稳定,以及 AI 流式回复期间视图被持续拉动。 -- 需求要求统一为“以用户消息作为定位锚点”,并对长文本气泡提供 3 行折叠/展开交互。 - -# Goals -- 发送消息后将最新用户消息定位为可见区域第一条,AI 回复显示在其下方。 -- 接收 AI 流式回复时不再把列表拖到底部。 -- 刚打开页面或切换会话时采用与发送一致的定位策略。 -- 消息气泡默认最多展示 3 行,超出显示省略并提供右上角展开/收起按钮;不超出不显示按钮。 - -# Implementation Plan (phased) -1. 调整 `MessageList` 自动滚动策略: - - 定位目标改为“最后一条 user 消息”,若不存在则回退到最后一条消息。 - - 列表变更后先确保尾部元素布局,再将目标消息 `ensureVisible(alignment: 0)` 到顶部。 - - 对同一条流式消息的文本增量更新增加判定,避免重复触发自动定位。 -2. 新增长文本折叠组件: - - 以 3 行为默认上限并开启 `TextOverflow.ellipsis`。 - - 用 `TextPainter` 判断是否溢出,溢出时显示气泡右上角展开/收起按钮。 -3. 更新测试: - - 将自动滚动断言调整为“聚焦用户消息而非到底部”。 - - 新增长文本展开按钮展示测试。 -4. 同步代码地图: - - 更新 `docs/code_maps/feature_map.yaml` 与 `docs/code_maps/logic_map.yaml` 中 chat 会话滚动行为描述与风险提示。 - -# Acceptance Criteria -- 发送用户消息后,消息列表顶部可见项为该用户消息,AI 回复位于其后。 -- AI 流式回复过程中列表不会因增量文本持续自动跳动。 -- 打开已有会话或切换频道/子会话时定位策略与发送后保持一致。 -- 长文本消息默认最多显示 3 行且省略;点击右上角按钮可展开/收起。 -- 短文本消息不显示展开/收起按钮。 -- `cd apps/mobile_chat_app && flutter test test/message_list_test.dart` 通过。 diff --git a/docs/plans/2026-04-09-06-02-UTC-ai-bubble-full-width.md b/docs/plans/2026-04-09-06-02-UTC-ai-bubble-full-width.md deleted file mode 100644 index 1a078efe..00000000 --- a/docs/plans/2026-04-09-06-02-UTC-ai-bubble-full-width.md +++ /dev/null @@ -1,22 +0,0 @@ -# Background -- 现有聊天消息气泡对 user 与 assistant 使用统一最大宽度(约 75%)。 -- 新需求要求保持 user 气泡宽度策略不变,同时将 AI 回复气泡扩展为全屏宽度,以提高阅读效率。 - -# Goals -- user 消息气泡继续沿用当前宽度规则(不变)。 -- assistant 消息气泡在消息列表可用宽度内占满整行。 -- 保持既有折叠/展开与自动定位行为不受影响。 - -# Implementation Plan (phased) -1. 在 `MessageList` 中拆分 user/assistant 气泡宽度约束: - - user: 维持 `maxWidth = screen * 0.75`。 - - assistant: 使用整行宽度(依赖列表 padding 留白)。 -2. 增加 widget 测试验证宽度差异: - - 同屏渲染一条 user 与一条 assistant 消息。 - - 断言 assistant 气泡宽度显著大于 user,且接近列表可用宽度。 -3. 运行目标测试确保回归通过。 - -# Acceptance Criteria -- user 消息气泡视觉宽度与改动前一致。 -- assistant 消息气泡占满消息列表内容区域宽度。 -- `cd apps/mobile_chat_app && flutter test test/message_list_test.dart` 通过。 diff --git a/docs/plans/2026-04-09-10-48-UTC-channel-persistence-hydration.md b/docs/plans/2026-04-09-10-48-UTC-channel-persistence-hydration.md deleted file mode 100644 index 68196607..00000000 --- a/docs/plans/2026-04-09-10-48-UTC-channel-persistence-hydration.md +++ /dev/null @@ -1,28 +0,0 @@ -# Channel persistence hydration plan - -## Background -Users can create multiple channels in ChatScreen and send messages, but after a page refresh only the default channel is visible in the sidebar. Message history is already persisted server-side by session scope, yet channel/sub-section navigation state is not rehydrated from persisted backend data. - -## Goals -- Ensure channels and sub-sections with persisted chat data are reconstructed on app load. -- Keep default channel behavior intact while merging persisted scopes. -- Minimize schema risk by reusing existing chat task/message tables. - -## Implementation Plan (phased) -1. Backend scope discovery API - - Add a service query that lists distinct `(channel_id, thread_id, session_id)` scopes for the authenticated user ordered by most recent activity. - - Expose a new authenticated route `GET /api/chat/scopes`. -2. Mobile API client support - - Extend `ChatHistoryApiService` with typed scope DTOs and `loadScopes`. -3. ChatScreen hydration - - On startup, fetch scopes, reconstruct channels/sub-sections, and preserve a valid active scope. - - Generate stable fallback display names from channel/sub IDs when explicit names are unavailable. -4. Validation - - Add backend route/service tests and mobile API service tests. - - Run repository bootstrap and targeted test suites. - -## Acceptance Criteria -- After creating channels, sending messages, and refreshing the page, sidebar still shows previously used channels. -- Switching restored channels loads their corresponding session message history. -- `GET /api/chat/scopes` returns authenticated user scopes only, sorted by recent activity. -- Validation commands succeed: `./tools/init_dev_env.sh`, backend tests, and mobile chat API tests. diff --git a/docs/plans/2026-04-10-01-10-UTC-menu-animation-speedup.md b/docs/plans/2026-04-10-01-10-UTC-menu-animation-speedup.md deleted file mode 100644 index d7136926..00000000 --- a/docs/plans/2026-04-10-01-10-UTC-menu-animation-speedup.md +++ /dev/null @@ -1,20 +0,0 @@ -# Background -The chat UI currently uses default Flutter popup menu transition settings, which feel slow and start from a full collapsed translation impression. The requested UX is a globally faster menu animation with an iOS-like feel: quick reveal from approximately 80% scale with low starting opacity. - -# Goals -- Speed up popup menu open/close animations in the chat screen and composer bar. -- Use a scale+fade transition profile that starts near 80% size and low opacity, avoiding a "from zero" feel. -- Keep the animation token centralized in the design system so it can be reused across any `PopupMenuButton`. - -# Implementation Plan (phased) -1. Locate global app theme construction and popup menu usage points. -2. Add a shared `AnimationStyle` token (`menuPopupAnimationStyle`) in `BricksTheme` to reduce duration by ~50% and adjust curves for snappy response. -3. Apply `BricksTheme.menuPopupAnimationStyle` directly on each `PopupMenuButton` via its `popUpAnimationStyle` parameter. Note: `PopupMenuThemeData` does not expose a `popUpAnimationStyle` field in the Flutter version used by this project, so the per-button approach is the correct mechanism. -4. Run environment bootstrap and targeted Flutter tests/analyze for the touched package. -5. Review code map files and update only if indexing/entry-path metadata needs to reflect this behavior change. - -# Acceptance Criteria -- Popup menus in the app use a faster transition than before (approximately half duration). -- Popup menus animate with scale+fade (starting around 0.8 scale and low opacity) rather than appearing to start from 0. -- Existing composer popup menu widget tests continue to pass. -- Validation commands are documented and reproducible (`./tools/init_dev_env.sh`, `cd apps/mobile_chat_app && flutter test test/composer_bar_test.dart`). diff --git a/docs/plans/2026-04-10-04-39-UTC-channel-management.md b/docs/plans/2026-04-10-04-39-UTC-channel-management.md deleted file mode 100644 index 8f3b1b43..00000000 --- a/docs/plans/2026-04-10-04-39-UTC-channel-management.md +++ /dev/null @@ -1,25 +0,0 @@ -# Background -The chat sidebar currently creates channels with auto-generated names, does not support channel rename/archive actions, and the chat app bar title is hardcoded as "Bricks". - -# Goals -- Require user-provided channel names during creation. -- Ensure channel names are unique. -- Add long-press channel actions in the sidebar for rename and archive. -- Hide archived channels from the sidebar by removing them from the active channel list. -- Show the active channel name in the top app bar title, left-aligned. - -# Implementation Plan (phased) -1. Extend `ChatNavigationPage` to support long-press channel context actions (rename/archive) via callbacks and a popup menu. -2. In `ChatScreen`, replace auto-generated channel creation with a name input dialog and duplicate-name validation. -3. Implement channel rename and archive handlers in `ChatScreen`, including active-channel fallback behavior when archiving. -4. Update app bar title rendering to use the active channel name. -5. Add/adjust widget tests for navigation menu behavior and command dispatch. -6. Run Flutter checks/tests and update code maps if feature/logic entry points changed. - -# Acceptance Criteria -- Long-pressing a channel in the drawer shows actions for rename and archive. -- Renaming a channel updates its displayed name and does not allow duplicates. -- Archiving a channel removes it from the visible channel list. -- Creating a channel requires entering a name and cannot create with duplicate names. -- The app bar title displays the active channel name (left aligned). -- Relevant widget tests pass. diff --git a/docs/plans/2026-04-14-01-30-UTC-openclaw-plugin-platform-api-and-settings-copy.md b/docs/plans/2026-04-14-01-30-UTC-openclaw-plugin-platform-api-and-settings-copy.md deleted file mode 100644 index 064590d2..00000000 --- a/docs/plans/2026-04-14-01-30-UTC-openclaw-plugin-platform-api-and-settings-copy.md +++ /dev/null @@ -1,34 +0,0 @@ -# Background -Bricks needs a production-oriented backend interface contract to support OpenClaw plugin development under a pull-only topology. The current design draft captures architectural intent, but it should be hardened into an implementation-ready development document with explicit API contracts, security boundaries, idempotency semantics, and phased delivery guidance. In parallel, the Settings experience in the mobile app should support one-click copy actions for API URL and API key values to reduce operator friction during plugin setup and debugging. - -# Goals -1. Produce an improved, implementation-ready Bricks × OpenClaw pull-only integration development document. -2. Define clear backend API boundaries (MVP-required vs optional), auth model, delivery semantics, and operational constraints. -3. Add copy actions in settings for API URL and API key. -4. Add/adjust automated tests for the new settings copy actions. -5. Update code maps if feature/doc index or behavior coverage changes. - -# Implementation Plan (phased) -## Phase 1: Documentation hardening -- Create a dedicated integration doc under `docs/` for Bricks platform API + OpenClaw plugin pull-only integration. -- Keep the architecture boundary explicit: Bricks platform vs plugin adapter vs OpenClaw core. -- Add a normative API contract section with request/response shape, error model, idempotency rules, and status code expectations. -- Add implementation sequence, rollout strategy, observability, and security checklist. - -## Phase 2: Settings copy UX -- Update `ModelSettingsScreen` to support copying Base URL (API URL) and API key to clipboard. -- Provide user feedback with snackbars for success/empty value cases. -- Preserve existing API key visibility toggle behavior. - -## Phase 3: Validation and code-map sync -- Run repository bootstrap command before Flutter checks: `./tools/init_dev_env.sh`. -- Run focused widget tests for settings behavior. -- Update `docs/code_maps/feature_map.yaml` and `docs/code_maps/logic_map.yaml` to reflect the new settings behavior and new integration document indexes. - -# Acceptance Criteria -1. A new OpenClaw pull-only integration development document exists under `docs/` and is implementation-ready for backend/API work. -2. Model settings UI exposes a copy action for API URL and a copy action for API key. -3. Tapping copy actions writes expected clipboard values when present and displays user feedback. -4. Existing settings interactions (save/delete/toggle visibility) remain functional. -5. Relevant tests pass (at minimum: `flutter test test/model_settings_screen_test.dart` from `apps/mobile_chat_app`). -6. Code maps are updated to include behavior/doc-index changes introduced by this task. diff --git a/docs/plans/2026-04-14-03-31-UTC-address-pr-review-comments.md b/docs/plans/2026-04-14-03-31-UTC-address-pr-review-comments.md deleted file mode 100644 index 134af00f..00000000 --- a/docs/plans/2026-04-14-03-31-UTC-address-pr-review-comments.md +++ /dev/null @@ -1,5 +0,0 @@ -- [x] Review PR feedback threads and current implementation details in docs/UI tests -- [x] Update OpenClaw integration doc to clarify PATCH semantics, ACK semantics, canonical plugin identity, and error schema/status matrices -- [x] Add widget test for successful non-empty API key copy flow -- [x] Run bootstrap and targeted Flutter tests for modified areas -- [ ] Run final validation (including parallel validation), capture screenshot, and reply to the new PR comment diff --git a/docs/plans/2026-04-15-07-21-UTC-comment-followup-unified-messages-events.md b/docs/plans/2026-04-15-07-21-UTC-comment-followup-unified-messages-events.md deleted file mode 100644 index 16765c20..00000000 --- a/docs/plans/2026-04-15-07-21-UTC-comment-followup-unified-messages-events.md +++ /dev/null @@ -1,5 +0,0 @@ -- [x] Review new PR comments and decide which need action/reply -- [x] Confirm current CI workflow status for this PR branch -- [x] Update integration doc to explicitly state unified messages storage + separate events/outbox -- [x] Validate documentation-only change and run final review validation -- [ ] Reply to actionable PR comment with commit hash and screenshot link diff --git a/docs/plans/2026-04-15-16-06-UTC-implementation-doc-alignment-check.md b/docs/plans/2026-04-15-16-06-UTC-implementation-doc-alignment-check.md deleted file mode 100644 index a86942af..00000000 --- a/docs/plans/2026-04-15-16-06-UTC-implementation-doc-alignment-check.md +++ /dev/null @@ -1,18 +0,0 @@ -## Background -Reviewer feedback requested checking current code implementation and ensuring the OpenClaw pull-only integration document stays aligned with actual backend behavior and boundaries. - -## Goals -1. Verify whether current backend storage patterns conflict with the integration document. -2. Update the document where needed so it clearly distinguishes current implementation status vs target OpenClaw contract. -3. Keep changes minimal and scoped to the feedback. - -## Implementation Plan (phased) -- [x] Inspect current backend chat storage schema and transport services. -- [x] Compare current implementation with the OpenClaw integration document data-model guidance. -- [x] Update `docs/openclaw_pull_only_integration_dev_doc.md` with explicit implementation-alignment notes. -- [ ] Validate changed docs and reply on PR thread with commit reference. - -## Acceptance Criteria -- The document explicitly states that message storage remains unified (not split by message source). -- The document explicitly states that event/outbox persistence is an independent concern and currently a target integration contract. -- PR thread receives a concise follow-up with the commit hash that contains the alignment update. diff --git a/docs/plans/2026-04-16-05-00-UTC-openclaw-gap-audit-and-token-chain.md b/docs/plans/2026-04-16-05-00-UTC-openclaw-gap-audit-and-token-chain.md deleted file mode 100644 index c1549745..00000000 --- a/docs/plans/2026-04-16-05-00-UTC-openclaw-gap-audit-and-token-chain.md +++ /dev/null @@ -1,29 +0,0 @@ -# OpenClaw Gap Audit and Token Chain Validation (Consolidated) - -## Background -The previous iteration produced multiple short plan files and partial implementation updates. This consolidated document merges the current review context and implementation goals into a single artifact. - -## Goals -1. Compare target requirements from post-main docs with current implementation status. -2. Close critical integration gaps for remote OpenClaw/小龙虾 token flow. -3. Ensure the user journey is end-to-end: generate token in Settings -> configure external server -> external server connects back and sends messages. - -## Implementation Plan (phased) -### Phase 1: Gap audit -- Verify target vs implementation for pull-only APIs and auth model. -- Confirm whether Settings currently exposes OpenClaw token issuance. - -### Phase 2: Gap closure -- Add backend token-issuance endpoint for authenticated users. -- Support JWT platform tokens in platform auth middleware. -- Wire Settings UI to request, show, and copy the OpenClaw token bundle. - -### Phase 3: Validation -- Add/extend automated tests for token retrieval and copy behavior. -- Verify backend type-check and platform route tests. - -## Acceptance Criteria -- Settings page can fetch and copy a dedicated OpenClaw token. -- External service can use that token with `X-Bricks-Plugin-Id` against `/api/v1/platform/*`. -- Platform middleware accepts scoped JWT tokens and enforces plugin identity constraints. -- Consolidated planning is captured in one markdown file for this iteration. diff --git a/docs/plans/2026-04-16-08-47-UTC-move-openclaw-token-to-settings-item.md b/docs/plans/2026-04-16-08-47-UTC-move-openclaw-token-to-settings-item.md deleted file mode 100644 index b1809ec7..00000000 --- a/docs/plans/2026-04-16-08-47-UTC-move-openclaw-token-to-settings-item.md +++ /dev/null @@ -1,20 +0,0 @@ -## Background -The PR feedback requests moving the token-generation action out of Model Settings into the top-level Settings page as a dedicated settings item, and renaming the button text to "Openclaw Token". - -## Goals -1. Remove token-generation UI from Model Settings. -2. Add a dedicated "Openclaw Token" settings entry in Settings. -3. Keep token generation/copy behavior working with updated naming. - -## Implementation Plan (phased) -- [x] Locate existing token generation implementation and related widget tests. -- [x] Move token UI/logic from `ModelSettingsScreen` into a dedicated settings flow. -- [x] Add a new settings list item in `SettingsScreen` that navigates to Openclaw token UI. -- [x] Update and add targeted widget tests for the new location and label. -- [x] Run targeted Flutter tests and finalize. - -## Acceptance Criteria -- "Get Xiaolongxia Token" no longer appears in Model Settings. -- Settings page contains a new "Openclaw Token" item. -- Entering that item allows generating a token, viewing token metadata, and copying the token. -- Widget tests cover the new settings entry and token generation/copy flow. diff --git a/docs/plans/2026-04-16-10-51-UTC-node-openclaw-plugin-implementation.md b/docs/plans/2026-04-16-10-51-UTC-node-openclaw-plugin-implementation.md deleted file mode 100644 index c93500d3..00000000 --- a/docs/plans/2026-04-16-10-51-UTC-node-openclaw-plugin-implementation.md +++ /dev/null @@ -1,33 +0,0 @@ -# Background -用户希望“根据文档实现 node_openclaw_plugin”。仓库当前已具备 Bricks 平台侧 pull-only API(`/api/v1/platform/*`)与 token 签发能力,但尚无独立的 Node.js 插件示例/运行时来执行文档中的事件拉取、ACK、消息写回闭环。 - -# Goals -- 新增 `apps/node_openclaw_plugin`,提供最小可运行的 Node.js/TypeScript 插件实现。 -- 按文档实现 pull-only 主循环:拉取事件、去重处理、ACK、消息回写。 -- 提供环境变量与运行说明,便于本地联调。 -- 增加基础单测,覆盖关键状态持久化与处理逻辑。 -- 同步更新代码地图索引,确保新功能可被测试与 AI 检索。 - -# Implementation Plan (phased) -## Phase 1: 项目骨架与客户端 -1. 创建 `apps/node_openclaw_plugin` 包(`package.json`、`tsconfig.json`、`src/`)。 -2. 实现平台 API 客户端:`events` 拉取、`events/ack`、`messages` 创建/更新、`conversations/resolve`。 -3. 定义插件内部类型与错误处理(标准化 HTTP 异常)。 - -## Phase 2: 事件处理与状态持久化 -1. 实现文件状态仓库(`cursor`、`processedEventIds`、`clientToken->messageId` 映射)。 -2. 实现插件运行器:轮询、按 `eventId` 去重、按批 ACK、失败重试边界。 -3. 为 `message.created` 事件实现示例回写逻辑(create + patch)。 -4. 为 `conversation.binding_changed` 增加日志处理分支(MVP noop)。 - -## Phase 3: 文档、测试与代码地图 -1. 添加插件目录 README(环境变量、运行命令、行为说明)。 -2. 编写并运行单测(状态仓库/处理流程)。 -3. 更新 `docs/code_maps/feature_map.yaml` 与 `docs/code_maps/logic_map.yaml`,纳入新插件功能索引。 - -# Acceptance Criteria -- 在 `apps/node_openclaw_plugin` 下可执行 `npm run build`、`npm run test` 并通过。 -- 插件主循环可根据环境变量连接 Bricks API,执行“pull -> process -> ack -> writeback”流程。 -- ACK 请求体不包含 `pluginId`,并使用请求头 `X-Bricks-Plugin-Id`。 -- 本地状态可持久化并在重启后继续使用 cursor 与去重集合。 -- 代码地图已更新,包含 `node_openclaw_plugin` 的入口、代码索引与回归风险。 diff --git a/docs/plans/2026-04-16-12-00-UTC-branch-db-diff-analysis.md b/docs/plans/2026-04-16-12-00-UTC-branch-db-diff-analysis.md deleted file mode 100644 index 7c5b4a95..00000000 --- a/docs/plans/2026-04-16-12-00-UTC-branch-db-diff-analysis.md +++ /dev/null @@ -1,21 +0,0 @@ -# Branch vs main database delta analysis plan - -## Background -A request was made to analyze what database-related changes this working branch introduced relative to `main`. -In this container snapshot, only the `work` local branch exists, so analysis relies on commit inspection and diffs against nearby merge-base candidates. - -## Goals -- Identify whether schema/migration files changed on this branch. -- Identify whether query behavior changed without schema changes. -- Produce a concise, evidence-based summary. - -## Implementation Plan (phased) -1. Inspect available local branches and commit graph to determine compare strategy. -2. Check for changed files under database-related paths (`apps/node_backend/src/db/**`) in branch commits. -3. Review backend service diffs for SQL/query-level behavioral changes. -4. Summarize findings and explicitly call out constraints (missing local `main` ref). - -## Acceptance Criteria -- The response clearly states whether any new migration/schema changes exist. -- If no schema changes exist, query-level data-access changes (if any) are listed. -- The response includes exact commands used and file citations. diff --git a/docs/plans/2026-04-16-12-20-UTC-api-feature-db-impact-recheck.md b/docs/plans/2026-04-16-12-20-UTC-api-feature-db-impact-recheck.md deleted file mode 100644 index 94aa7cf0..00000000 --- a/docs/plans/2026-04-16-12-20-UTC-api-feature-db-impact-recheck.md +++ /dev/null @@ -1,20 +0,0 @@ -# API feature database impact re-check plan - -## Background -The branch introduced platform API capabilities. We need to verify whether the earlier claim of "no database changes" is fully accurate. - -## Goals -- Distinguish schema/migration changes from data-access/query behavior changes. -- Verify changed backend files since the presumed base commit used in prior analysis. -- Produce a correction-ready summary. - -## Implementation Plan (phased) -1. Diff backend files between `5e7d966` and `HEAD` to isolate API-feature changes. -2. Diff database migration path (`apps/node_backend/src/db/**`) over the same range. -3. Inspect changed service code for SQL reads/writes against existing tables. -4. Summarize as: schema impact vs behavior impact. - -## Acceptance Criteria -- Explicitly states whether new migrations/schema files were added. -- Explicitly states whether API changes read/write database tables. -- Includes concrete command evidence and file citations. diff --git a/docs/plans/2026-04-16-12-40-UTC-main-worktree-feature-logic-review.md b/docs/plans/2026-04-16-12-40-UTC-main-worktree-feature-logic-review.md deleted file mode 100644 index 5c7b2a26..00000000 --- a/docs/plans/2026-04-16-12-40-UTC-main-worktree-feature-logic-review.md +++ /dev/null @@ -1,21 +0,0 @@ -# Main worktree comparison review plan (feature-logic oriented) - -## Background -The request is to perform code review against the "main worktree" and summarize logic per changed feature. -In this container snapshot, no separate `main` worktree exists, so we compare against commit `5e7d966` as the closest available main-line baseline. - -## Goals -- Group code changes by feature capability rather than file list only. -- Explain endpoint and UI behavior flows with security and data-access implications. -- Provide a review-ready summary with evidence references. - -## Implementation Plan (phased) -1. Enumerate changed files in `5e7d966..HEAD` and cluster by backend/mobile/test features. -2. Review platform API auth, routing, and service logic to map request → validation → DB access → response. -3. Review mobile settings flow for token generation/display/copy interactions. -4. Cross-check tests that validate new logic and identify untested risk edges. - -## Acceptance Criteria -- Each changed feature has a concise logic description. -- Security boundary (auth + scopes + userId scoping) is explicitly described. -- Database impact is explicitly separated into schema vs query/write behavior. diff --git a/docs/plans/2026-04-16-13-00-UTC-plugin-development-architecture-doc.md b/docs/plans/2026-04-16-13-00-UTC-plugin-development-architecture-doc.md deleted file mode 100644 index d120b5ef..00000000 --- a/docs/plans/2026-04-16-13-00-UTC-plugin-development-architecture-doc.md +++ /dev/null @@ -1,20 +0,0 @@ -# Plugin development architecture document plan - -## Background -A plugin architecture document is requested, and it must clearly show both repository code structure and practical usage. - -## Goals -- Provide a readable architecture document for plugin developers. -- Explain where backend auth/routes/services and mobile token UX live in code. -- Include setup, auth, endpoint usage, and extension guidance. - -## Implementation Plan (phased) -1. Extract plugin-related modules and flows from current backend/mobile code. -2. Write an architecture document with code tree, request flow, and security boundaries. -3. Add step-by-step usage examples for token issuance and platform API calls. -4. Add extension checklist and troubleshooting notes for implementation teams. - -## Acceptance Criteria -- Document includes explicit code structure mapping (path-level). -- Document includes end-to-end usage steps with example requests. -- Document is actionable for new plugin developers without reading all source code first. diff --git a/docs/plans/2026-04-16-16-00-UTC-node-openclaw-plugin-jwt-hardening.md b/docs/plans/2026-04-16-16-00-UTC-node-openclaw-plugin-jwt-hardening.md deleted file mode 100644 index 4dbca7a4..00000000 --- a/docs/plans/2026-04-16-16-00-UTC-node-openclaw-plugin-jwt-hardening.md +++ /dev/null @@ -1,30 +0,0 @@ -# Background -用户反馈上一版 `node_openclaw_plugin` 与目标不一致:需要明确该实现是用于 OpenClaw 对接的插件运行时(而非文档描述中的宽泛 adapter 表述),并且需要在代码层面真正收敛到 JWT token 路径,避免“文档改了但逻辑仍保留静态 key 兼容”的遗留错误资产。 - -# Goals -- 将 `node_openclaw_plugin` 的运行前校验与调用语义收敛为 JWT-only。 -- 修复当前插件与后端平台接口字段不一致导致的消息 patch 失效问题。 -- 修复 ACK 与本地状态持久化顺序问题,避免 ACK 成功但本地状态未落盘。 -- 同步更新 README 与架构文档,消除“adapter/bridge”歧义与静态 key 残留描述。 - -# Implementation Plan (phased) -## Phase 1: JWT-only 运行时约束 -1. 新增 JWT claims 解析与基础校验逻辑(typ/pluginId/userId/exp)。 -2. 在启动配置加载阶段强制校验 token,失败直接退出。 -3. 在运行时使用 token userId 执行消息过滤(避免自触发回环)。 - -## Phase 2: 协议对齐与可靠性修复 -1. 将消息 patch 字段调整为后端当前可识别的 `text`/`metadata`。 -2. 重构 ACK 流程:先持久化 pending ACK,再发送 ACK,成功后提交 cursor。 -3. 为 pending ACK 增加重试入口,确保崩溃恢复后可继续推进 cursor。 - -## Phase 3: 文档与测试收口 -1. 更新 `apps/node_openclaw_plugin/README.md`:明确 OpenClaw 插件运行时 + JWT-only。 -2. 更新 `docs/plugin_development_architecture.md` 代码结构,纳入新插件目录。 -3. 增加测试覆盖 JWT claims 校验与状态仓库新字段兼容。 - -# Acceptance Criteria -- 插件启动时若 token 非合法 JWT claims(缺 typ/pluginId/userId 或过期)会直接失败。 -- 插件向 `PATCH /api/v1/platform/messages/:id` 发送后端可识别字段并能更新文本。 -- ACK 流程具备 pending 持久化,崩溃恢复后可继续 ACK 并推进 cursor。 -- README 不再声明静态 key 支持,且语义明确为 OpenClaw 插件运行时。 diff --git a/docs/plans/2026-04-16-22-15-UTC-openclaw-onboarding-enable.md b/docs/plans/2026-04-16-22-15-UTC-openclaw-onboarding-enable.md deleted file mode 100644 index deaa4742..00000000 --- a/docs/plans/2026-04-16-22-15-UTC-openclaw-onboarding-enable.md +++ /dev/null @@ -1,29 +0,0 @@ -# Background -`apps/node_openclaw_plugin` 目前提供的是 pull-only 运行时示例,但尚未具备 OpenClaw 插件“安装后可在 onboarding/configure 向导中交互写入 channel 配置”的能力。用户希望 channel id 使用 `dev-askman-bricks`,并在配置过程中提示输入 `BRICKS_PLATFORM_TOKEN` 等必填参数,自动写入 `channels.dev-askman-bricks`。 - -# Goals -- 为 `apps/node_openclaw_plugin` 增加可被 OpenClaw 识别的插件元数据(manifest + package metadata)。 -- 增加 channel onboarding hook(`configureInteractive`)以提示用户输入并返回配置。 -- 在 README 中补充“子目录安装 + onboarding 配置”步骤,便于用户把说明交给 AI 自动执行。 -- 同步代码地图索引,反映新增 onboarding 能力与风险点。 - -# Implementation Plan (phased) -## Phase 1: 插件清单与安装发现元数据 -1. 新增 `openclaw.plugin.json`,声明 `channelConfigs.dev-askman-bricks` schema 与 `uiHints`。 -2. 在 `package.json` 中补充 `openclaw.extensions/openclaw.channel/openclaw.install` 元数据,支持子目录与 npm 安装引导。 - -## Phase 2: onboarding 交互配置能力 -1. 新增 OpenClaw 插件入口文件,导出带 `onboarding.configureInteractive(ctx)` 的插件对象。 -2. 在 hook 中通过 `ctx.prompter.input` 询问 `BRICKS_BASE_URL`、`BRICKS_PLUGIN_ID`、`BRICKS_PLATFORM_TOKEN`。 -3. 返回 `{ cfg, accountId }` 以让 OpenClaw 自动写入 `channels.dev-askman-bricks`。 - -## Phase 3: 文档、测试与代码地图 -1. 更新 `apps/node_openclaw_plugin/README.md`,写清子目录安装、onboard/configure 流程、非 install lifecycle 限制。 -2. 运行 TypeScript 构建与测试检查。 -3. 更新 `docs/code_maps/feature_map.yaml` 与 `docs/code_maps/logic_map.yaml`。 - -# Acceptance Criteria -- `apps/node_openclaw_plugin` 包含可通过 OpenClaw 发现的 `openclaw.plugin.json` 与 `openclaw.extensions` 元数据。 -- 安装插件后执行 onboarding/configure 时,向导可提示用户输入 token/base URL/plugin id,并将配置写入 `channels.dev-askman-bricks`。 -- README 明确说明子目录安装步骤与配置命令。 -- 代码地图已覆盖 onboarding 入口与回归风险。 diff --git a/docs/plans/2026-04-17-02-22-UTC-address-pr-review-followups.md b/docs/plans/2026-04-17-02-22-UTC-address-pr-review-followups.md deleted file mode 100644 index 9a698cdf..00000000 --- a/docs/plans/2026-04-17-02-22-UTC-address-pr-review-followups.md +++ /dev/null @@ -1,22 +0,0 @@ -## Background -Recent PR review comments identified contract drift between the OpenClaw integration document and current backend implementation, plus two code issues: insufficient JWT plugin isolation in platform auth middleware and plugin-side event filtering that drops valid user messages. - -## Goals -1. Align integration documentation to the currently implemented API/auth behavior. -2. Enforce strict plugin isolation for JWT platform tokens. -3. Ensure plugin event filtering does not skip valid user-origin events. -4. Keep related tests and code maps synchronized. - -## Implementation Plan (phased) -- [x] Inspect review comments, current backend/plugin implementation, and impacted docs/tests. -- [x] Update integration doc sections for auth model, PATCH contract shape, rawId examples, and current implementation alignment. -- [x] Tighten platform JWT validation to require pluginId claim and header equality. -- [x] Adjust OpenClaw plugin event filter to avoid dropping same-user message.created events. -- [x] Update/add targeted backend and plugin tests for new auth/filtering behavior. -- [x] Run targeted tests and finalize. - -## Acceptance Criteria -- Integration doc reflects current supported auth modes (JWT primary + optional static key fallback) and current MVP message patch contract. -- Middleware rejects platform JWT tokens missing pluginId claim, and still rejects pluginId mismatches. -- Plugin processes message.created events from user senders even when sender.userId equals token userId, while still ignoring assistant/system events. -- Targeted tests pass in `apps/node_backend` and `apps/node_openclaw_plugin`. diff --git a/docs/plans/2026-04-17-05-52-UTC-openclaw-channel-plugin-conversion.md b/docs/plans/2026-04-17-05-52-UTC-openclaw-channel-plugin-conversion.md deleted file mode 100644 index 0795d0a7..00000000 --- a/docs/plans/2026-04-17-05-52-UTC-openclaw-channel-plugin-conversion.md +++ /dev/null @@ -1,29 +0,0 @@ -# Background -`apps/node_openclaw_plugin` currently exposes OpenClaw package metadata and onboarding prompts, but OpenClaw still treats it like a generic plugin instead of a true channel plugin. That leaves `channels.dev-askman-bricks.*` invalid in `openclaw config set`, which blocks narrow channel-only configuration and makes the metadata/runtime contract inconsistent. - -# Goals -- Make `dev-askman-bricks` a real OpenClaw channel from manifest, package metadata, and runtime registration perspectives. -- Keep the existing Bricks pull-only standalone runner intact. -- Restore the intended config surface so `channels.dev-askman-bricks.*` is valid. -- Update targeted docs/tests so the new channel contract is explicit and regression-resistant. - -# Implementation Plan (phased) -## Phase 1: Channel contract alignment -1. Update `apps/node_openclaw_plugin/openclaw.plugin.json` to declare channel ownership (`kind`, `channels`) while preserving `channelConfigs`. -2. Add/adjust package metadata (`openclaw.setupEntry` if needed) so setup/configure surfaces can load the plugin as a channel. - -## Phase 2: Minimal native channel runtime -1. Replace the current onboarding-only extension entry with a real channel entry that calls `api.registerChannel({ plugin })`. -2. Define the smallest viable channel plugin object: metadata, config schema, and setup/config wiring for `channels.dev-askman-bricks`. -3. Add a lightweight setup-only entry for setup/configure flows. - -## Phase 3: Verification and docs -1. Update `apps/node_openclaw_plugin/README.md` to document the channel config path and command flow. -2. Update tests to cover channel registration/config wiring instead of the old onboarding-only object shape. -3. Run package build/tests and local OpenClaw verification, including `openclaw config set channels.dev-askman-bricks.*`. - -# Acceptance Criteria -- OpenClaw accepts `channels.dev-askman-bricks.*` writes for the Bricks plugin. -- `openclaw plugins inspect dev-askman-bricks` shows the plugin as a channel-capability plugin rather than a generic non-capability plugin. -- `apps/node_openclaw_plugin` build/test/type-check continue to pass. -- The README explains how to configure Bricks via the channel config path. diff --git a/docs/plans/2026-04-17-07-29-UTC-openclaw-router-default-plan.md b/docs/plans/2026-04-17-07-29-UTC-openclaw-router-default-plan.md deleted file mode 100644 index 55917bb4..00000000 --- a/docs/plans/2026-04-17-07-29-UTC-openclaw-router-default-plan.md +++ /dev/null @@ -1,65 +0,0 @@ -# Problem -Add a per-scope message router for Bricks chat so each channel and thread can choose between: -- `default`: messages are handled inside Bricks by `/api/chat/respond` and `generateWithUserConfig(...)` -- `openclaw`: user messages are handed off asynchronously through the Bricks platform/OpenClaw plugin path, and plugin-side updates flow back into Bricks UI - -The current codebase does not persist channel/thread settings yet. Chat scopes are inferred from existing traffic, and the mobile UI still assumes `/api/chat/respond` returns assistant text immediately. - -# Proposed approach -## 1. Persist scope router settings -- Add a backend persistence model for scope settings, recommended as a new `chat_scope_settings` table keyed by `(user_id, channel_id, thread_id nullable)`. -- Store a `router` enum/string with allowed values `default` and `openclaw`. -- Resolve the effective router as: - - thread-level override - - else channel-level setting - - else implicit `default` -- Extend chat scope APIs so the mobile app can fetch and update both channel-level and thread-level router settings. - -## 2. Branch backend dispatch by effective router -- Keep `/api/chat/respond` as the main authenticated entrypoint for app-originated sends. -- For `default`, preserve the existing synchronous flow: - - accept task - - persist the user message - - call `generateWithUserConfig(...)` - - persist the assistant message - - return assistant text immediately -- For `openclaw`, change the flow to async handoff: - - accept task - - persist the user message - - skip internal LLM generation - - return an accepted/pending response shape that the UI can track - - let the OpenClaw plugin pull the new user message from the platform events API and write back assistant updates through `/api/v1/platform/messages` - - if the user's plugin/token is not currently active, keep the message pending instead of falling back to `default` - -## 3. Tighten the platform event bridge -- Filter platform events so only eligible user-originated messages from `openclaw`-routed scopes are emitted to plugins. -- Prevent replay/echo loops for plugin-authored assistant messages and platform patch updates. -- Preserve conversation resolution through the existing `(channelId, threadId, sessionId)` mapping. - -## 4. Update the mobile chat UX -- Add router controls for channels and threads in the chat UI/state managed from `chat_screen.dart`. -- Hydrate scope settings from backend instead of treating channels/threads as traffic-derived only. -- Keep current behavior for `default`. -- For `openclaw`, stop assuming an immediate assistant reply: - - show dispatched/pending task state - - rely on existing chat sync/history updates to render plugin-created assistant messages when they arrive - - keep messages pending while OpenClaw is unavailable rather than silently rerouting them - -## 5. Validate end to end -- Backend tests for router resolution, new scope settings APIs, `/api/chat/respond` branching, and platform event filtering. -- Flutter tests for effective-router resolution and async/pending message UX. -- Plugin tests for event handling assumptions if server-side filtering changes. -- Run the existing package build/test commands for touched apps. - -# Todos -1. Add router persistence and scope settings APIs in the backend. -2. Branch `/api/chat/respond` into synchronous `default` handling and asynchronous `openclaw` handoff. -3. Filter platform events and writeback behavior so only OpenClaw-routed user messages are exported and plugin updates do not loop. -4. Add channel/thread router controls and async OpenClaw task handling in the Flutter chat UI. -5. Add/update tests and run repository checks for the touched packages. - -# Notes -- Current scope discovery is traffic-derived via `/api/chat/scopes`; there is no persisted channel/thread settings model yet. -- `ChatHistoryApiService.respond()` and `chat_screen.dart` currently expect immediate assistant text, so OpenClaw requires a contract/UI change rather than a pure config switch. -- Recommended precedence: thread router overrides channel router. -- Confirmed behavior: if a scope is set to `openclaw` and the local plugin/token is not currently available, Bricks should still accept the message and leave it pending until OpenClaw becomes available. There should be no silent fallback to `default`. diff --git a/docs/plans/2026-04-17-10-29-UTC-request-performance-improvements.md b/docs/plans/2026-04-17-10-29-UTC-request-performance-improvements.md deleted file mode 100644 index 0d34acf7..00000000 --- a/docs/plans/2026-04-17-10-29-UTC-request-performance-improvements.md +++ /dev/null @@ -1,244 +0,0 @@ -# Request performance improvements - -## Problem - -The current Bricks request model favors simple polling over request efficiency. -That works for correctness, but it can create too many empty requests and raise -the chance of `429` responses when either: - -- the web client repeatedly polls `/api/chat/sync/:sessionId` -- the OpenClaw plugin repeatedly polls `/api/v1/platform/events` - -We do **not** want to implement the full fix in this task. This document is a -future-work guide for improving request efficiency safely. - -## Current state - -### Web chat sync - -- `apps/mobile_chat_app/lib/features/chat/chat_screen.dart` -- The chat UI polls `/api/chat/sync/:sessionId` every 2 seconds when the active - scope is routed to OpenClaw or when there are pending assistant tasks. -- The current loop is timer-driven, not completion-driven. -- Recent work already reduced one source of 429s by moving `/api/chat/sync/*` - off the coarse global `/api/*` limiter. - -### OpenClaw plugin polling - -- `apps/node_openclaw_plugin/src/pluginRunner.ts` -- The plugin loop is already **end-to-start**: - 1. run a full tick - 2. pull events - 3. ack them - 4. write message updates - 5. then sleep -- Default poll interval is 2000 ms. - -### Backend rate limiting - -- `apps/node_backend/src/app.ts` -- A coarse limiter still applies to most `/api/*` traffic: - - 100 requests / 15 minutes / IP -- That is too small for any route that intentionally polls. -- `/api/chat/sync/*` has already been carved out, but `/api/v1/platform/*` - still deserves its own dedicated policy. - -### Platform routes - -- `apps/node_backend/src/routes/platform.ts` -- Platform routes already have authentication and scope checks, but they do not - yet have a platform-specific limiter or a long-poll contract. - -## Goals - -1. Reduce empty requests. -2. Reduce `429` responses during normal chat/plugin operation. -3. Keep perceived latency low when new messages arrive. -4. Stay compatible with Vercel/serverless constraints. -5. Avoid broad protocol changes until the smaller fixes are validated. - -## Recommended staged plan - -### Stage 1: Improve polling behavior without changing the HTTP contract - -#### Web `/api/chat/sync` - -Move the web client to **completion-based polling**: - -- schedule the next sync **after** the previous request finishes -- do not use fixed periodic ticks as the source of truth - -Add client backoff on failed syncs: - -- success: `2s` -- first failure: `4s` -- second failure: `8s` -- later failures: cap at `10s` - -Reset the delay back to `2s` after any successful response. - -Also: - -- honor `Retry-After` if the backend sends it -- add a small jitter window (for example ±10%) so many clients do not poll in - lockstep -- make sure only one sync loop is active per visible scope/tab - -#### OpenClaw plugin `/api/v1/platform/events` - -The plugin already polls end-to-start, so it is in a better shape than the web -client. The next improvement should be **retry behavior**, not loop shape: - -- treat `429` and retryable `5xx` responses as backoff signals -- use the same capped retry ladder: - - `2s` -> `4s` -> `8s` -> `10s` -- prefer backend-provided `Retry-After` when present - -### Stage 2: Add route-specific backend rate limiting - -#### `/api/chat/sync/*` - -Keep the current approach: - -- do **not** let this route share the generic `/api/*` IP limiter -- keep a route-specific budget keyed by authenticated chat context - -#### `/api/v1/platform/*` - -Add a dedicated limiter for platform traffic instead of sharing the generic IP -bucket. - -Recommended keying: - -- JWT platform token mode: `pluginId:userId` -- static platform key mode: `pluginId:remoteAddress` (or another stable plugin - identity if one is introduced later) - -Recommended behavior: - -- separate read-heavy polling routes from write routes if needed -- return structured `429` errors with retry hints -- avoid adding many new environment variables unless real operations require it - -### Stage 3: Reduce empty responses with long polling - -If Stage 1 and Stage 2 are still not enough, add **short long-poll** behavior. - -#### `/api/chat/sync/:sessionId` - -Allow the server to hold an empty request for up to **10 seconds** before -returning a no-change response. - -Behavior: - -- if new messages already exist, return immediately -- if no new messages exist, wait up to 10 seconds -- return early as soon as new data becomes available -- return an empty/no-change payload only when the wait window expires - -#### `/api/v1/platform/events` - -Apply the same idea to platform event pulls: - -- return immediately when unread events exist -- otherwise hold the request briefly instead of replying empty right away - -Important note: - -- this is a bigger change than client backoff -- validate carefully on Vercel/serverless before rolling it out broadly - -### Stage 4: Consider event-driven transport only if needed - -If polling and long-polling are still not sufficient, then evaluate: - -- Server-Sent Events for chat updates -- Server-Sent Events for platform events -- WebSockets only if truly bidirectional low-latency behavior is needed - -This should come later because it increases operational and protocol -complexity. - -## Detailed implementation notes - -### Web sync scheduler - -Recommended behavior for the next implementation: - -1. start one sync request -2. wait for completion -3. decide the next delay from the outcome -4. schedule exactly one future sync - -Outcome table: - -| Outcome | Next delay | -| --- | --- | -| Success | 2s | -| Retryable failure / 429 | 4s, then 8s, then 10s max | -| `Retry-After` present | use `Retry-After`, capped at a sane upper bound if desired | - -Additional safeguards: - -- ignore stale responses when the user changed channel/thread while the request - was in flight -- cancel timers immediately on scope switch -- consider pausing or widening intervals when the tab is hidden - -### Platform polling - -The plugin loop should remain: - -```text -tick -> sleep -``` - -That is already the right shape. - -Future work should focus on: - -- backend rate-limit symmetry -- retry/backoff -- optional long-poll for empty event pulls - -## Validation plan - -### Functional tests - -- web sync scheduler never runs more than one request at a time -- failure backoff caps at `10s` -- success resets delay to `2s` -- stale scope changes do not reschedule old loops -- platform limiter does not block normal polling traffic - -### Load / soak checks - -- one OpenClaw-routed web tab open for 15+ minutes -- multiple tabs for the same user -- multiple plugin runners against the same backend -- empty queue behavior vs active queue behavior - -### Metrics to watch - -- requests/minute per active session -- requests/minute per active plugin -- `429` count by route -- empty response ratio -- median and p95 latency -- Vercel execution time and timeout behavior for long-poll candidates - -## Recommended implementation order - -1. Web completion-based polling with `2s -> 4s -> 8s -> 10s` capped backoff -2. Platform route-specific limiter and plugin retry/backoff -3. Short long-poll prototype for `/api/chat/sync` -4. Long-poll evaluation for `/api/v1/platform/events` -5. Event-driven transport only if polling remains insufficient - -## Decision log - -- This document intentionally records future work only. -- We are **not** implementing the long-poll / advanced performance changes in - this task. -- Prefer fixed code-level defaults first; only introduce more env config if - production operations actually need it. diff --git a/docs/plans/2026-04-18-00-20-UTC-openclaw-message-routing-current-state.md b/docs/plans/2026-04-18-00-20-UTC-openclaw-message-routing-current-state.md deleted file mode 100644 index 8412b461..00000000 --- a/docs/plans/2026-04-18-00-20-UTC-openclaw-message-routing-current-state.md +++ /dev/null @@ -1,55 +0,0 @@ -# OpenClaw 消息路由与读状态(当前有效基线) - -## Background - -为避免历史方案并存造成误解,本文件统一承接 2026-04-17 这一组 OpenClaw 消息路由调整的阶段性结论。 -此前对应的阶段性计划文档已从 `docs/plans/` 中移除,因此此处不再保留旧文件名列表,避免形成失效引用。 -本文件即为该主题当前唯一保留且持续维护的有效基线。 - -## Goals - -1. 定义 OpenClaw 路由下“已发送未读 / 已读并回复”的**当前**语义与数据形态。 -2. 明确平台事件过滤条件与空事件排查入口。 -3. 给出当前测试覆盖边界与后续补强重点。 - -## Implementation Plan (phased) - -### Phase 1 — 当前行为基线(已生效) -- OpenClaw 异步发送不再持久化 assistant 站位消息行。 -- `respond` 在 OpenClaw 路由下返回 `mode=async`, `state=accepted`, `text=''`。 -- 用户消息通过属性表达状态演进: - - `accepted/dispatched`:已发送待插件消费。 - - 插件 ACK 后更新为 `completed`,并记录 `metadata.pluginReadBy.`。 - -### Phase 2 — 平台事件供给条件(已生效) -`/api/v1/platform/events` 返回事件时,需满足: -- `write_seq > cursor` -- `role = 'user'` -- `task_state IN ('accepted','dispatched')` -- `metadata` 含 `pendingAssistantMessageId` -- 若 JWT 带 `userId`,还需匹配 `msg.user_id = token.userId` - -### Phase 3 — 空事件排查(当前推荐顺序) -1. 核对 cursor 是否已追平。 -2. 核对 JWT `userId` 与消息归属用户是否一致。 -3. 核对消息是否满足 role / task_state / metadata 过滤条件。 -4. 核对插件请求头 `X-Bricks-Plugin-Id` 与 token claim/plugin scope 是否一致。 - -### Phase 4 — 测试覆盖现状与补强方向 -- 当前覆盖是“分段覆盖”: - - 前端 `respond` 状态映射 - - 后端 OpenClaw `respond` 行为 - - 平台鉴权与 userId 透传 - - 平台 events/ack 服务逻辑 -- 尚缺“单用例闭环集成测试”串联: - - `respond -> events -> messages(create/patch) -> ack -> sync` - -## Acceptance Criteria - -1. 仓库仅保留本文件作为 2026-04-17 这一组 OpenClaw 消息路由调整的有效基线。 -2. 旧阶段性文档删除并由本文件统一承接。 -3. 代码地图索引仅指向本文件,不再指向旧文档。 - -### Validation commands - -- `git diff -- docs/plans docs/code_maps/logic_map.yaml` diff --git a/docs/plans/2026-04-18-03-19-UTC-ai-message-no-bubble.md b/docs/plans/2026-04-18-03-19-UTC-ai-message-no-bubble.md deleted file mode 100644 index d69d7260..00000000 --- a/docs/plans/2026-04-18-03-19-UTC-ai-message-no-bubble.md +++ /dev/null @@ -1,18 +0,0 @@ -# Background -The mobile chat message list currently renders both user and assistant messages inside rounded bubble containers. For assistant replies, this consumes horizontal space due to bubble padding and creates a denser layout than desired. - -# Goals -- Remove the bubble visual treatment for assistant messages in the conversation list. -- Reduce horizontal spacing for assistant output so more text fits per line. -- Keep user message bubble style and behavior unchanged. - -# Implementation Plan (phased) -1. Update `MessageList` assistant row rendering to use plain text content without background bubble decoration. -2. Keep user branch on the existing bubble styling path so outgoing message visuals are unchanged. -3. Adjust/add widget tests to verify assistant layout width increases while user bubble constraints remain. - -# Acceptance Criteria -- Assistant messages no longer show a rounded/background bubble container in the message list. -- Assistant messages have less horizontal inset than before and display a wider content area. -- User messages still render with existing bubble style and compact width constraints. -- Validation command passes: `cd apps/mobile_chat_app && flutter test test/message_list_test.dart`. diff --git a/docs/plans/2026-04-18-08-10-UTC-channel-rename-persistence.md b/docs/plans/2026-04-18-08-10-UTC-channel-rename-persistence.md deleted file mode 100644 index 5642d1e6..00000000 --- a/docs/plans/2026-04-18-08-10-UTC-channel-rename-persistence.md +++ /dev/null @@ -1,32 +0,0 @@ -# Channel rename persistence plan - -## Background -用户反馈:在频道列表中将频道改名后,刷新页面会恢复为旧名字。当前前端仅在内存中更新频道名,未将改名结果持久化到后端数据库,因此重新加载后无法恢复用户自定义名称。 - -## Goals -- 让频道改名结果在刷新后保持不变。 -- 将频道自定义名称写入数据库并可按用户读取。 -- 保持现有频道路由与消息存储行为不变,避免影响聊天主链路。 - -## Implementation Plan (phased) -1. **Backend schema & service** - - 新增数据库迁移,创建 `chat_channel_names` 表(按 `user_id + channel_id` 唯一)。 - - 新增后端服务用于查询、写入、删除频道名映射。 -2. **Backend API** - - 在 `/api/chat` 下新增读取/写入频道名接口(`GET /channel-names`、`PUT /channel-names`)。 - - 写入接口支持覆盖更新与删除(传空名或 null 删除)。 -3. **Flutter client integration** - - 在 `ChatHistoryApiService` 增加加载与保存频道名 API 调用。 - - 启动加载时将数据库中的频道名覆盖到已恢复的频道列表。 - - 频道改名时调用保存接口;归档频道时删除该频道名映射。 -4. **Tests** - - 增加/更新 Node backend route tests(新接口行为)。 - - 增加/更新 Flutter service tests(新接口解析与请求体)。 -5. **Code map sync** - - 根据本次行为变更同步更新 `docs/code_maps/feature_map.yaml` 和 `docs/code_maps/logic_map.yaml`。 - -## Acceptance Criteria -- 用户将非默认频道改名后,刷新页面仍显示新名字。 -- 后端数据库存在该用户的频道名映射记录,且重复改名会更新同一条记录。 -- 删除/归档频道时可清理对应频道名映射,不影响其他频道。 -- `apps/node_backend/src/routes/chat.test.ts` 与 `apps/mobile_chat_app/test/chat_history_api_service_test.dart` 相关测试通过。 diff --git a/docs/plans/2026-04-18-09-29-UTC-ai-markdown-rendering.md b/docs/plans/2026-04-18-09-29-UTC-ai-markdown-rendering.md deleted file mode 100644 index 09f4037b..00000000 --- a/docs/plans/2026-04-18-09-29-UTC-ai-markdown-rendering.md +++ /dev/null @@ -1,38 +0,0 @@ -# Background -Users want AI output messages to support markdown-style formatting, with two strict presentation constraints: heading markers (`#`, `##`, etc.) should not increase font size, and heading blocks should not introduce extra top/bottom spacing compared with normal text. The chat message list currently renders assistant messages as plain `Text`, so markdown semantics are not visualized. - -# Goals -1. Render assistant message content with lightweight markdown formatting for headings, inline emphasis, and list indentation. -2. Keep heading typography aligned to body text size while using bold weight only. -3. Avoid extra block margin/padding for heading lines so visual rhythm matches plain paragraphs. -4. Preserve existing user-message rendering and behavior. -5. Add tests covering heading style normalization and list indentation output. - -# Implementation Plan (phased) -## Phase 1: Introduce a lightweight markdown renderer for assistant messages -- Add a `_AssistantMarkdownText` widget in `message_list.dart`. -- Parse assistant text line-by-line into basic block structures: - - heading-like lines beginning with `#`..`######` - - unordered/ordered list lines - - plain paragraph lines -- Render all block text at body font size. - -## Phase 2: Apply style rules requested by product behavior -- Render heading lines with bold font weight only (no larger font size). -- Keep heading vertical spacing equal to normal paragraph spacing (no extra top/bottom margins). -- Render lists with explicit left indentation and optional list markers. -- Add inline emphasis parsing for `**bold**`, `__bold__`, `*italic*`, and `_italic_` without introducing heading-size changes. - -## Phase 3: Validation and regression safety -- Add widget tests in `message_list_test.dart` to confirm: - - heading-like text is rendered with body font size. - - heading-like text has bold weight. - - list items are indented relative to non-list text. -- Run targeted Flutter tests from the package directory. - -# Acceptance Criteria -1. Assistant message containing `# Title` displays as bold text with the same font size as adjacent body text. -2. Heading-like lines do not have extra margins/padding compared to regular lines. -3. Markdown list items render with visible indentation. -4. Existing `MessageList` tests continue to pass. -5. New markdown rendering tests pass under `flutter test` in `apps/mobile_chat_app`. diff --git a/docs/plans/2026-04-18-14-59-UTC-chat-stream-order-and-completeness-fix.md b/docs/plans/2026-04-18-14-59-UTC-chat-stream-order-and-completeness-fix.md deleted file mode 100644 index 7bc1f5ea..00000000 --- a/docs/plans/2026-04-18-14-59-UTC-chat-stream-order-and-completeness-fix.md +++ /dev/null @@ -1,28 +0,0 @@ -# Background -- 用户反馈发送消息后会话流顺序异常,截图显示用户与 AI 消息在时间线上错位。 -- 截图中还出现 AI 内容被渲染成用户气泡的迹象,且怀疑存在输出不完整问题。 -- 该问题很可能与异步回包时按列表索引更新消息有关:一旦期间发生同步刷新与重排,旧索引会指向错误消息。 - -# Goals -- 修复会话消息在异步返回与同步刷新并发时的渲染顺序/归属错乱。 -- 修复(或确认并防止)因错位更新导致的 AI 输出不完整或被写入错误消息。 -- 保持现有协议字段和 UI 展示行为不变,仅修复更新定位逻辑。 - -# Implementation Plan (phased) -1. 代码定位与根因确认 - - 检查 `ChatScreen` 发送与回包流程,重点关注 message append、sync merge、respond 回调。 - - 核对是否存在“先存 index,后因 sort/sync 变动导致 index 失效”的路径。 -2. 代码修复 - - 将回包写入逻辑从“按 index 更新”改为“按 messageId 更新(实时查找索引)”。 - - 在成功、失败、缺 token、取消等路径统一使用稳定标识定位消息。 - - 对找不到目标消息的边界情况做安全退出,避免错误覆盖其他消息。 -3. 验证与回归 - - 运行仓库要求的环境初始化脚本。 - - 运行至少覆盖聊天模块的测试命令。 - - 手工检查代码地图是否需要更新;若不需要,在最终说明中给出理由。 - -# Acceptance Criteria -- 当用户连续快速发送消息且期间发生 sync 合并时,AI 回包不会写入错误消息气泡。 -- 不再出现 AI 文本显示在用户气泡中的情况(由错误索引写入导致)。 -- 同一任务的最终文本完整写入对应 assistant 消息,不会因索引漂移而覆盖或截断其他消息。 -- 验证命令可执行并通过(例如:`./tools/init_dev_env.sh`、`cd apps/mobile_chat_app && flutter test`)。 diff --git a/docs/plans/2026-04-18-15-06-UTC-chat-router-button-relocation.md b/docs/plans/2026-04-18-15-06-UTC-chat-router-button-relocation.md deleted file mode 100644 index 2c239226..00000000 --- a/docs/plans/2026-04-18-15-06-UTC-chat-router-button-relocation.md +++ /dev/null @@ -1,24 +0,0 @@ -# Background -The chat page currently shows a router switch popup button in the top-right AppBar area. The requested UX is to place the router switch control near the composer controls at the lower-left side, immediately to the left of the existing tune/config button, and to show route-specific icons (including a lobster icon for openclaw). - -# Goals -- Move the router switch button from AppBar actions to the composer action row. -- Place the router button to the left of the existing config/tune button. -- Make the router button icon reflect the currently effective router: - - default route: suitable default routing icon - - openclaw route: lobster icon (🦞) -- Keep button sizing/layout stable regardless of icon type. - -# Implementation Plan (phased) -1. Extend `ComposerBar` API to accept an optional router menu button widget that can be rendered before the config button. -2. In `ChatScreen`, remove the existing AppBar router popup button. -3. In `ChatScreen`, build the router popup button and pass it into `ComposerBar` through the new slot. -4. Implement dynamic icon rendering for router button based on `_effectiveRouterForScope()`, using a fixed-size container so emoji/icon changes do not alter layout. -5. Run formatting and targeted Flutter tests. - -# Acceptance Criteria -- Router popup button no longer appears in top-right AppBar. -- Router popup button appears in composer controls, immediately left of the config/tune button. -- Effective router icon changes with route state (default uses routing icon, openclaw shows 🦞). -- Switching icon does not change button size or row layout. -- Validation commands pass (at minimum: `dart format` on modified files and relevant Flutter test command). diff --git a/docs/plans/2026-04-18-15-09-UTC-chat-send-flow-simplification.md b/docs/plans/2026-04-18-15-09-UTC-chat-send-flow-simplification.md deleted file mode 100644 index 71fdbec1..00000000 --- a/docs/plans/2026-04-18-15-09-UTC-chat-send-flow-simplification.md +++ /dev/null @@ -1,24 +0,0 @@ -# Background -- 上一版修复通过 messageId 解决了异步回包写错行的问题,但 `_sendMessage` 内重复定位/更新代码较多,可读性一般。 -- 需求强调“更优雅、可减少代码量”,希望在保证正确性的前提下进一步简化逻辑。 - -# Goals -- 保持“按 messageId 定位更新”这一正确性保障不变。 -- 精简 `_sendMessage` 的分支重复逻辑,减少样板代码。 -- 让 user/assistant messageId 来源更直接,避免通过“append 后再取 index”读取。 - -# Implementation Plan (phased) -1. 抽象公共更新逻辑 - - 增加按 messageId 更新消息的统一 helper,封装 index 查找与 setState 写回。 -2. 简化发送路径 - - 在 append 之前直接生成 user/assistant messageId 并写入消息对象。 - - `respond`、缺 token、失败路径统一调用 helper,减少重复代码。 -3. 验证 - - 运行 `./tools/init_dev_env.sh`。 - - 运行 `cd apps/mobile_chat_app && flutter test`。 - - 检查代码地图是否需更新;若无需更新,在最终说明给出理由。 - -# Acceptance Criteria -- 与上一版一致:不会因 sync/sort 导致 AI 回包写入错误消息。 -- `_sendMessage` 中消息更新逻辑比上一版更集中、更少重复。 -- 测试可通过,且无新的回归行为。 diff --git a/docs/plans/2026-04-19-11-46-UTC-openclaw-history-ordering-fix.md b/docs/plans/2026-04-19-11-46-UTC-openclaw-history-ordering-fix.md deleted file mode 100644 index e7f2d0a3..00000000 --- a/docs/plans/2026-04-19-11-46-UTC-openclaw-history-ordering-fix.md +++ /dev/null @@ -1,19 +0,0 @@ -# Background -Users reported incorrect chat history rendering when switching router to OpenClaw for one turn (with no OpenClaw server response yet), then switching back to the default router and sending another message. The delayed or pending OpenClaw assistant message can appear out of expected order in the message list. - -# Goals -- Ensure chat history rendering is consistently ordered by message creation timestamp across mixed router usage. -- Prevent stale/pending OpenClaw task messages from disrupting visual chronology after switching routers. -- Add regression tests that cover unsorted backend payloads and verify stable chronological rendering expectations. - -# Implementation Plan (phased) -1. Inspect chat history hydration and sync merge paths in `chat_screen.dart` and `chat_history_api_service.dart`. -2. Introduce deterministic chronological sorting for history snapshots (especially initial load) using createdAt/timestamp with stable tie-breakers. -3. Add/extend unit tests in mobile chat app history service tests to validate sorting behavior for unsorted message payloads. -4. Run repository bootstrap and targeted Flutter tests for the modified package. -5. Check code maps and update if behavior/indexing expectations changed. - -# Acceptance Criteria -- On history load, messages are ordered by creation time (oldest to newest), with deterministic tie-breakers. -- Mixed default/OpenClaw scenarios with pending and later-delivered messages no longer render in incorrect order. -- `flutter test` for `apps/mobile_chat_app` passes for affected test files. diff --git a/docs/plans/2026-04-19-13-08-UTC-openclaw-install-instructions-redo.md b/docs/plans/2026-04-19-13-08-UTC-openclaw-install-instructions-redo.md deleted file mode 100644 index a18e770a..00000000 --- a/docs/plans/2026-04-19-13-08-UTC-openclaw-install-instructions-redo.md +++ /dev/null @@ -1,37 +0,0 @@ -## Background - -The Openclaw Token settings page currently supports generating and copying only the token value. -For OpenClaw plugin onboarding, users also need a complete instruction payload that includes plugin parameters and an `openclaw.json` channel snippet. - -## Goals - -1. Show install instructions right after token generation. -2. Include `pluginId`, `url`, `scopes`, and `token` in a copyable text payload. -3. Add a one-click copy button for the full install instructions. -4. Keep the existing token copy flow unchanged. - -## Implementation Plan (phased) - -### Phase 1: UI content - -- Add a formatter method that builds a full install instruction string from `PlatformTokenBundle`. -- Render an "Install Instructions" section with: - - concise guidance text, - - a JSON snippet for `channels` in `openclaw.json`, - - parameter lines for `pluginId/url/scopes/token`. - -### Phase 2: Copy action - -- Add `Copy Install Instructions` button. -- Reuse existing clipboard/snackbar helper for consistency. - -### Phase 3: Validation and docs index sync - -- Update widget tests to verify instruction rendering and copy behavior. -- Update code maps for `openclaw_token_settings` smoke checks and risk notes. - -## Acceptance Criteria - -1. After generating token info, install instructions are visible on the page. -2. `Copy Install Instructions` copies the full instruction text and shows success feedback. -3. `Copy Openclaw Token` still copies only the token and continues to pass tests. diff --git a/docs/plans/2026-04-20-04-36-UTC-openclaw-install-instructions-branch-redo.md b/docs/plans/2026-04-20-04-36-UTC-openclaw-install-instructions-branch-redo.md deleted file mode 100644 index 80291158..00000000 --- a/docs/plans/2026-04-20-04-36-UTC-openclaw-install-instructions-branch-redo.md +++ /dev/null @@ -1,32 +0,0 @@ -## Background - -The previous implementation already added OpenClaw install instructions and copy support, but follow-up iteration is required on a new branch and with cleaner test interaction stability. - -## Goals - -1. Continue implementation work on a new branch name. -2. Keep the install-instructions UX behavior unchanged. -3. Improve widget-test reliability by avoiding long fixed-time waits. - -## Implementation Plan (phased) - -### Phase 1: Branch and test refinement - -- Create and use a new working branch for this follow-up. -- Update `openclaw_token_settings_screen_test.dart` to dismiss snackbars deterministically before the second copy action. - -### Phase 2: Validation - -- Run repository bootstrap script. -- Run focused Flutter widget tests for Openclaw token settings. - -### Phase 3: Documentation sync - -- Add this plan artifact for the task record. -- Keep code-map files unchanged if behavior/entry points do not change. - -## Acceptance Criteria - -1. Work is delivered from a non-previous branch. -2. Install-instructions copy behavior and token copy behavior both pass tests. -3. No user-visible behavior regression on Openclaw token settings page. diff --git a/docs/plans/2026-04-20-05-19-UTC-message-order-bug-analysis.md b/docs/plans/2026-04-20-05-19-UTC-message-order-bug-analysis.md deleted file mode 100644 index fd3f4e65..00000000 --- a/docs/plans/2026-04-20-05-19-UTC-message-order-bug-analysis.md +++ /dev/null @@ -1,98 +0,0 @@ -# Background -A message ordering bug is reported for session `session:default:sub-1776598618924-10`: newer messages sometimes render above older messages in the front-end timeline. The task is to use readonly Turso environment variables to inspect persisted data and identify the likely root cause. - -# Goals -1. Connect to the readonly Turso database using the provided environment variables. -2. Inspect message/task records for the target session and verify whether database ordering is stable by creation time. -3. Correlate database ordering behavior with UI symptoms and identify the most likely bug source. -4. Produce actionable remediation guidance (query/index/sort key strategy) and validation checks. - -# Implementation Plan (phased) -## Phase 1: Locate schema and read paths -- Find tables and columns storing messages/events for sessions. -- Find server/client code paths that query and render timeline data. -- Identify current `ORDER BY` strategy and tie-breakers. - -## Phase 2: Database evidence collection -- Connect with `TURSO_DATABASE_URL_READONLY` and `TURSO_AUTH_TOKEN_READONLY`. -- Pull records for `session:default:sub-1776598618924-10` and compare: - - `created_at` order - - insertion/key order (e.g., `id`, `rowid`) - - any event timestamp fields used by UI. -- Detect equal timestamps or mixed precision that can produce nondeterministic ordering. - -## Phase 3: Root-cause analysis and fix proposal -- Determine whether the issue is caused by DB query ordering, merge logic, or front-end sort. -- Propose exact ordering contract (primary + tie-breaker keys) from oldest to newest. -- Add a concise analysis artifact in-repo for maintainers. - -# Acceptance Criteria -- A reproducible data snapshot demonstrates why unstable ordering occurs for the target session. -- The analysis maps symptoms to concrete columns/query behavior in code. -- A clear fix recommendation is documented, including deterministic ordering keys and validation command(s). - -# Investigation Notes (2026-04-20) -- Connected to Turso read-only endpoint using provided in-memory credentials (not persisted to files). -- Session inspected: `session:default:sub-1776598618924-10`. -- Row count: `18`. -- Confirmed same-timestamp collisions in this session: - - `2026-04-19T19:38:10.305` has 2 rows (`write_seq` 1757..1758). - - `2026-04-19T23:41:38.860` has 2 rows (`write_seq` 1764..1765). -- Code-path analysis still identifies a deterministic ordering gap: - - Backend returns messages ordered by monotonic `write_seq`. - - Mobile client discarded `writeSeq` and re-sorted by `createdAt` only. - - When multiple rows share the same `createdAt` (common under async writes / second-level collisions), UI order depends on fallback keys and can drift from actual write order. -- Implemented remediation in app code: - 1. Parse and carry `writeSeq` in `ChatMessage`. - 2. Use `writeSeq` as tie-breaker when `createdAt` is equal. - 3. Added regression test verifying equal-`createdAt` messages keep `writeSeq` order. - -## Direct answers to review questions - -### 1) “How do you confirm there are same-timestamp conflicts?” -- Confirmed from live DB rows in your target session with this grouping query: - - `SELECT created_at, COUNT(*), MIN(write_seq), MAX(write_seq) ... GROUP BY created_at HAVING COUNT(*) > 1`. -- Result shows 2 concrete collision groups: - - `2026-04-19T19:38:10.305` (`write_seq` 1757..1758), - - `2026-04-19T23:41:38.860` (`write_seq` 1764..1765). - -### 2) “Do you read data?” -- Yes. Read directly from Turso with readonly token in-memory and queried `chat_messages` for your session. -- Verification SQL used: - -```sql -SELECT - message_id, - write_seq, - created_at -FROM chat_messages -WHERE session_id = 'session:default:sub-1776598618924-10' -ORDER BY created_at ASC, write_seq ASC; -``` - -Then check collisions: - -```sql -SELECT - created_at, - COUNT(*) AS n -FROM chat_messages -WHERE session_id = 'session:default:sub-1776598618924-10' -GROUP BY created_at -HAVING COUNT(*) > 1 -ORDER BY created_at ASC; -``` - -### 3) “What is the precision of create time? Can it hit this condition?” -- Effective precision in this dataset is mixed: - - some rows are second-level style (e.g. `2026-04-19 11:37:11`), - - some rows are millisecond ISO strings (e.g. `2026-04-19T23:41:38.860`). -- So yes, same-`created_at` condition is real (observed above), and timeline ordering must include deterministic tie-breakers (e.g., `writeSeq`) rather than relying on `createdAt` alone. - -## Screenshot flow re-check -- Re-checked against the provided screenshots: the timeline shows mixed clock representations (`11:37`, `15:41`, `23:41`) and user/assistant blocks that can be affected by inconsistent `created_at` semantics across write paths. -- This means “`createdAt` primary + `writeSeq` only tie-break” is insufficient for some real flows: order can still drift even when timestamps are not exactly equal. -- Updated comparator strategy to: - 1. use `writeSeq` as primary key whenever both messages have it (server-synced rows), - 2. fallback to `createdAt/timestamp` only when `writeSeq` is unavailable. -- Added regression test that reproduces “clock order disagrees with write order” and verifies the list still follows `writeSeq`. diff --git a/docs/plans/2026-04-20-07-14-UTC-message-order-root-cause-fix.md b/docs/plans/2026-04-20-07-14-UTC-message-order-root-cause-fix.md deleted file mode 100644 index fb1ea909..00000000 --- a/docs/plans/2026-04-20-07-14-UTC-message-order-root-cause-fix.md +++ /dev/null @@ -1,34 +0,0 @@ -# Message ordering root-cause fix plan (session:default:sub-1776598618924-10) - -## Background -The UI showed assistant replies before their corresponding user queries for `session:default:sub-1776598618924-10`. -Live Turso rows confirm this is not only a same-timestamp collision problem: -- For the same task IDs, assistant rows have lower `write_seq` than user rows in several pairs. -- The same rows keep immutable `seq_id` order as user -> assistant. -- `created_at` format is mixed (`YYYY-MM-DD HH:MM:SS` and ISO8601 with timezone), indicating inconsistent timestamp sources/parsing. - -## Goals -1. Ensure rendered order follows semantic conversation order (user prompt before assistant reply). -2. Avoid using mutable sync cursor (`write_seq`) as a chronological ordering key. -3. Normalize timestamp parsing for legacy DB values without timezone suffix. -4. Add regression tests that model the DB-observed inversion pattern. - -## Implementation Plan (phased) -1. **Model and mapping updates** - - Add `seqId` to `ChatMessage` and parse from server payload. - - Keep `writeSeq` for sync cursor semantics only. -2. **Ordering logic updates** - - Update comparator: `seqId` primary (when available), then `createdAt/timestamp`, then role/messageId. - - Keep deterministic fallback behavior for mixed/local-only messages. -3. **Timestamp parsing normalization** - - Parse server `createdAt` strings without timezone as UTC to avoid local-time misinterpretation. -4. **Regression tests** - - Add/adjust tests for `seqId` vs `writeSeq` conflict and no-timezone UTC parsing behavior. -5. **Code map sync** - - Update `docs/code_maps/feature_map.yaml` and `docs/code_maps/logic_map.yaml` to reflect the corrected ordering strategy. - -## Acceptance Criteria -- Given rows where `write_seq` contradicts prompt/reply semantics, rendered order remains user-before-assistant using `seqId`. -- Given `createdAt` in `YYYY-MM-DD HH:MM:SS`, displayed local time reflects UTC-origin input rather than raw local parsing. -- `cd apps/mobile_chat_app && flutter test test/chat_message_sort_test.dart` passes. -- `cd apps/mobile_chat_app && flutter test test/chat_message_test.dart` passes (or equivalent parsing-focused tests). diff --git a/docs/plans/2026-04-20-07-54-UTC-user-query-delivery-status.md b/docs/plans/2026-04-20-07-54-UTC-user-query-delivery-status.md deleted file mode 100644 index a16ae589..00000000 --- a/docs/plans/2026-04-20-07-54-UTC-user-query-delivery-status.md +++ /dev/null @@ -1,22 +0,0 @@ -# Background -Users need clearer visual delivery states on user query bubbles when chat routing can be synchronous (default LLM route) or asynchronous remote routes (OpenClaw and future routers). - -# Goals -- Show no delivery mark for default-route user messages. -- Show OpenClaw-specific status progression on user bubbles: pending/accepted and pulled states as gray lobster, then green check after reply. -- Show generic remote-route status progression on user bubbles: gray check after persistence, green check after reply. -- Keep status logic robust when state updates arrive later through sync/history refresh. - -# Implementation Plan (phased) -1. Extend chat message metadata parsing/storage in mobile app to preserve server `source` metadata and router info from `/api/chat/respond`. -2. Update send flow to propagate router-derived source metadata back onto the local user message after respond acknowledgement. -3. Add delivery-indicator derivation in `MessageList` for user messages based on route type and assistant reply state within the same task. -4. Render visual indicators beside user message timestamps using lobster/check icons and route-specific color rules. -5. Add widget tests to cover default route, OpenClaw pending/completed, and generic remote pending/completed behavior. -6. Sync code maps for chat-session regression coverage updates. - -# Acceptance Criteria -- For default route (`backend.respond`), user bubbles show no delivery status icon. -- For OpenClaw route (`backend.respond.openclaw`), user bubbles show gray lobster before completion and green check after assistant reply completion. -- For other remote routes (`backend.respond.`), user bubbles show gray check before completion and green check after assistant reply completion. -- `flutter test test/message_list_test.dart` passes from `apps/mobile_chat_app`. diff --git a/docs/plans/2026-04-20-08-57-UTC-controlled-user-query-commit.md b/docs/plans/2026-04-20-08-57-UTC-controlled-user-query-commit.md deleted file mode 100644 index 4494bc21..00000000 --- a/docs/plans/2026-04-20-08-57-UTC-controlled-user-query-commit.md +++ /dev/null @@ -1,22 +0,0 @@ -# Background -The chat frontend previously persisted messages by repeatedly upserting the whole in-memory message list. During streaming and state transitions this caused replay writes, write sequence churn, and potential ordering side effects. - -# Goals -1. Stop full-list client-side persistence on every streaming delta or state transition. -2. Rely on the backend `/respond` endpoint for authoritative user-query persistence (the endpoint already upserts the user message before generating a response). -3. Reduce unnecessary historical message upserts to improve write sequence stability and ordering signal quality. - -# Implementation Plan (phased) -1. Remove debounced whole-list persistence (`_persistActiveScopeMessages` / `_doPersistActiveScopeMessages`) from `chat_screen.dart`. -2. Remove the `_persistDebounce` timer and related cleanup. -3. Remove all call sites that trigger whole-list upserts during streaming, message updates, and scope changes. -4. Confirm that the backend `/respond` endpoint already persists the user message (both sync and OpenClaw async paths). -5. Verify that no client-side single-message commit remains — the `/respond` endpoint is the single source of persistence for user queries. -6. Run targeted Dart/Flutter tests and formatting for touched files. -7. Review code-map impact and update maps only if feature/logic/test entry indexing changed. - -# Acceptance Criteria -- Streaming assistant updates no longer trigger full-history upsert requests from the client. -- User message persistence is handled exclusively by the backend `/respond` endpoint (one write per send). -- No client-side duplicate upsert of the user message occurs alongside the `/respond` call. -- Existing chat history API service tests pass for updated behavior. diff --git a/docs/plans/2026-04-20-09-40-UTC-openclaw-accountid-userid-scoping.md b/docs/plans/2026-04-20-09-40-UTC-openclaw-accountid-userid-scoping.md deleted file mode 100644 index 9952ab94..00000000 --- a/docs/plans/2026-04-20-09-40-UTC-openclaw-accountid-userid-scoping.md +++ /dev/null @@ -1,15 +0,0 @@ -# Problem - -The Bricks OpenClaw plugin currently uses a fixed `DEFAULT_ACCOUNT_ID` for its single account slot. When the configured `BRICKS_PLATFORM_TOKEN` changes to a different Bricks user, OpenClaw still sees the same account id, so its internal per-account routing/session scoping can overlap across users. - -# Proposed approach - -- Keep the plugin's flat channel config model as-is. -- Derive the OpenClaw `accountId` from the configured platform token's `userId` claim. -- If the stored `BRICKS_PLUGIN_ID` / `BRICKS_PLATFORM_TOKEN` cannot produce a valid user-scoped identity, surface an explicit error instead of silently falling back to `DEFAULT_ACCOUNT_ID`. -- Do not change the plugin state file layout in this step. - -# Notes - -- This fixes OpenClaw's account-slot scoping only; it does not yet isolate the plugin's own persisted state file. -- Validation for this step is limited to the plugin package checks and local `openclaw status`. diff --git a/docs/plans/2026-04-20-10-19-UTC-openclaw-reply-failure-db-analysis.md b/docs/plans/2026-04-20-10-19-UTC-openclaw-reply-failure-db-analysis.md deleted file mode 100644 index 72132d7c..00000000 --- a/docs/plans/2026-04-20-10-19-UTC-openclaw-reply-failure-db-analysis.md +++ /dev/null @@ -1,191 +0,0 @@ -# OpenClaw reply failure DB analysis - -## Problem - -The current async OpenClaw flow can persist a delayed assistant reply, but the -database model does not express the reply relationship explicitly. Core async -workflow state is also encoded in message metadata (`pendingAssistantMessageId`) -and exported with a text search, which is brittle. - -## Proposed approach - -Keep `chat_messages` as the source of truth for visible conversation history and -preserve `write_seq` ordering for late replies. Normalize the async reply -contract into first-class columns so the backend and plugin do not depend on -metadata string matching for routing or reply linkage. - -Recommended schema direction: - -1. Add `reply_to_message_id` on assistant messages to explicitly link `A1 -> A`. -2. Add `source_message_id` on `chat_tasks` so each async/OpenClaw task records - which user message triggered it. -3. Keep pending async execution state on `chat_tasks` (or a future task-specific - table), rather than encoding a one-to-one future reply id on the message row. -4. Add indexes/constraints so reply linkage stays intra-user and lookup remains - efficient. - -## Todos - -- Confirm the exact schema migration shape for `reply_to_message_id` and - `chat_tasks.source_message_id`. -- Update platform export/create/patch logic to read/write first-class reply - columns instead of metadata-only workflow markers. -- Decide whether transient assistant placeholder rows should remain visible in - `chat_messages` or move to a non-message status path. -- Add tests covering delayed OpenClaw replies, explicit reply references, and - mixed default/OpenClaw ordering. - -## Notes - -- `write_seq` already gives the desired DB ordering for late replies like - `A, B, B reply, A reply`. -- The active client ordering bug is primarily a UI merge issue, not a DB - ordering limitation. -- A separate relation table is possible, but likely overkill for the current - one-reply-per-user-message model. -- `expected_reply_message_id` on `chat_messages` is no longer recommended, - because future branching can produce multiple child replies for a single - source message. -- Runtime evidence from local OpenClaw + platform API: - - Plugin state shows processed event IDs and assistant client-token mappings - for five historical OpenClaw events, so those events reached - `handleMessageCreated(...)`. - - The state file still contains `pendingAck` and the persisted cursor remains - `cur_0`. - - Direct `GET /api/v1/platform/events` with the configured plugin token works, - but direct `POST /api/v1/platform/events/ack` returns `500 INTERNAL_ERROR`. - - Querying events after the pending cursor shows newer events waiting - unprocessed, so the runner is currently blocked on ack retry before it can - fetch subsequent messages. - - Vercel error logs confirm the server-side cause: `ackPlatformEvents()` - emitted PostgreSQL-only SQL that Turso/libSQL rejects with - `SQL_PARSE_ERROR`. - - Implemented fix: keep the existing PostgreSQL `jsonb_set + UNNEST` batch - update for Postgres, and use a Turso/libSQL-specific atomic - `json_patch(json_object(...))` batch update instead. - -## Implementation update - -- `apps/node_openclaw_plugin` no longer replies with the hard-coded - `收到消息:...` echo. -- The runner now resolves Bricks conversation topology, maps it to a stable - OpenClaw session key (`channelId` base session plus `:thread:` when - applicable), and dispatches the inbound turn through OpenClaw's real - `recordInboundSessionAndDispatchReply(...)` pipeline. -- OpenClaw-visible reply payloads are accumulated back into the reserved Bricks - assistant placeholder row, so the user sees one assistant message updated as - OpenClaw produces text/media output. -- The runner persists `clientToken -> messageId` immediately after placeholder - creation so retries reuse the same assistant row if OpenClaw dispatch fails - after the placeholder was already created. -- Validated locally with: - - `cd apps/node_openclaw_plugin && npm test` - - `cd apps/node_openclaw_plugin && npm run type-check` - - `cd apps/node_openclaw_plugin && npm run build` - - a 10-second real runtime smoke start using the user's configured - `~/.openclaw/openclaw.json`, which booted successfully on cursor - `cur_1883`. -- Follow-up lifecycle completion: - - The Bricks channel plugin now implements `gateway.startAccount/stopAccount` - so OpenClaw gateway, not a manually launched shell process, owns the pull - runner lifecycle. - - Verified against real gateway logs in `~/.openclaw/logs/gateway.log`: - - `starting Bricks pull runner` - - `[node_openclaw_plugin] started with cursor: cur_1892` - - `stopping Bricks pull runner` - - `[node_openclaw_plugin] stopped` - - This means “true reply” now comes from the full OpenClaw-managed path: - Bricks event -> gateway-managed plugin runner -> OpenClaw internal - AI/session pipeline -> Bricks message writeback. -- Follow-up rate-limit completion: - - The repeated `429` loop was caused by two problems together: - - authenticated `/api/v1/platform/*` polling still shared the coarse - generic `/api/*` IP limiter (`100 / 15 min / IP`) - - the plugin retried platform failures on a fixed poll interval, so once it - hit `429` it kept hammering the same limit bucket - - Backend fix: - - authenticated platform requests now bypass the generic app limiter - - `apps/node_backend/src/routes/platform.ts` now applies a dedicated - platform limiter with separate read/write budgets, stable structured `429` - responses, and `Retry-After` - - Plugin fix: - - `PlatformHttpError` now captures `Retry-After` - - the runner now treats `429` and retryable platform failures as backoff - signals (`2s -> 4s -> 8s -> 10s`, while preferring backend-provided - `Retry-After` when present) - - Validated with: - - `cd apps/node_backend && npm test -- --run src/app.test.ts src/routes/platform.test.ts` - - `cd apps/node_backend && npm run type-check` - - `cd apps/node_backend && npm run build` - - `cd apps/node_openclaw_plugin && npm test` - - `cd apps/node_openclaw_plugin && npm run type-check` - - `cd apps/node_openclaw_plugin && npm run build` - - real `openclaw gateway restart` plus `~/.openclaw/logs/gateway.log` - sampling, which showed the old runner stop, the new runner restart on - `cur_1892`, and no new sampled `429` lines after the restart -- Follow-up replay/writeback diagnosis: - - The OpenClaw session log for `sessionId=cd08fd10-ef3c-42fa-a8d8-7b06011c0eb3` - shows two runs of the same Bricks user message `msg-1776707308374-8`. - - The earlier run produced a normal assistant reply (`信号非常清晰,我已经收到了...`), - which strongly suggests the original user turn was answered inside OpenClaw. - - A later replay of that same message produced only `\nNO_REPLY`, after - which OpenClaw's embedded runner surfaced the generic visible error - `⚠️ Agent couldn't generate a response. Please try again.` - - This means the observed `payloads=0`/`NO_REPLY` behavior is most likely a - retry artifact after an earlier final Bricks writeback failure, not proof - that the initial model run generated no answer. -- Follow-up plugin hardening: - - The runner now persists accumulated visible reply text to - `clientTokenReplyTextMap` before attempting the platform patch/writeback. - - That lets a later retry recover the original OpenClaw answer even when the - first writeback attempt fails after payload generation. - - Added a regression test covering the sequence “reply generated -> writeback - fails -> reply text remains persisted for retry recovery”. - - Revalidated `apps/node_openclaw_plugin` successfully with: - - `npm test` - - `npm run type-check` - - `npm run build` - - Restarted the local gateway again and verified a fresh Bricks runner start - at `cur_1896` in `~/.openclaw/logs/gateway.log`. -- Latest undeployed live behavior clarification: - - The UI marker `task:accepted · id:...` is an optimistic/local async task - placeholder created by the client before `/api/chat/respond` completes. - - On the async OpenClaw backend path, the server persists the user message and - returns `mode=async/state=accepted`, but it does not persist the plugin - assistant placeholder at that moment. - - The visible assistant row `Node OpenClaw Plugin 正在处理...` is created later - only after the plugin successfully polls the platform event and calls - `/api/v1/platform/messages`. - - During the newest undeployed test, plugin state remained at `cur_1902` and - the runtime log recorded `retryable platform failure; backing off for - 653000ms` at `2026-04-21T02:19:47+08:00`, which means the plugin hit the - remote backend's old limiter before `getEvents(...)` returned newer events. - - Therefore the UI can legitimately show `task:accepted` while showing no - plugin placeholder at all: the app-side async task was created, but the - plugin never reached the event-processing/writeback stage. -- Additional rate-limit caveat: - - `/api/chat/respond` is still subject to the generic `/api/*` IP limiter in - `apps/node_backend/src/app.ts`. - - The current code only exempts `/api/chat/sync/*` and authenticated - `/api/v1/platform/*`; `apps/node_backend/src/routes/chat.ts` does not apply a - dedicated limiter to `/respond`. - - That means repeated manual/browser testing can receive `429 Too Many - Requests` on the initial send path before the async OpenClaw handoff even - starts. - - In that case the limiter rejects the request before the route handler runs, - so the backend does not accept/persist the task through `/api/chat/respond`. -- Follow-up `/api/chat/respond` limiter fix: - - Authenticated `/api/chat/respond` now bypasses the generic `/api/*` IP - limiter in `apps/node_backend/src/app.ts`, just like chat sync/platform - routes already do. - - `apps/node_backend/src/routes/chat.ts` now adds a dedicated route-specific - limiter for `/respond`, keyed by `userId:sessionId`, with a budget of - `120 requests / minute / user-session`. - - Added regression coverage proving: - - authenticated `/api/chat/respond` no longer exhausts the generic IP bucket - - `/api/chat/respond` is still capped per user/session - - different sessions get independent `/respond` limiter buckets - - Revalidated `apps/node_backend` successfully with: - - `npm test -- --run src/app.test.ts src/routes/chat.test.ts src/routes/platform.test.ts` - - `npm run type-check` - - `npm run build` diff --git a/docs/plans/2026-04-20-19-29-UTC-platform-ack-scaling-dialect-cleanup.md b/docs/plans/2026-04-20-19-29-UTC-platform-ack-scaling-dialect-cleanup.md deleted file mode 100644 index d48a157d..00000000 --- a/docs/plans/2026-04-20-19-29-UTC-platform-ack-scaling-dialect-cleanup.md +++ /dev/null @@ -1,34 +0,0 @@ -# Platform ack scaling and dialect cleanup - -## Problem - -Two follow-up issues remain in the platform ack path: - -1. The Turso/libSQL ack update builds a large `OR` predicate with two bind - parameters per acked message and the `/api/v1/platform/events/ack` route - does not currently cap `ackedEventIds`. -2. `platformIntegrationService.ts` re-reads `process.env.TURSO_DATABASE_URL` - even though the DB module already selects the active pool/dialect. - -## Approach - -- Add a clear server-side batch cap for `ackedEventIds`, aligned with the - platform events page-size limit. -- Chunk Turso ack updates into smaller SQL statements so large valid batches do - not build oversized statements. -- Expose DB dialect metadata from the DB module and branch on that instead of - duplicating env-var checks in service code. -- Add/adjust backend tests for route validation, Turso SQL chunking, and - dialect-driven behavior. - -## Validation - -- `cd apps/node_backend && npm test -- --run src/routes/platform.test.ts src/services/platformIntegrationService.test.ts` -- `cd apps/node_backend && npm run type-check` -- `cd apps/node_backend && npm run build` - -## Notes - -- This task is a follow-up to review feedback after the OpenClaw/platform - handoff work already merged. -- The intended PR should be a fresh branch from latest `main`. diff --git a/docs/plans/2026-04-20-19-34-UTC-multi-node-plugin-routing-blueprint.md b/docs/plans/2026-04-20-19-34-UTC-multi-node-plugin-routing-blueprint.md deleted file mode 100644 index c40a1e92..00000000 --- a/docs/plans/2026-04-20-19-34-UTC-multi-node-plugin-routing-blueprint.md +++ /dev/null @@ -1,135 +0,0 @@ -# Background -The current implementation supports issuing a single OpenClaw-oriented platform token from Settings (`Openclaw Token`) and uses `pluginId` as the token-scoping identity. This works for one plugin runtime but does not provide a user-facing concept of multiple target runtimes (“nodes”) such as: -- a local company computer OpenClaw runtime, -- a cloud runtime on AWS, -- and future additional plugin runtimes. - -From code inspection, the current behavior is: -- Mobile app Settings exposes one token generation page and calls `GET /api/config/platform-token?pluginId=<...>` with a default plugin id (`plugin_local_main`). -- Node routing identity in backend platform APIs is `X-Bricks-Plugin-Id` + JWT claim `pluginId` + optional scoped `userId`. -- Chat async persistence stores `resolvedBotId`, `agentName`, `source`, etc. in message metadata, and the UI currently displays assistant attribution using `agentName` (which is currently set to `resolvedBotId`, so it often appears as `ask`). - -The requested enhancement is to add a “Node” layer where each token belongs to one node name, and messages sent to a node are retrievable only by that node’s token (JWT algorithm unchanged). - -# Goals -1. Introduce a first-class **Node** model (display name + stable identity) so users can manage multiple plugin endpoints under Settings. -2. Make platform tokens node-scoped by binding token issuance/verification to a node-owned plugin identity (without changing JWT signing algorithm/protocol family). -3. Ensure message delivery isolation: a token for Node A cannot read/ack/write Node B messages. -4. Add Settings UX for node management: - - “节点” entry point, - - empty-state “create first node” CTA, - - auto-generated default names (`openclaw 1`, `openclaw 2`, …; extensible naming strategy including zodiac labels), - - editable node names, - - copy conveniences under each node. -5. Update chat message attribution so assistant items display **Node Name** instead of generic `ask` when message source is node-delivered. -6. Preserve backward compatibility/migration path for existing single-token users and currently deployed node_openclaw_plugin runtimes. - -# Implementation Plan (phased) -## Phase 0: Domain framing and data model decisions -1. Define Node identity split: - - `node_id` (stable storage identity, immutable), - - `display_name` (editable label shown in UI). -2. Define node-to-plugin binding strategy: - - Option A (recommended): one stable `pluginId` per node persisted server-side. - - Option B: derive pluginId from node_id deterministically. -3. Keep token algorithm unchanged (existing JWT signing/verification), but include `pluginId` corresponding to node binding. -4. Confirm compatibility policy: - - Existing `plugin_local_main` treated as a migrated default node, - - no breaking change for old clients during rollout window. - -## Phase 1: Backend data layer and migration -1. Add DB migration(s) in `apps/node_backend/src/db/migrations/` for node entities, for example: - - `platform_nodes` table: - - `id` (PK), - - `user_id`, - - `node_id` (unique per user), - - `display_name`, - - `plugin_id` (unique per user), - - timestamps, - - optional soft-delete/archive field. -2. Add indexes and constraints: - - uniqueness for `(user_id, node_id)`, `(user_id, plugin_id)`, - - non-empty normalized display_name. -3. Add a migration routine or lazy bootstrap path to create a default node for users without nodes. - -## Phase 2: Backend services and APIs -1. Add `platformNodeService` (or equivalent) in `apps/node_backend/src/services/`: - - list nodes, - - create node with generated default name, - - rename node, - - resolve node by `nodeId` and/or `pluginId`. -2. Extend config routes (`apps/node_backend/src/routes/config.ts`): - - replace/augment `GET /config/platform-token` with node-aware issuance (`nodeId` parameter). - - response includes node metadata (`nodeId`, `nodeName`, `pluginId`, `token`, `scopes`, `baseUrl`, `expiresIn`). -3. Add dedicated node management routes (recommended under `/api/config/nodes` or `/api/platform/nodes`): - - `GET /nodes` list, - - `POST /nodes` create, - - `PATCH /nodes/:nodeId` rename, - - optional `DELETE /nodes/:nodeId` (only if product wants archival/removal now). -4. Ensure token issuance rejects unknown/foreign nodeId and always signs token with node-owned pluginId. - -## Phase 3: Platform auth and isolation guarantees -1. Keep `issuePlatformAccessToken` algorithm unchanged, but enforce that token `pluginId` comes from node binding. -2. Maintain existing middleware checks in `authenticatePlatformApiKey`: - - JWT `pluginId` must match header `X-Bricks-Plugin-Id`. -3. Strengthen platform integration service queries (`platformIntegrationService`) to remain plugin-scoped and user-scoped. -4. Add explicit guard tests for cross-node access attempts: - - token from node A + header for node B => forbidden, - - node A token cannot ACK/patch messages created under node B plugin scope. - -## Phase 4: Mobile app settings UX for Nodes -1. Add a new Settings entry: `节点` (Node Settings). -2. Build node list screen: - - empty state with “创建第一个节点” button, - - populated list with each node card showing name, plugin id summary, copy actions. -3. Create node flow: - - default naming generator (`openclaw 1`, `openclaw 2`, …), - - optional alternative naming strategy hook (zodiac sequence) behind utility/service. -4. Rename flow: - - inline or modal edit with validation. -5. Token actions per node: - - fetch/generate token for selected node, - - copy token, - - copy install instructions with node-specific pluginId/token. -6. Refactor existing `OpenclawTokenSettingsScreen` into node-centric UI or keep as backward-compatible subpage delegated from node details. - -## Phase 5: Chat attribution update (show Node Name instead of ask) -1. Decide attribution source of truth for assistant messages: - - prefer `metadata.nodeName` when source is node-delivered, - - fallback to `agentName` for legacy records. -2. Update server write paths (`chat.ts`, `platformIntegrationService.ts`) to include node metadata (`nodeId`, `nodeName`) when messages originate from platform/plugin pipeline. -3. Update `ChatHistoryApiService._messageFromServerMap` and/or `ChatMessage` mapping to preserve and expose node attribution. -4. Update `MessageList` rendering logic so assistant chip shows node name for node-origin messages (instead of `ask`). -5. Ensure historical compatibility for records without node metadata. - -## Phase 6: Plugin/runtime compatibility (node_openclaw_plugin) -1. Update plugin setup docs and runtime expectations: - - each runtime uses node-specific `BRICKS_PLUGIN_ID` + token. -2. No JWT algorithm changes in plugin claim parser; only values differ by node. -3. Optional: improve plugin logs to include node identity for observability. - -## Phase 7: Tests, regression checks, and docs/code map updates -1. Backend tests: - - route tests for node CRUD and node-scoped token issuance, - - middleware tests for cross-node rejection, - - integration-service tests for plugin/user isolation. -2. Flutter tests: - - settings navigation includes 节点 entry, - - empty-state/create/rename/copy interactions, - - node-aware token instruction rendering, - - message list attribution renders node name. -3. Validation commands: - - `./tools/init_dev_env.sh` - - `cd apps/node_backend && npm test` - - `cd apps/mobile_chat_app && flutter test` - - optional targeted checks: `cd apps/mobile_chat_app && flutter analyze` -4. Update code maps (`docs/code_maps/feature_map.yaml`, `docs/code_maps/logic_map.yaml`) after implementation because feature entry paths, business logic, and tests will change. - -# Acceptance Criteria -1. User can create multiple nodes in Settings, starting from empty-state CTA, and can rename each node with persisted result. -2. Each node can issue/copy its own token and install instructions, and returned token payload clearly indicates node binding. -3. A message addressed to a node is only retrievable/acknowledgeable/updateable by that node’s token scope (pluginId isolation enforced). -4. Existing JWT algorithm/protocol remains unchanged; only claim values (pluginId) reflect node binding. -5. In chat list UI, assistant messages generated via node/plugin path display the corresponding Node Name instead of generic `ask` when node metadata is available. -6. Legacy users with pre-existing single-token setup can continue working via migrated/default node behavior. -7. Automated tests covering node lifecycle, token scoping, and UI attribution pass in CI/local validation commands. diff --git a/docs/plans/2026-04-20-19-36-UTC-chat-sync-long-polling.md b/docs/plans/2026-04-20-19-36-UTC-chat-sync-long-polling.md deleted file mode 100644 index a4c759b1..00000000 --- a/docs/plans/2026-04-20-19-36-UTC-chat-sync-long-polling.md +++ /dev/null @@ -1,36 +0,0 @@ -# Background - -`/api/chat/sync/:sessionId` is currently consumed by fixed-interval short polling from the mobile chat app. When the conversation has pending async tasks (especially OpenClaw routing), this can generate many repeated requests with no new data and hit backend rate limits. - -# Goals - -1. Reduce unnecessary `/api/chat/sync` request frequency while preserving near-real-time updates. -2. Keep backward compatibility for existing clients that do not send long-poll parameters. -3. Add automated coverage for new long-poll behavior. - -# Implementation Plan (phased) - -## Phase 1: Backend long-poll support -- Add optional `waitMs` query parsing to `GET /api/chat/sync/:sessionId`. -- If no messages are available, keep the request open and retry sync checks until either: - - new messages are found, or - - `waitMs` timeout expires. -- Cap `waitMs` with a safe upper bound to avoid runaway request lifetimes. - -## Phase 2: Client sync call upgrade -- Extend `ChatHistoryApiService.sync` to accept optional `waitMs` and append it to sync query parameters. -- Update chat screen sync loop to request long polling (bounded timeout) instead of pure short polling. - -## Phase 3: Validation -- Update Node route tests to cover long-poll query parsing and bounded wait behavior. -- Update Flutter service tests to verify `waitMs` query transmission. -- Run targeted backend and Flutter tests. - -# Acceptance Criteria - -1. Chat sync endpoint accepts `waitMs` and does not block longer than the configured max bound. -2. Mobile chat sync requests include long-poll timeout for active scope polling. -3. Existing sync behavior remains compatible when `waitMs` is omitted. -4. Relevant route/service tests pass: - - `npm test -- src/routes/chat.test.ts` - - `cd apps/mobile_chat_app && flutter test test/chat_history_api_service_test.dart` diff --git a/docs/plans/2026-04-21-00-12-UTC-multi-node-plugin-implementation.md b/docs/plans/2026-04-21-00-12-UTC-multi-node-plugin-implementation.md deleted file mode 100644 index a945e0fd..00000000 --- a/docs/plans/2026-04-21-00-12-UTC-multi-node-plugin-implementation.md +++ /dev/null @@ -1,34 +0,0 @@ -# Background -The repository currently supports issuing one OpenClaw platform token from settings, but lacks a first-class Node model that can bind tokens to specific runtimes (local machine, cloud runtime, etc.). - -# Goals -- Implement node management APIs and persistence in backend. -- Make platform token issuance node-aware while keeping existing JWT algorithm unchanged. -- Add Settings UI for node list/create/rename and node-scoped token copy/install instructions. -- Display node attribution in assistant messages instead of generic ask where node metadata is available. -- Add tests for backend and Flutter changes. - -# Implementation Plan (phased) -## Phase 1: Backend foundation -1. Add DB migration for `platform_nodes`. -2. Add service for CRUD/list + default-name generation. -3. Add `/api/config/nodes` endpoints and node-aware `/api/config/platform-token`. - -## Phase 2: Chat metadata & attribution -1. Extend platform message persistence metadata with `nodeName` using plugin id mapping. -2. Update chat message mapping/rendering fallback to show node name chip. - -## Phase 3: Mobile settings UX -1. Add Node Settings entry in settings screen. -2. Add node list page with empty-state create button, rename, token generation, copy actions. -3. Reuse existing install-instruction format with node-scoped pluginId/token. - -## Phase 4: Validation and docs/index updates -1. Run backend unit tests and mobile flutter tests. -2. Update code maps for new entry points and logic indexes. - -# Acceptance Criteria -- Users can create and rename multiple nodes in settings. -- Platform token can be generated for a specific node and includes node-bound pluginId. -- Assistant message attribution shows node name when available. -- Backend + Flutter tests pass for new behavior. diff --git a/docs/plans/2026-04-21-01-07-UTC-chat-sync-sse.md b/docs/plans/2026-04-21-01-07-UTC-chat-sync-sse.md deleted file mode 100644 index 86307562..00000000 --- a/docs/plans/2026-04-21-01-07-UTC-chat-sync-sse.md +++ /dev/null @@ -1,87 +0,0 @@ -# Replace Long Polling with SSE for Chat Sync - -**Date:** 2026-04-21 01:07 UTC - -## Background - -PR #174 introduced long-polling on `GET /api/chat/sync/:sessionId` (via a `waitMs` query -parameter) to reduce the number of empty short-poll round-trips when multi-agent group -conversations are idle. A review comment on that PR argued—correctly—that SSE (Server-Sent -Events) is a strictly better transport for the same use-case because: - -* A single persistent HTTP connection replaces the repeated TCP/TLS handshake-tear-down cycle - of long-polling. -* The server can push multiple events (one per bot reply) over the same open pipe. -* AI streaming "typewriter" output maps naturally onto SSE frames. -* SSE's `EventSource` reconnect semantics are well-understood and free of the state-management - complexity that long-poll reconnection requires. - -## Goals - -1. Remove long-polling (`waitMs`, the retry loop, `sleep()`) from the sync endpoint. -2. Add a new `GET /api/chat/events/:sessionId` SSE endpoint on the Node.js backend. -3. Replace the Flutter timer-based polling with a persistent SSE stream subscription. -4. Keep the simple (non-long-poll) `GET /api/chat/sync/:sessionId` endpoint intact for - on-demand fetches (e.g. after history load). -5. Update all affected tests. - -## Implementation Plan - -### Phase 1 – Backend (`apps/node_backend/src/routes/chat.ts`) - -* Remove `CHAT_SYNC_LONG_POLL_MAX_WAIT_MS`, `CHAT_SYNC_LONG_POLL_INTERVAL_MS`, `sleep()`. -* Revert the `/sync/:sessionId` handler to a simple single-call `syncMessages` + `res.json`. -* Add `GET /events/:sessionId` handler: - * Rate-limited at 10 new connections / user / session / minute (`eventsLimiter`). - * Sets `Content-Type: text/event-stream`, `Cache-Control: no-cache`, `Connection: keep-alive`. - * Sends `setInterval` keep-alive heartbeat comments every 15 s. - * Polls `syncMessages` every 1 s via `setTimeout`; when new data arrives writes - `data: \n\n` and advances `afterSeq`. - * Cleans up timers on `req.on('close')`. - -### Phase 2 – Flutter service (`apps/mobile_chat_app/lib/features/chat/chat_history_api_service.dart`) - -* Remove `waitMs` parameter from `_syncUri()` and `sync()`. -* Add `_eventsUri(sessionId, afterSeq)` URI helper. -* Add `Stream listenEvents({token, sessionId, afterSeq})` using - `http.Client.send()` for a streaming response, parsing SSE lines into snapshots. - -### Phase 3 – Flutter screen (`apps/mobile_chat_app/lib/features/chat/chat_screen.dart`) - -* Replace `Timer? _syncTimer`, `bool _syncInFlight`, backoff fields with - `StreamSubscription? _sseSubscription`. -* Replace `_cancelSyncPolling` → `_disconnectSse`. -* Replace `_scheduleSync` / `_configureActiveScopeSync` / `_syncActiveScope` with - `_connectSse` / `_configureActiveScopeSync` / `_applySseSnapshot`. -* On SSE stream error or done: reconnect after `_sseReconnectDelay` (3 s) if still mounted - and scope hasn't changed. - -### Phase 4 – Tests - -* **Backend** (`chat.test.ts`): remove long-poll retry test; add SSE streaming test that reads - `response.body` as a `ReadableStream` and asserts a `data:` line with the expected payload. -* **Flutter** (`chat_history_api_service_test.dart`): remove `waitMs` assertion; add two SSE - tests using `_MockStreamedClient` (a `BaseClient` subclass) that controls a - `StreamController>` to emit raw SSE bytes. - -## Acceptance Criteria - -* `GET /api/chat/events/:sessionId` responds with `Content-Type: text/event-stream` and - pushes `data:` frames within ~1 s of new messages being inserted. -* `GET /api/chat/sync/:sessionId` continues to work without `waitMs` (no long-poll loop). -* Flutter app opens one SSE connection per active scope, updates the message list on each - incoming event, and reconnects automatically if the stream drops. -* All existing backend tests pass; new SSE test passes. -* All existing Flutter tests pass; two new SSE stream tests pass. -* `flutter analyze lib/features/chat/` reports no errors in modified files. - -## Validation Commands - -```sh -# Backend -cd apps/node_backend && npm test -- src/routes/chat.test.ts - -# Flutter -cd apps/mobile_chat_app && flutter test test/chat_history_api_service_test.dart -cd apps/mobile_chat_app && flutter analyze lib/features/chat/ -``` diff --git a/docs/plans/2026-04-21-03-23-UTC-openclaw-network-retry-hardening.md b/docs/plans/2026-04-21-03-23-UTC-openclaw-network-retry-hardening.md deleted file mode 100644 index c849a68c..00000000 --- a/docs/plans/2026-04-21-03-23-UTC-openclaw-network-retry-hardening.md +++ /dev/null @@ -1,31 +0,0 @@ -# OpenClaw network retry hardening - -## Problem - -The Bricks OpenClaw plugin runner already survives ordinary tick failures, but -DNS/connectivity failures such as `TypeError: fetch failed` with -`getaddrinfo ENOTFOUND ...` are not currently classified as retryable. That -means the runner keeps looping, but it logs an error every poll interval and -does not enter the same bounded backoff path used for retryable platform HTTP -failures. - -## Approach - -- Keep the runner alive on transient network failures as it already does today. -- Treat pre-response fetch/network failures as retryable runner failures. -- Reuse the existing bounded backoff ladder so DNS/connectivity outages recover - automatically once the network or DNS issue clears. -- Add regression coverage proving both the helper classification and the - `runUntilAbort()` loop behavior. - -## Validation - -- `cd apps/node_openclaw_plugin && npm test` -- `cd apps/node_openclaw_plugin && npm run type-check` -- `cd apps/node_openclaw_plugin && npm run build` - -## Notes - -- This hardening is intentionally narrow: it does not change message dispatch - semantics or state handling, only how the runner classifies and retries - transport-level failures before an HTTP response exists. diff --git a/docs/plans/2026-04-21-03-37-UTC-thread-channel-router-menu-adjustment.md b/docs/plans/2026-04-21-03-37-UTC-thread-channel-router-menu-adjustment.md deleted file mode 100644 index 069e5bea..00000000 --- a/docs/plans/2026-04-21-03-37-UTC-thread-channel-router-menu-adjustment.md +++ /dev/null @@ -1,20 +0,0 @@ -# Thread/Channel Router Menu Adjustment - -## Background -The chat input router menu currently renders both channel-level and thread-level option groups together, which can be confusing when the current conversation scope is channel-only or thread-scoped. - -## Goals -1. For thread conversations, keep the thread router choices focused and visible as exactly: Follow channel, Bricks Default, and OpenClaw. -2. For channel conversations, hide the thread router group entirely. -3. Preserve existing router selection behavior and snackbar feedback. - -## Implementation Plan (phased) -1. Inspect `chat_screen.dart` router-menu rendering to identify where channel and thread sections are always shown. -2. Update menu-building logic to conditionally include the thread section only when viewing a thread conversation. -3. Verify labels and menu values remain unchanged for thread context (Follow channel + concrete routers). -4. Run targeted Flutter tests for chat UI behavior. - -## Acceptance Criteria -1. In channel conversations, the router menu only shows channel router options and no thread router group. -2. In thread conversations, the thread router group appears with exactly three options: Follow channel, Bricks Default, and OpenClaw. -3. Existing compile/test checks continue to pass for the mobile chat app. diff --git a/docs/plans/2026-04-21-05-36-UTC-openclaw-plugin-refresh-skill.md b/docs/plans/2026-04-21-05-36-UTC-openclaw-plugin-refresh-skill.md deleted file mode 100644 index 15eef20c..00000000 --- a/docs/plans/2026-04-21-05-36-UTC-openclaw-plugin-refresh-skill.md +++ /dev/null @@ -1,36 +0,0 @@ -# OpenClaw plugin refresh skill - -## Problem - -This repository already has a repeatable workflow for applying updated local -`apps/node_openclaw_plugin` code to a real OpenClaw install, but there is no -dedicated `.codex/skills` entry that captures the repo-specific safe path, -fast path, config refresh path, and verification steps. - -## Approach - -- Add a new custom skill under `.codex/skills/openclaw-plugin-refresh/`. -- Document the repository-specific facts that matter for this workflow: - - plugin source location - - `dist/*` build requirement - - linked local install path - - gateway-managed runtime behavior - - Node version requirement -- Cover both the safe reinstall workflow and the fast path for existing linked - installs. -- Include the config refresh path and log-based verification steps. -- Surface the new skill from `AGENTS.md` so future agents are more likely to - discover and use it. - -## Validation - -- Review the new skill content against: - - `apps/node_openclaw_plugin/README.md` - - `apps/node_openclaw_plugin/package.json` -- Verify the new skill path appears under `.codex/skills/`. -- Verify `AGENTS.md` points future agents at the new skill. - -## Notes - -- This is a documentation/automation-surface change only; no application code - or runtime behavior should change. diff --git a/docs/plans/2026-04-21-06-51-UTC-chat-header-subsection-switcher-merge.md b/docs/plans/2026-04-21-06-51-UTC-chat-header-subsection-switcher-merge.md deleted file mode 100644 index 2c7f0259..00000000 --- a/docs/plans/2026-04-21-06-51-UTC-chat-header-subsection-switcher-merge.md +++ /dev/null @@ -1,33 +0,0 @@ -# Chat header subsection switcher merge plan - -## Background -- 当前聊天页面将子区切换器放在右上角 actions,标题区域独立显示频道名与 router 小字。 -- 需求要求把子区切换与标题合并:标题后可下拉切换子区,切换后标题区域即时更新。 -- 同时移除标题区域中的 router 第二行小字,并在右侧保留子区控制菜单(改名/存档暂不实现)。 - -## Goals -1. 将子区切换入口移动到左侧标题区域,与频道标题形成同一组件。 -2. 标题区域不再显示 router 信息小字。 -3. 在右上角提供子区控制下拉菜单,包含“分区改名(未实现)”“分区存档(未实现)”。 -4. 保持现有主区/子区切换与历史加载行为不回退。 - -## Implementation Plan (phased) -### Phase 1: Header structure refactor -- 重构 `AppBar.title`,将频道标题与子区下拉按钮组合为一行组件。 -- 下拉菜单保留“回到主区”“新建子区”与动态子区列表,用于直接切换。 -- 抽取切换逻辑为独立方法,避免与 actions 中逻辑重复。 - -### Phase 2: Right-side subsection controls -- 将原右上角切换菜单改为“子区管理”菜单。 -- 菜单包含“分区改名(未实现)”“分区存档(未实现)”,并以禁用态呈现。 - -### Phase 3: Cleanup and validation -- 删除未再使用的 router summary 文本逻辑。 -- 运行 Flutter 测试/检查验证聊天页构建与交互不报错。 -- 根据变更同步检查并更新代码地图文件。 - -## Acceptance Criteria -- 用户在标题右侧下拉可切换主区/子区;切换后标题中的子区文案变化正确。 -- 标题区域不再出现 `Router: ...` 小字。 -- 右上角存在子区控制菜单,显示“分区改名(未实现)”“分区存档(未实现)”。 -- 现有聊天页面测试在当前仓库环境可通过(如 `cd apps/mobile_chat_app && flutter test test/app_test.dart`)。 diff --git a/docs/plans/2026-04-21-07-57-UTC-readme-refresh-consolidated-plan.md b/docs/plans/2026-04-21-07-57-UTC-readme-refresh-consolidated-plan.md deleted file mode 100644 index 3cc42d96..00000000 --- a/docs/plans/2026-04-21-07-57-UTC-readme-refresh-consolidated-plan.md +++ /dev/null @@ -1,28 +0,0 @@ -# Background -The README refresh went through multiple iterations. Review feedback requested two final adjustments: (1) use a flowchart-style ASCII diagram instead of a tree layout, and (2) consolidate previously split README planning notes into a single plan document. - -# Goals -1. Keep README aligned with actual packages and current architecture language. -2. Present architecture as a flowchart-style ASCII diagram (not tree-style) above the Packages table. -3. Keep concise README/BUILD separation (README overview; BUILD.md detailed workflows). -4. Replace multiple README plan docs with one consolidated document. - -# Implementation Plan (phased) -## Phase 1: README diagram refinement -- Replace the existing tree-form ASCII with a flowchart-form ASCII block. -- Ensure the flow shows channel routing, channel partitions, plugin nodes, and multi-instance controller relationship. - -## Phase 2: Plan document consolidation -- Create one consolidated README plan document capturing prior goals and current feedback. -- No prior split plan documents were found for this specific README refresh task; cleanup of older split documents is therefore already complete and this step is done. - -## Phase 3: Verification -- Verify diagram placement above the Packages table and readable markdown rendering. -- Verify repository now contains a single consolidated README refresh plan document. -- Confirm code maps are unchanged because this remains a documentation-only update. - -# Acceptance Criteria -- README contains a flowchart-style ASCII diagram in English above the Packages table. -- Diagram expresses relationships among channels, partitions, and OpenClaw plugin nodes. -- Only one consolidated plan document remains for this README refresh task. -- No runtime behavior or code logic is changed. diff --git a/docs/plans/2026-04-21-10-20-UTC-respond-async-delivery-indicators.md b/docs/plans/2026-04-21-10-20-UTC-respond-async-delivery-indicators.md deleted file mode 100644 index 1f43d0b5..00000000 --- a/docs/plans/2026-04-21-10-20-UTC-respond-async-delivery-indicators.md +++ /dev/null @@ -1,39 +0,0 @@ -# Background -The chat flow currently treats default-router `/api/chat/respond` as a synchronous request/response path, and the Flutter UI inserts an optimistic empty assistant placeholder immediately after the user sends a message. Delivery/read indicators for user messages are also tied to older heuristics, which no longer match the latest product behavior for default-router and OpenClaw flows. - -# Goals -1. Make default-router `respond` follow the async transport contract: persist user message first, return quickly, and complete assistant generation/persistence asynchronously. -2. Remove immediate fake assistant placeholder rendering from the client; assistant content should appear only when actual reply content starts arriving from backend sync/SSE. -3. Update user-message delivery indicators to the latest rule: - - show a first check mark when the user query is persisted; - - add a second conversation-status mark when assistant processing/reply starts; - - when the responder is OpenClaw, the second mark must be 🦞 instead of ✓. -4. Ensure sync/SSE wiring still activates for default-router async replies after send. - -# Implementation Plan (phased) -## Phase 1: Backend respond async unification -- Refactor `apps/node_backend/src/routes/chat.ts` `/respond` so both default and OpenClaw return `mode: "async"` after accepted persistence. -- Persist the user message first. -- For default router, schedule background generation + assistant message persistence (accepted/completed/failed state transitions) without blocking HTTP response. -- Add/update route tests to validate default router no longer behaves synchronously. - -## Phase 2: Flutter send-flow and SSE behavior -- Update `apps/mobile_chat_app/lib/features/chat/chat_screen.dart` send flow to stop adding optimistic empty assistant messages. -- Keep user message visible, then rely on SSE/sync snapshots for assistant records. -- Ensure active-scope sync activation includes pending user tasks so default-router async replies are still fetched even without a local assistant placeholder. - -## Phase 3: Delivery indicator behavior -- Update `apps/mobile_chat_app/lib/features/chat/widgets/message_list.dart` delivery indicator model/UI to support two-stage icons. -- Implement first icon (persisted check) and second icon (reply started, with OpenClaw lobster variant). -- Update widget tests in `apps/mobile_chat_app/test/message_list_test.dart` accordingly. - -## Phase 4: Code map and validation -- Run targeted backend + Flutter tests covering changed behavior. -- Review and update `docs/code_maps/feature_map.yaml` and `docs/code_maps/logic_map.yaml` to reflect behavior changes. - -# Acceptance Criteria -- `POST /api/chat/respond` for default scope responds with async accepted payload without waiting for LLM text generation. -- User message record is persisted before async response is returned. -- Flutter chat does not render an empty/fake assistant placeholder on send. -- User message delivery UI shows two-stage state per latest rules (OpenClaw second icon uses lobster). -- Relevant automated tests pass for backend route behavior and Flutter delivery indicator rendering. diff --git a/docs/plans/2026-04-21-12-37-UTC-router-switch-toast-behavior.md b/docs/plans/2026-04-21-12-37-UTC-router-switch-toast-behavior.md deleted file mode 100644 index 27e371fe..00000000 --- a/docs/plans/2026-04-21-12-37-UTC-router-switch-toast-behavior.md +++ /dev/null @@ -1,19 +0,0 @@ -# Background -The chat composer route switcher (left-side button near the input area) currently shows a success toast whenever channel/thread router changes are saved. Product expectation is to keep the UX silent on success and only notify users when persistence fails. - -# Goals -- Remove success toasts for channel router updates. -- Remove success toasts for thread router updates. -- Preserve existing failure toasts so users are informed when the update fails. - -# Implementation Plan (phased) -1. Locate router save handlers in chat UI and identify success/failure toast branches. -2. Remove only the success snack bars from both channel and thread save flows. -3. Keep error handling and optimistic rollback logic unchanged. -4. Run focused Flutter checks to confirm no analyzer/test regressions. - -# Acceptance Criteria -- Changing channel router does not show a success toast. -- Changing thread router does not show a success toast. -- If saving channel or thread router fails, a failure toast still appears. -- Validation command(s): `./tools/init_dev_env.sh`, then `cd apps/mobile_chat_app && flutter test` (or a focused equivalent if full suite is too slow). diff --git a/docs/plans/2026-04-21-12-43-UTC-router-menu-checkmark-selection.md b/docs/plans/2026-04-21-12-43-UTC-router-menu-checkmark-selection.md deleted file mode 100644 index 8a061a0e..00000000 --- a/docs/plans/2026-04-21-12-43-UTC-router-menu-checkmark-selection.md +++ /dev/null @@ -1,20 +0,0 @@ -# Background -The router switch popup now has silent success behavior, but users still need clear visibility into which router option is currently selected. The menu header currently includes both partition type and selected value, which is redundant once selection is visually indicated. - -# Goals -- Show current selection using a checkmark in the popup menu options. -- Reserve left-side checkmark space for every selectable menu item. -- Simplify the menu header line to show only partition type. - -# Implementation Plan (phased) -1. Add reusable menu option rendering with fixed leading checkmark slot. -2. Update channel router menu items to render checkmarks based on current selection. -3. Update thread router menu items to render checkmarks based on explicit thread setting (including Follow channel). -4. Simplify non-selectable header row text for channel/thread sections. -5. Run focused Flutter tests for chat navigation UI regressions. - -# Acceptance Criteria -- In channel context, the active router option shows a checkmark and other options keep aligned left padding. -- In thread context, exactly one of Follow channel / Bricks Default / OpenClaw shows a checkmark. -- Header line displays only `Channel router` or `Thread router` without selected value text. -- Validation command(s): `./tools/init_dev_env.sh`, `cd apps/mobile_chat_app && flutter test test/chat_navigation_page_test.dart`. diff --git a/docs/plans/2026-04-21-14-45-UTC-chat-appbar-bg.md b/docs/plans/2026-04-21-14-45-UTC-chat-appbar-bg.md deleted file mode 100644 index 11e85818..00000000 --- a/docs/plans/2026-04-21-14-45-UTC-chat-appbar-bg.md +++ /dev/null @@ -1,16 +0,0 @@ -# Background -用户希望将对话页面顶部 AppBar 的背景色改为与内容区一致,消除顶部与主体区域的视觉色差。 - -# Goals -- 让聊天页 AppBar 背景与内容区(Scaffold 背景)保持一致。 -- 不改变聊天页现有交互行为与布局结构。 - -# Implementation Plan (phased) -1. 定位 `chat_screen.dart` 中聊天页 `AppBar` 的定义位置。 -2. 为 `AppBar` 显式设置与内容区一致的背景色(基于主题的 `scaffoldBackgroundColor`)。 -3. 运行格式化与最小化测试/检查,确认无回归。 - -# Acceptance Criteria -- 打开聊天页时,顶部 AppBar 背景色与内容区背景色一致。 -- 聊天页现有菜单、切换子区、发送消息等功能不受影响。 -- `flutter test`(在 `apps/mobile_chat_app` 目录下执行)通过。 diff --git a/docs/plans/2026-04-21-14-45-UTC-user-bubble-metadata-context-menu.md b/docs/plans/2026-04-21-14-45-UTC-user-bubble-metadata-context-menu.md deleted file mode 100644 index 8d38c5eb..00000000 --- a/docs/plans/2026-04-21-14-45-UTC-user-bubble-metadata-context-menu.md +++ /dev/null @@ -1,40 +0,0 @@ -# Background -The user wants to simplify the message row by removing the metadata line outside user bubbles while preserving all existing metadata information. They also want user-bubble metadata to use delivery checkmarks rather than `task:accepted` text, remove visible task/message IDs from the bubble body, and add a long-press context menu with copy/branch/resend actions plus subtle ID diagnostics in the menu footer. - -# Goals -1. Keep all user-message metadata but move it into the user bubble. -2. Remove visible `id:task-...` text from message rendering. -3. Replace textual accepted-state indication with the existing first ✓ delivery indicator. -4. Place the second delivery indicator immediately after the first inside bubble metadata. -5. Add long-press context menu on user bubbles with: - - Copy (implemented) - - Branch (placeholder) - - Resend (placeholder) - - Bottom two-line light metadata: message id and task id. -6. Keep/extend tests to cover the updated rendering and menu behavior. - -# Implementation Plan (phased) -## Phase 1: Bubble metadata layout -- Update `message_list.dart` to render user metadata row inside the user bubble. -- Remove the external metadata row for user messages. -- Keep assistant rows unchanged. - -## Phase 2: Metadata semantics cleanup -- Stop rendering `task:*` and `id:*` text in the user bubble body. -- Keep timestamp/thread/recovered text and delivery icons together inside bubble metadata. - -## Phase 3: Long-press context menu -- Add a long-press handler for user bubbles. -- Implement context menu actions and footer metadata lines. -- Implement copy action with clipboard support. - -## Phase 4: Validation and map synchronization -- Run mobile app tests for `message_list_test.dart`. -- Update code maps to include the new long-press/context-menu behavior in chat session smoke checks/keywords. - -# Acceptance Criteria -- User message rows no longer show metadata text outside the bubble. -- User bubble contains timestamp/thread metadata and delivery checks. -- `task:accepted` / `id:task-*` are not shown in user bubble text. -- Long-pressing a user bubble shows a menu with Copy/Branch/Resend and footer metadata lines for message/task IDs. -- `flutter test` for message list passes from `apps/mobile_chat_app`. diff --git a/docs/plans/2026-04-22-03-03-UTC-agent-bot-sidebar-blueprint.md b/docs/plans/2026-04-22-03-03-UTC-agent-bot-sidebar-blueprint.md deleted file mode 100644 index f5bbd348..00000000 --- a/docs/plans/2026-04-22-03-03-UTC-agent-bot-sidebar-blueprint.md +++ /dev/null @@ -1,48 +0,0 @@ -# Background -目前移动端聊天侧边栏已有 `Agents` 分组,但其内容仅来自本地 Agent 文件仓库(`AgentsRepository`)读取结果;在未创建任何 Agent 时会显示空状态文案。现有 Agent 定义已包含 `name`、`description`、`model`、`systemPrompt`,与“BOT = system prompt + 模型选项(可选)”方向高度一致,但命名与产品语义仍需统一。 - -# Goals -1. 明确现状:梳理 Agent 在域模型、存储、运行时、UI 侧边栏的现有设计与数据流。 -2. 给出 BOT 概念映射:将“BOT 内核概念”映射为用户可见 `Agent`,并保证兼容既有实现。 -3. 提出可落地改造分期:让侧边栏 `Agents` 分组内置并稳定展示可用 BOT(如文档、问答、问卷、儿童 workbook)。 - -# Implementation Plan (phased) -## Phase 1 - 概念与命名统一(不改协议) -- 在产品与文档层统一术语: - - **用户层名称**:Agent(侧边栏继续显示 Agent) - - **内部设计名称**:BOT(prompt-profile + model-profile 的可运行单元) -- 定义字段映射: - - `bot_id` -> 现有 `AgentDefinition.name` - - `display_name` -> 新增展示名(可选,未配置时回落为 `name`) - - `system_prompt` -> 现有 `systemPrompt` - - `model_profile` -> 现有 `model`(后续可扩展 provider、temperature 等可选项) - -## Phase 2 - 内建 BOT 注册表 -- 新增“内建 BOT 清单”模块(建议在 chat_domain 或 mobile_chat_app feature 层): - - 至少包含:写文档、轻松问答、问卷、儿童 workbook。 -- 启动时合并来源: - - 内建 BOT(只读) - - 用户自建 Agent/BOT(可编辑) -- 侧边栏展示按分组或标记区分(Built-in / Custom)。 - -## Phase 3 - 侧边栏与入口联动 -- `ChatNavigationPage` 的 Agents 分组改为显示“合并后的 Agent 列表”。 -- 空状态逻辑调整:当用户自建为空时仍显示内建 BOT,不再出现“分组空”的主路径体验。 -- “配置”入口从未实现状态接入可用页面: - - 内建 BOT:仅允许复制为自定义版本(避免直接破坏默认模板) - - 自建 BOT:可编辑 system prompt 与模型选项。 - -## Phase 4 - 运行时选择与审计 -- 发送消息时把当前选中 Agent/BOT 的 `bot_id`、`system_prompt_version`、`resolved_model`记录到消息元数据,便于回放与排障。 -- 与现有 `ChatBotRegistry`(ask/image_generation)关系: - - 短期并存:保留 skill 级路由;新增 BOT 层用于 persona/prompt 封装。 - - 中期收敛:把默认 ask 也迁移为内建 BOT 的一个实例。 - -# Acceptance Criteria -1. 新安装或未创建自定义 Agent 的用户,打开侧边栏 `Agents` 分组时可见内建 Agent 列表(至少 4 个)。 -2. 用户可明确区分“内建 Agent”和“自定义 Agent”。 -3. 选择任一 Agent 发送消息后,运行时使用该 Agent 的 system prompt;模型选项未指定时按默认策略回退。 -4. 保持向后兼容:旧 `.md` Agent 文件仍可加载并显示。 -5. 验证命令(开发阶段): - - `./tools/init_dev_env.sh` - - `cd apps/mobile_chat_app && flutter test` diff --git a/docs/plans/2026-04-22-03-04-UTC-remove-readme-packages-section.md b/docs/plans/2026-04-22-03-04-UTC-remove-readme-packages-section.md deleted file mode 100644 index 184bdc1d..00000000 --- a/docs/plans/2026-04-22-03-04-UTC-remove-readme-packages-section.md +++ /dev/null @@ -1,23 +0,0 @@ -# Background -The repository README currently includes a "Packages" section listing package directories and descriptions. The user requested removing the packages section from the README. - -# Goals -- Remove the entire "Packages" section from README content. -- Keep the rest of the README intact and readable. - -# Implementation Plan (phased) -## Phase 1: Inspect current README structure -- Locate the "Packages" heading and the associated table content. - -## Phase 2: Apply documentation update -- Delete the "Packages" section block from the README. -- Verify surrounding sections still flow naturally. - -## Phase 3: Validate and finalize -- Run a quick diff/stat check to confirm only intended docs changes were made. -- Commit the change with a clear message. - -# Acceptance Criteria -- The README no longer contains a "Packages" section heading. -- The package listing table is fully removed. -- Only the expected documentation and plan files are changed for this task. diff --git a/docs/plans/2026-04-22-03-44-UTC-router-linked-composer-actions.md b/docs/plans/2026-04-22-03-44-UTC-router-linked-composer-actions.md deleted file mode 100644 index 57cb1f4a..00000000 --- a/docs/plans/2026-04-22-03-44-UTC-router-linked-composer-actions.md +++ /dev/null @@ -1,34 +0,0 @@ -# Background -The chat composer currently shows a route button and a static configuration button with fixed menu items. Product requirements now require route-aware action buttons: default route should expose the config button with current model visibility, generic non-default routes should hide config by default, and the OpenClaw route should expose dedicated slash-command tooling. - -# Goals -1. Make composer actions dynamically depend on the effective router selection. -2. Update the configuration menu to show the currently effective model under the “模型” item. -3. Remove “Agents” and “新上下文” entries from the configuration menu. -4. Add an OpenClaw slash-command button that inserts selected slash commands into the input box. -5. Keep route-selection behavior intact and covered by widget tests. - -# Implementation Plan (phased) -1. **Composer widget API refactor** - - Replace the single optional `routerAction` slot with a flexible `leadingActions` list. - - Add optional model display text and slash command list support. - - Update menu enum/actions to remove deprecated entries. -2. **Chat screen wiring** - - Build route-dependent leading actions in `ChatScreen` based on `_effectiveRouterForScope()`. - - Preserve existing route picker button. - - For `default` route, render config button only. - - For `openclaw`, render slash command button plus config button. - - For unknown future routes, render no config button by default. -3. **OpenClaw slash command presets** - - Add a curated initial slash command list based on OpenClaw docs and bind selection to composer input insertion. -4. **Tests and map updates** - - Update `composer_bar_test.dart` for API and behavior changes. - - Run Flutter tests from package directory after environment bootstrap. - - Evaluate code map impact and update maps if feature/logic index changed. - -# Acceptance Criteria -- Selecting default route shows the config button with menu item “模型” plus subtitle of the currently selected model. -- Config menu no longer includes “Agents” or “新上下文”. -- Selecting OpenClaw route shows a slash button; picking a slash command inserts command text into the composer input. -- For non-default non-openclaw routes, config button is hidden by default. -- `cd apps/mobile_chat_app && flutter test test/composer_bar_test.dart` passes after `./tools/init_dev_env.sh`. diff --git a/docs/plans/2026-04-22-03-53-UTC-streaming-output-120k-merged-plan.md b/docs/plans/2026-04-22-03-53-UTC-streaming-output-120k-merged-plan.md deleted file mode 100644 index 214bea90..00000000 --- a/docs/plans/2026-04-22-03-53-UTC-streaming-output-120k-merged-plan.md +++ /dev/null @@ -1,36 +0,0 @@ -# Streaming Output 120K Boundary + User-side Incremental Delivery (Merged Plan) - -## Background -The previous plan audited route-level streaming capability and added conservative output limits. Based on follow-up requirements, we now need to merge that planning context with a concrete implementation plan that: -1) raises output boundaries to 120K as the new default budget, and -2) achieves true user-visible incremental output for the default chat router, while preserving the existing `/respond` acceptance flow and SSE architecture. - -This merged plan supersedes and consolidates prior route-audit planning for the same objective. - -## Goals -1. Set output budget defaults and upper bounds to **120K** for model output controls. -2. Keep `/api/chat/respond` request/ack contract unchanged. -3. Keep existing SSE transport shape (`/api/chat/events/:sessionId`) unchanged. -4. Refactor backend default-router generation to stream from the model and persist incremental assistant content so users can see progressive updates via existing SSE polling. -5. Align route docs with the final behavior and boundaries. - -## Implementation Plan (phased) -1. **Boundary normalization to 120K** - - Update LLM route `maxTokens` defaults and upper-bound validation to 120K. - - Update platform message text cap to 120K characters. -2. **Default router true incremental generation** - - Replace single-shot model generation in chat default async pipeline with streaming model generation. - - Persist assistant message progressively (same messageId, growing content, `dispatched` state during stream). - - Finalize the same message with `completed` state after stream ends; preserve failure write-path semantics. -3. **Test adaptation** - - Update route tests for the new 120K thresholds. - - Add assertions that default-router async flow now emits intermediate `dispatched` writes before completion. -4. **Documentation merge** - - Refresh audit/report docs so boundaries and streaming behavior match implementation. - -## Acceptance Criteria -- `POST /api/llm/chat` and `POST /api/llm/chat/stream` enforce 120K default and 120K upper-bound for `maxTokens`. -- `POST /api/chat/respond` validates and propagates 120K-bounded `maxTokens` into default async generation. -- Default router uses model streaming and writes incremental assistant content updates observable through existing `/api/chat/events/:sessionId` flow. -- `POST /api/v1/platform/messages` and `PATCH /api/v1/platform/messages/:messageId` reject payload text beyond 120K. -- Updated route tests pass for changed behavior and thresholds. diff --git a/docs/plans/2026-04-22-03-55-UTC-agent-bot-sidebar-implementation.md b/docs/plans/2026-04-22-03-55-UTC-agent-bot-sidebar-implementation.md deleted file mode 100644 index c2e922eb..00000000 --- a/docs/plans/2026-04-22-03-55-UTC-agent-bot-sidebar-implementation.md +++ /dev/null @@ -1,36 +0,0 @@ -# Background -根据已完成的 Agent/BOT 方案文档,当前需要把“侧边栏 Agent 分组默认可见内建 BOT、并支持配置入口”落地到代码,而不是仅停留在计划层。 - -# Goals -1. 在未创建自定义 Agent 时,侧边栏仍展示内建 Agent(BOT)。 -2. 将“配置”按钮从占位提示改为可执行入口。 -3. 在侧边栏区分内建与自定义 Agent。 - -# Implementation Plan (phased) -## Phase 1 - 内建 BOT 注册 -- 新增 `chat_builtin_agents.dart`,提供 4 个内建 Agent 模板: - - doc-writer - - easy-qa - - survey-designer - - kids-workbook - -## Phase 2 - 合并加载与运行时接入 -- `ChatScreen` 在读取自定义 Agent 后,与内建 Agent 合并。 -- 保持自定义 Agent 优先(同名覆盖内建)。 -- 继续复用现有 `_settingsForAgent`,使 system prompt / model 流程保持兼容。 - -## Phase 3 - 侧边栏展示与配置入口 -- 扩展 `ChatAgentItem` 支持 `description` 与 `isBuiltIn`。 -- 侧边栏 Agent 列表展示来源标签(内建/自定义),内建显示只读图标。 -- 将“配置”按钮行为改为触发 `manageAgents` 动作,接入 `AgentsScreen`。 - -## Phase 4 - 测试与代码地图 -- 更新 `chat_navigation_page_test.dart` 覆盖新交互。 -- 新增 `chat_builtin_agents_test.dart` 覆盖内建模板存在性。 -- 同步更新 `docs/code_maps/feature_map.yaml` 与 `docs/code_maps/logic_map.yaml`。 - -# Acceptance Criteria -1. 侧边栏 Agents 分组在默认安装场景下不为空。 -2. 点击“配置”可进入 Agent 管理流程。 -3. 侧边栏可看到内建/自定义区分信息。 -4. 相关 Flutter 测试通过。 diff --git a/docs/plans/2026-04-22-07-20-UTC-chat-agent-ui-adjustments.md b/docs/plans/2026-04-22-07-20-UTC-chat-agent-ui-adjustments.md deleted file mode 100644 index 722c4ea2..00000000 --- a/docs/plans/2026-04-22-07-20-UTC-chat-agent-ui-adjustments.md +++ /dev/null @@ -1,38 +0,0 @@ -# Chat Agent UI Adjustments Plan - -## Background -The chat UI currently supports `@agent` mention insertion inside the composer text field and shows an Agents section in the sidebar with a group-level configuration button. The requested behavior is to remove composer `@` mention mechanics, associate `@` visibility with router selection in the action row, and update sidebar agent interactions to focus on per-agent prompt preview plus explicit actions. - -## Goals -1. Remove `@` mention parsing/insertion and `@` hint text from the composer input area. -2. Show an `@` marker next to the router action only when the effective route is the default route. -3. Remove the sidebar Agents group-level config button. -4. Make each sidebar agent row open a prompt preview page with two actions: - - 修改配置 (navigates to existing manage-agents configuration page) - - 发起对话 (selects this agent for the current conversation) -5. Keep current chat/channel behavior intact. - -## Implementation Plan (phased) -### Phase 1: Composer behavior and layout -- Refactor `ComposerBar` to remove mention state, filtering, insertion logic, and mention popup rendering. -- Adjust input hint and icon behavior to avoid embedding `@` in text field UX. -- Add a new boolean prop controlling whether an `@` marker is rendered between router and config buttons. - -### Phase 2: Sidebar agent interaction flow -- Extend sidebar agent item model to carry prompt content. -- Remove the Agents header config button. -- Add per-agent tap behavior to open a prompt detail screen. -- Implement prompt detail screen with two bottom actions: 修改配置 and 发起对话. - -### Phase 3: Chat screen wiring and regression checks -- Wire `showRouteAtMarker` to router effective state in `ChatScreen`. -- Wire sidebar callbacks for opening configuration and selecting active agent from prompt detail action. -- Run targeted Dart/Flutter analysis/tests for touched chat widgets. - -## Acceptance Criteria -- Composer input no longer auto-detects `@` mentions and no mention dropdown appears while typing. -- Composer hint text contains no `@` symbol. -- When effective router is default, an `@` marker appears to the right of the router button and left of the config button; it is hidden on non-default router. -- Sidebar Agents section has no header-level config button. -- Tapping an agent in sidebar opens a prompt page that displays prompt text and includes buttons: 修改配置 and 发起对话. -- 修改配置 opens the existing agent configuration flow; 发起对话 sets the active chat agent. diff --git a/docs/plans/2026-04-22-07-55-UTC-remove-top-active-agents-indicator.md b/docs/plans/2026-04-22-07-55-UTC-remove-top-active-agents-indicator.md deleted file mode 100644 index bd52a1d7..00000000 --- a/docs/plans/2026-04-22-07-55-UTC-remove-top-active-agents-indicator.md +++ /dev/null @@ -1,23 +0,0 @@ -# Remove Top Active Agents Indicator Plan - -## Background -After the previous UI adjustment, the chat page still renders a top horizontal participant indicator (`_buildActiveAgentsIndicator`) under the AppBar. Product feedback requests removing this list entirely. - -## Goals -1. Remove the top participant indicator from the chat page UI. -2. Keep chat routing, sending, and sidebar agent interactions unchanged. -3. Ensure code and tests remain healthy after cleanup. - -## Implementation Plan (phased) -### Phase 1: UI cleanup -- Remove AppBar `bottom` usage that mounts `_buildActiveAgentsIndicator`. -- Delete `_buildActiveAgentsIndicator` helper if no longer referenced. - -### Phase 2: Validation -- Run Flutter analyze for `chat_screen.dart`. -- Run focused widget tests to ensure no regressions in chat navigation/composer surfaces. - -## Acceptance Criteria -- Chat page no longer displays the top horizontal active-agent indicator bar. -- AppBar layout remains stable and chat page renders without runtime/layout errors. -- Analysis/tests for touched chat files pass. diff --git a/docs/plans/2026-04-22-08-08-UTC-openclaw-plugin-publish-plan.md b/docs/plans/2026-04-22-08-08-UTC-openclaw-plugin-publish-plan.md deleted file mode 100644 index af6fb50e..00000000 --- a/docs/plans/2026-04-22-08-08-UTC-openclaw-plugin-publish-plan.md +++ /dev/null @@ -1,93 +0,0 @@ -# OpenClaw plugin publish plan - -## Background - -The Bricks OpenClaw plugin currently works as an in-repo local plugin under -`apps/node_openclaw_plugin`, but the goal is to publish it for external -OpenClaw users. - -OpenClaw community plugin discovery works through: - -- `openclaw plugins install ` with **ClawHub first, then npm** -- explicit ClawHub installs via `openclaw plugins install clawhub:` -- local path / linked installs for development only - -ClawHub is the canonical community discovery surface for OpenClaw plugins, but -npm is also a supported install source and automatic fallback. - -## Goals - -- Make the Bricks plugin installable by outside OpenClaw users with a single - command. -- Decide the publish path: npm only vs npm + ClawHub. -- Close the packaging gaps between the current repo-local plugin package and a - real public plugin release. -- Validate the published artifact from a fresh OpenClaw install flow. - -## Implementation Plan - -### Phase 1: Decide the public distribution target - -- Prefer **npm + ClawHub**: - - npm provides a standard public package artifact - - ClawHub improves discoverability and is the canonical community listing -- Pick the final public package name and plugin branding. -- Confirm whether the public plugin id stays `dev-askman-bricks` or should be - renamed before first public release. - -### Phase 2: Make the package publishable - -- Remove `private: true` from `apps/node_openclaw_plugin/package.json`. -- Add public release metadata if missing: - - `license` - - `repository` - - `homepage` - - `bugs` - - optional `keywords` -- Ensure the npm tarball includes built runtime files and manifest files. -- Add/verify `openclaw` package metadata required for packaged plugin - distribution, especially: - - built runtime entrypoints - - compatibility metadata for ClawHub / packaged plugin validation -- Review whether repo-local-only install hints such as `openclaw.install.localPath` - should remain, be adjusted, or be removed for the public package. - -### Phase 3: Make the artifact release-safe - -- Ensure `npm run build` produces the files referenced by package metadata. -- Run plugin validation: - - `cd apps/node_openclaw_plugin && npm test` - - `cd apps/node_openclaw_plugin && npm run type-check` - - `cd apps/node_openclaw_plugin && npm run build` -- Run a local package dry run: - - `npm pack` -- Inspect the tarball contents to confirm `dist/*`, `openclaw.plugin.json`, and - package metadata are present. - -### Phase 4: Publish - -- Publish to npm first: - - `npm publish --access public` -- Publish to ClawHub after npm or directly from source using: - - `clawhub package publish ` - - use `--dry-run` first -- Keep the GitHub repo public and link it from the package metadata/docs. - -### Phase 5: Verify the real external-user flow - -- In a clean OpenClaw profile or clean machine, test: - - `openclaw plugins install ` - - `openclaw plugins inspect ` - - `openclaw plugins enable ` if needed - - `openclaw configure` / channel onboarding for the Bricks config -- Confirm gateway startup and plugin load behavior from logs. -- Confirm the plugin can be updated later via: - - `openclaw plugins update ` - -## Acceptance Criteria - -- External users can install the plugin without cloning this repository. -- `openclaw plugins install ` works from a clean environment. -- The published artifact contains built runtime files and valid OpenClaw plugin - metadata. -- The plugin is discoverable at least on npm, and ideally also on ClawHub. diff --git a/docs/plans/2026-04-22-08-22-UTC-openclaw-plugin-publish-prep-skill.md b/docs/plans/2026-04-22-08-22-UTC-openclaw-plugin-publish-prep-skill.md deleted file mode 100644 index 296bc11f..00000000 --- a/docs/plans/2026-04-22-08-22-UTC-openclaw-plugin-publish-prep-skill.md +++ /dev/null @@ -1,46 +0,0 @@ -# OpenClaw plugin publish prep skill - -## Background - -The user wants a reusable repository-local skill that future Codex runs can use -to prepare `apps/node_openclaw_plugin` for public release. The immediate output -should not be the code changes themselves, but a stable skill that captures the -locked product decisions, the release-prep scope, a PRD-style prompt, and the -manual publish steps that a human operator must execute later. - -## Goals - -- Add a `.codex/skills` entry for OpenClaw plugin public-release preparation. -- Capture the locked product decisions: - - distribution is npm + ClawHub - - plugin id remains `dev-askman-bricks` - - final publish is manual -- Include a PRD-style prompt that can be pasted into Codex. -- Include explicit manual publish and verification commands so the operator does - not forget them. -- Surface the new skill from `AGENTS.md`. - -## Implementation Plan - -### Phase 1: Add the skill - -- Create `.codex/skills/openclaw-plugin-publish-prep/SKILL.md`. -- Document when to use it, what decisions are locked, and what work is in scope. -- Include the PRD-style prompt inside the skill. - -### Phase 2: Surface it in repository guidance - -- Update `AGENTS.md` so future agents know to use the new skill first when the - task is preparing `apps/node_openclaw_plugin` for external release. - -### Phase 3: Keep the human handoff explicit - -- Write the manual npm publish, ClawHub publish, and clean-install verification - steps directly into the skill. - -## Acceptance Criteria - -- A new skill exists at `.codex/skills/openclaw-plugin-publish-prep/SKILL.md`. -- The skill includes a concrete PRD-style prompt. -- The skill explicitly states that final publish remains a manual human step. -- `AGENTS.md` points future agents to the new skill. diff --git a/docs/plans/2026-04-22-08-41-UTC-rate-limit-global-optimization.md b/docs/plans/2026-04-22-08-41-UTC-rate-limit-global-optimization.md deleted file mode 100644 index ff612732..00000000 --- a/docs/plans/2026-04-22-08-41-UTC-rate-limit-global-optimization.md +++ /dev/null @@ -1,47 +0,0 @@ -# Background - -The backend currently applies a coarse global limiter to all `/api/*` routes (`100 requests / 15 minutes / IP`). This can over-throttle authenticated traffic such as `GET /api/config?category=llm`, especially when upstream gateway rate limiting already exists. Chat and platform routes also maintain route-specific limiters, creating mixed and hard-to-reason behavior. - -# Goals - -1. Remove the global `/api/*` limiter. -2. Keep `GET /api/config` without app-layer limiting (rely on gateway). -3. Add config write limiter: `60 requests / minute / user`. -4. Add anonymous auth limiter: `20~30 requests / minute / IP` (implement as `30/min/IP` initial policy). -5. Unify chat and platform route-level limits to `120 requests / minute` to simplify policy while protecting DB from high-frequency bursts. - -# Implementation Plan (phased) - -## Phase 1: App-level routing and limiter wiring -- Remove global limiter middleware from `apps/node_backend/src/app.ts`. -- Add dedicated auth limiter middleware mounted on auth routes only. -- Keep existing routing structure (`/api/auth` and `/api` mounts for auth router). - -## Phase 2: Config route limiter -- In `apps/node_backend/src/routes/config.ts`, introduce a write limiter applied only to `POST/PUT/PATCH/DELETE` handlers. -- Key by authenticated `userId` (fallback to IP if absent for safety). -- Leave `GET /api/config` unthrottled at app layer. - -## Phase 3: Chat + platform policy unification -- Update chat constants so sync/respond/events all use `120 requests/minute`. -- Update platform defaults so read/write/events-stream all use `120 requests/minute`. -- Keep keying strategies unchanged (chat: user+session, platform: plugin+user/IP). - -## Phase 4: Test updates and verification -- Update tests that assumed global limiter behavior. -- Add/adjust tests to validate: - - config GET not blocked by app limiter. - - config writes are limited per-user. - - auth endpoints are rate limited by IP. - - chat/platform still enforce route-level 429 behavior with unified policy defaults. - -## Phase 5: Code map sync -- Review and update `docs/code_maps/feature_map.yaml` and `docs/code_maps/logic_map.yaml` for policy/index changes related to backend auth and backend chat/platform behavior. - -# Acceptance Criteria - -1. Repeated requests to `/api/config?category=llm` no longer receive the prior global-IP 429 from app-layer limiter under normal authenticated usage. -2. `POST/PUT/DELETE /api/config*` are limited to `60/min/user`. -3. Auth anonymous endpoints are limited to `30/min/IP`. -4. Chat and platform default route limits are each `120/min` across their route-specific limiters. -5. Node backend unit tests pass for updated limiter behavior. diff --git a/docs/plans/2026-04-22-09-22-UTC-openclaw-plugin-public-release-final.md b/docs/plans/2026-04-22-09-22-UTC-openclaw-plugin-public-release-final.md deleted file mode 100644 index 5dbb4559..00000000 --- a/docs/plans/2026-04-22-09-22-UTC-openclaw-plugin-public-release-final.md +++ /dev/null @@ -1,92 +0,0 @@ -# OpenClaw plugin public release final plan - -## Background - -`apps/node_openclaw_plugin` is being prepared for external distribution to -OpenClaw users. Product decisions are finalized: - -- Distribution target: npm + ClawHub -- Canonical OpenClaw plugin id remains: `dev-askman-bricks` -- npm package name for public install: `@askman-dev/bricks-openclaw-plugin` -- Final registry publication is a manual operator step (no auto publish) - -The plugin should be installable through standard OpenClaw package flow without -requiring repository cloning. - -## Goals - -1. Keep package metadata in a publishable state for npm + ClawHub. -2. Keep OpenClaw runtime metadata aligned to built `dist/*` entrypoints for - packaged installs. -3. Keep release-facing documentation aligned to the final public package name - and external install/config/update flow. -4. Keep release validation commands and manual publish handoff explicit. - -## Implementation Plan (phased) - -### Phase 1: Public package metadata and identity - -- Ensure `apps/node_openclaw_plugin/package.json` stays public publishable: - - package name `@askman-dev/bricks-openclaw-plugin` - - no `private: true` - - release metadata (`license`, `repository`, `homepage`, `bugs`, `keywords`) - - packaging controls (`files`, `exports`) -- Keep `apps/node_openclaw_plugin/package-lock.json` root package identity - consistent with `package.json`. - -### Phase 2: OpenClaw packaged-install metadata - -- Preserve plugin/channel identity as `dev-askman-bricks`. -- Keep OpenClaw metadata pointing to built runtime outputs (`dist/*`). -- Keep packaged install preference (`openclaw.install.defaultChoice=package`). -- Preserve native plugin manifest inclusion (`openclaw.plugin.json`) in package - artifact. - -### Phase 3: External-user documentation - -- Keep `apps/node_openclaw_plugin/README.md` aligned to public package flow: - - install: `openclaw plugins install @askman-dev/bricks-openclaw-plugin` - - onboarding/configure steps - - update flow - - troubleshooting notes - - clear separation from local dev-only install workflow - -### Phase 4: Validation and artifact inspection - -Run and record: - -- `cd apps/node_openclaw_plugin && npm test` -- `cd apps/node_openclaw_plugin && npm run type-check` -- `cd apps/node_openclaw_plugin && npm run build` -- `cd apps/node_openclaw_plugin && npm pack` -- inspect tarball contents and confirm: - - `dist/*` - - `openclaw.plugin.json` - - `package.json` - - `README.md` - -### Phase 5: Manual operator handoff (explicit) - -Do not auto-publish from agent flow. Human operator runs: - -- npm publish: - - `cd apps/node_openclaw_plugin && npm publish --access public` -- ClawHub publish: - - `clawhub package publish apps/node_openclaw_plugin --dry-run` - - `clawhub package publish apps/node_openclaw_plugin` -- clean-install verification: - - `openclaw plugins install @askman-dev/bricks-openclaw-plugin` - - `openclaw plugins inspect dev-askman-bricks` - - `openclaw configure` - - `openclaw gateway restart` - - `openclaw plugins update dev-askman-bricks` - -## Acceptance Criteria - -- Public package identity is finalized as - `@askman-dev/bricks-openclaw-plugin`. -- Plugin id remains `dev-askman-bricks`. -- Packaged artifact includes built runtime files and plugin manifest. -- README/install commands match the final public package name. -- Manual npm + ClawHub publication and clean-install verification commands are - explicitly documented. diff --git a/docs/plans/2026-04-22-09-24-UTC-chat-web-build-fix.md b/docs/plans/2026-04-22-09-24-UTC-chat-web-build-fix.md deleted file mode 100644 index e6972d08..00000000 --- a/docs/plans/2026-04-22-09-24-UTC-chat-web-build-fix.md +++ /dev/null @@ -1,23 +0,0 @@ -# Chat web build fix plan - -## Background -The mobile chat app web build currently fails during dart2js compilation with errors in chat navigation and chat screen code. Reported failures include a missing required `prompt` parameter when constructing `ChatAgentItem`, a list type mismatch for mapped agent items, and missing `_openClawSlashCommands` / `_currentComposerModelLabel` members in `_ChatScreenState`. - -## Goals -- Restore a successful web compile for `apps/mobile_chat_app`. -- Ensure chat navigation agent construction matches current model signatures. -- Ensure chat screen references only defined state fields/methods. -- Keep behavior coherent for OpenClaw and non-OpenClaw chat contexts. - -## Implementation Plan (phased) -1. Inspect the relevant Dart files and model definitions to identify signature and type expectations. -2. Patch `chat_navigation_page.dart` to provide required fields and resolve list typing. -3. Patch `chat_screen.dart` to replace/restore missing members for slash commands and composer model labels. -4. Run bootstrap (`./tools/init_dev_env.sh`) and then targeted verification commands from the correct package directory. -5. If functionality changed, evaluate and update code maps; otherwise document why updates are unnecessary. - -## Acceptance Criteria -- `flutter` analysis/build checks for `apps/mobile_chat_app` no longer report the listed compile errors. -- `ChatAgentItem` construction includes required named parameters expected by the current type. -- `_ChatScreenState` has valid references for slash command source and composer model label. -- Any code map update decision is documented in the final report. diff --git a/docs/plans/2026-04-22-09-49-UTC-pr-chat-ui-check.md b/docs/plans/2026-04-22-09-49-UTC-pr-chat-ui-check.md deleted file mode 100644 index b2f9969c..00000000 --- a/docs/plans/2026-04-22-09-49-UTC-pr-chat-ui-check.md +++ /dev/null @@ -1,31 +0,0 @@ -# Background -用户怀疑某个 PR 导致聊天界面相关代码丢失,需要核对并修复两处交互: -1) 对话界面顶部不应展示参与者列表。 -2) 输入框内/前不应出现 @;@ 只与路由选择关联,当选中默认路由时,@ 显示在路由按钮右侧(配置按钮左侧)。 - -# Goals -- 确认当前实现是否满足上述两条要求。 -- 若不满足,完成最小改动修复并补充/更新测试。 -- 运行相关检查,确保改动可验证。 - -# Implementation Plan (phased) -## Phase 1: 代码定位与现状核对 -- 定位聊天页面顶部参与者列表渲染逻辑。 -- 定位输入框与路由选择区域中 @ 相关渲染逻辑。 -- 记录与需求不一致点。 - -## Phase 2: 实现修复 -- 移除/隐藏顶部参与者列表显示。 -- 调整 @ 的展示位置与触发条件: - - 输入框内/前不展示 @。 - - 默认路由选中时,在路由按钮右侧展示 @(且位于配置按钮左侧)。 - -## Phase 3: 验证与收尾 -- 运行格式化/分析/测试(至少覆盖受影响模块)。 -- 检查是否需要更新代码地图(docs/code_maps/*),并据实更新或在最终说明给出理由。 - -# Acceptance Criteria -- 聊天界面顶部不再显示参与者列表。 -- 输入框内及其左侧前缀区域不再出现 @。 -- 仅当路由选择为默认路由时,@ 显示在路由按钮右侧、配置按钮左侧。 -- 相关测试通过,或明确说明受环境限制的检查项。 diff --git a/docs/plans/2026-04-22-10-19-UTC-openclaw-route-actions-and-sidebar-groups.md b/docs/plans/2026-04-22-10-19-UTC-openclaw-route-actions-and-sidebar-groups.md deleted file mode 100644 index cac16b70..00000000 --- a/docs/plans/2026-04-22-10-19-UTC-openclaw-route-actions-and-sidebar-groups.md +++ /dev/null @@ -1,32 +0,0 @@ -# OpenClaw route actions and sidebar groups plan - -## Background -The chat composer and navigation drawer currently do not fully satisfy route-aware interaction requirements. In particular, the `@` action is non-interactive in default route mode, OpenClaw route-specific command presets are not populated, and the sidebar lacks a dedicated Skills group above Agents. - -## Goals -1. Make route-linked composer actions behaviorally correct for `default` and `openclaw` routes. -2. Ensure OpenClaw slash command presets are available from official OpenClaw docs and selectable in UI. -3. Add a Skills section above Agents in the sidebar (empty state for now). -4. Cover the updated behavior with widget tests. -5. Keep code maps aligned with changed user-facing behavior. - -## Implementation Plan (phased) -1. **Composer route actions** - - Add explicit at-menu support in `ComposerBar` with route-specific menu content. - - Wire default route `@` menu to existing agents list. - - Wire OpenClaw `@` menu to a placeholder empty menu item (`待实现`). -2. **OpenClaw slash commands** - - Populate a curated static slash command list in `chat_screen.dart` based on OpenClaw official slash commands documentation. - - Keep current insert-on-select behavior in composer unchanged. -3. **Sidebar grouping** - - Add a Skills section above Agents in `ChatNavigationPage` with an empty-state row. -4. **Validation & docs sync** - - Add/adjust widget tests for composer and navigation groups. - - Update `docs/code_maps/feature_map.yaml` and `docs/code_maps/logic_map.yaml` to reflect new visible behavior and risks. - -## Acceptance Criteria -- In `openclaw` route, composer shows both `/` slash command action and clickable `@` action; clicking `@` opens a dropdown with `待实现` placeholder. -- In `default` route, composer shows clickable `@` action and dropdown items match available agents used by sidebar Agents section. -- Sidebar shows a Skills section above Agents with empty content state. -- Widget tests pass for the new composer and sidebar behaviors. -- Code maps are updated to reflect the new route/action behavior. diff --git a/docs/plans/2026-04-22-19-08-UTC-ios-build-enablement-implementation.md b/docs/plans/2026-04-22-19-08-UTC-ios-build-enablement-implementation.md deleted file mode 100644 index d1c43e73..00000000 --- a/docs/plans/2026-04-22-19-08-UTC-ios-build-enablement-implementation.md +++ /dev/null @@ -1,154 +0,0 @@ -# iOS build enablement implementation - -## Problem - -`apps/mobile_chat_app` needs a first-class iOS host project. A Flutter app's -Dart code is not sufficient by itself for iOS builds; the Flutter tool also -needs an Xcode project, a Runner target, CocoaPods integration, build -configuration files, and app metadata. If the `ios/` directory is missing, -`flutter build ios` fails structurally before normal compilation even begins. - -## Implementation principles - -- Restore a standard Flutter iOS host project for the app package instead of - inventing a custom layout. -- Keep machine-specific signing data out of committed project files. -- Put shared defaults in versioned xcconfig files and allow local overrides for - device signing. -- Validate simulator, device, and launch-mode behavior separately because they - exercise different parts of the iOS toolchain. - -## Technical approach - -### 1. Restore the iOS host project - -Create the missing `apps/mobile_chat_app/ios/` project using the standard -Flutter iOS structure for the current app package. The restored project needs -the usual Flutter iOS pieces: - -- `ios/Podfile` -- `ios/Runner.xcodeproj` -- `ios/Runner.xcworkspace` -- `ios/Runner/Info.plist` -- `ios/Runner/AppDelegate.swift` -- app assets, storyboards, and the test target scaffold - -This step solves the root structural problem: without the host project, Flutter -cannot invoke Xcode for iOS at all. - -### 2. Define app identity in shared project files - -Set the visible app name and bundle-related settings in the committed iOS -project so every contributor builds the same app identity by default. In this -project that means: - -- the display name should be `Bricks` -- the iOS bundle identifier should resolve from shared build settings - -`Info.plist` should keep using build variables such as -`$(PRODUCT_BUNDLE_IDENTIFIER)` rather than embedding local machine values. - -### 3. Use xcconfig indirection for signing and bundle settings - -The committed Xcode project should not hardcode a contributor's Apple team or -local signing state. Instead: - -- `ios/Flutter/Debug.xcconfig` -- `ios/Flutter/Release.xcconfig` - -should define shared keys such as: - -- `APP_BUNDLE_IDENTIFIER` -- `APP_DEVELOPMENT_TEAM` - -and optionally include a local override file: - -- `#include? "Local.xcconfig"` - -Then `ios/Runner.xcodeproj/project.pbxproj` should read: - -- `PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"` -- `DEVELOPMENT_TEAM = "$(APP_DEVELOPMENT_TEAM)"` - -This keeps the committed project portable while still allowing local real-device -signing. - -### 4. Keep local signing config out of version control - -If a developer needs real-device deployment, they can create -`ios/Flutter/Local.xcconfig` on their machine with their own team identifier and -any local bundle override. That file is local operational state, not shared -source code, so it should not be committed. - -This split is the cleanest implementation pattern: - -- committed files describe how the project is built -- local override files describe who is signing it on one machine - -### 5. Let Flutter regenerate ephemeral iOS build files - -Generated Flutter iOS files such as these should remain uncommitted: - -- `ios/Flutter/Generated.xcconfig` -- `ios/Flutter/flutter_export_environment.sh` -- `ios/Flutter/ephemeral/` -- `ios/Pods/` -- `ios/.symlinks/` - -They are derived from `flutter pub get`, CocoaPods resolution, and local build -state. The repository should keep only the stable project definition, not the -generated outputs. - -### 6. Validate the build in layers - -Use staged validation rather than jumping directly to device install: - -1. Bootstrap the workspace: - - `./tools/init_dev_env.sh --no-doctor` -2. Verify the iOS project structure with a simulator build: - - `cd apps/mobile_chat_app && flutter build ios --simulator --debug` -3. Verify device compilation without requiring final signing: - - `cd apps/mobile_chat_app && flutter build ios --debug --no-codesign` -4. Verify the app logic still passes tests: - - `cd apps/mobile_chat_app && flutter test` -5. Verify native launch behavior with a release build: - - `cd apps/mobile_chat_app && flutter clean && flutter pub get && flutter build ios --release` - -This order isolates structural issues, Pod integration issues, signing issues, -and runtime launch issues more cleanly. - -## Launch-mode note - -On iOS, a Flutter `debug` build is not equivalent to a normal distributable app -build. A debug build expects Flutter tooling or Xcode to be attached. That -means: - -- `debug` is appropriate for `flutter run` / Xcode debugging -- `release` or `profile` is required for normal home-screen/native launch - -So if an app installs successfully but crashes when launched directly from the -phone, launch mode must be checked before assuming there is an application logic -bug. - -## Commit boundary - -Commit: - -- `apps/mobile_chat_app/.metadata` -- shared files under `apps/mobile_chat_app/ios/` - -Do not commit: - -- `apps/mobile_chat_app/ios/Flutter/Local.xcconfig` -- `apps/mobile_chat_app/ios/Pods/` -- `apps/mobile_chat_app/ios/.symlinks/` -- `apps/mobile_chat_app/ios/Flutter/Generated.xcconfig` -- `apps/mobile_chat_app/ios/Flutter/flutter_export_environment.sh` -- other generated or machine-specific files - -## Result - -The correct implementation is not "copy another app." The correct implementation -is to restore the standard Flutter iOS host project, parameterize signing -through xcconfig indirection, keep local credentials out of Git, and validate -simulator, device, and release launch paths independently. diff --git a/docs/plans/2026-04-23-03-37-UTC-node-management-token-ui.md b/docs/plans/2026-04-23-03-37-UTC-node-management-token-ui.md deleted file mode 100644 index ed2fcb7d..00000000 --- a/docs/plans/2026-04-23-03-37-UTC-node-management-token-ui.md +++ /dev/null @@ -1,33 +0,0 @@ -# Background -The settings area currently exposes both a Node Management entry and a separate Openclaw Token entry. The user asked to verify whether token generation outputs are equivalent and reusable, then remove duplicate token-generation entry points if redundant. The user also requested Node Management UX updates: move the add-node action to the AppBar top-right using icon + text, and switch Node Management copy to English. - -# Goals -1. Verify whether token generation from Node Management and Openclaw Token page uses the same backend/service logic and whether outputs are interchangeable. -2. Remove duplicate Openclaw Token generation entry point from settings if redundant, preserving Node Management flow. -3. Move Node Management “add node” action to AppBar right side with icon + text. -4. Convert Node Management related UI copy to English. -5. Run targeted validation and summarize whether code maps require updates. - -# Implementation Plan (phased) -## Phase 1: Discovery and equivalence audit -- Locate settings route definitions and both token-generation entry points. -- Trace service calls for token generation (API endpoints, payloads, and UI wiring) to confirm equivalence/reuse. -- Record findings in final summary. - -## Phase 2: UI and navigation changes -- Remove/disable Settings list tile that navigates to standalone Openclaw Token page if duplicate. -- Ensure Node Management remains the canonical path to token generation. -- Update Node Management screen AppBar action to include an icon + text add-node button on the right. -- Translate Node Management-related labels/buttons/messages into English. - -## Phase 3: Validation and docs alignment -- Run required bootstrap script and targeted Flutter checks/tests relevant to touched package. -- Review code map files and update if feature entry/logic index changed. -- Prepare concise change notes and verification results. - -# Acceptance Criteria -- Settings no longer offers duplicate Openclaw Token generation entry if Node Management uses the same token generation logic. -- Token generation from Node Management produces output consistent with prior Openclaw token generation behavior (same data shape and intended use). -- Node Management screen shows add action in AppBar top-right with icon + text. -- Node Management visible copy is English. -- Validation commands complete successfully (or limitations are documented). diff --git a/docs/plans/2026-04-23-10-45-WIB-ios-github-login-fix.md b/docs/plans/2026-04-23-10-45-WIB-ios-github-login-fix.md deleted file mode 100644 index 636e5086..00000000 --- a/docs/plans/2026-04-23-10-45-WIB-ios-github-login-fix.md +++ /dev/null @@ -1,60 +0,0 @@ -# iOS GitHub login fix - -## Background - -The Flutter web app can start GitHub OAuth through `/api/auth/github`, and the -backend callback writes the resulting JWT into browser storage before returning to -the app. The iOS build currently compiles the same login screen, but the GitHub -button uses the non-web stub implementation, which returns `null` and produces no -visible action. - -## Goals - -- Make the iOS GitHub button open the existing backend OAuth flow. -- Allow the backend to redirect successful native logins back to the app through a - constrained custom URL scheme. -- Persist the returned JWT through the existing `AuthService` storage path. -- Preserve the current Flutter web OAuth behavior. - -## Implementation Plan (phased) - -### Phase 1: Add native OAuth launch and callback handling - -- Add mobile dependencies for launching browser URLs and listening for app links. -- Add a non-web GitHub OAuth implementation that opens the backend auth URL with a - native `return_to` target. -- Add a native OAuth callback helper that listens for `bricks://auth/github/callback` - links, extracts `auth_token`, and returns it to the login button. -- Surface a visible login failure message instead of silently returning to idle. - -### Phase 2: Configure iOS URL routing - -- Register the `bricks` URL scheme in `ios/Runner/Info.plist`. -- Keep storage identity (`auth_token`) separate from the visible login button label. - -### Phase 3: Update backend return target validation - -- Extend `isAllowedReturnTo` to allow only the expected native callback target: - `bricks://auth/github/callback`. -- Keep web return targets restricted to the existing HTTPS rules. -- Add backend tests for allowed/rejected native callback values. - -### Phase 4: Validate - -- Run `./tools/init_dev_env.sh` before Flutter checks. -- Run `cd apps/mobile_chat_app && flutter test`. -- Run `cd apps/mobile_chat_app && flutter analyze`. -- Run relevant backend tests for auth return target behavior. - -## Acceptance Criteria - -- Tapping "Continue with GitHub" in an iOS build opens the GitHub OAuth browser - flow through the backend. -- A successful OAuth callback to `bricks://auth/github/callback` saves the JWT under - the existing `auth_token` key. -- After token persistence, the app navigates to `ChatScreen`. -- Failed or cancelled login leaves the user on `LoginScreen` with a visible message. -- Web OAuth still starts from `/api/auth/github?return_to=...` and continues to use - the existing browser redirect behavior. -- Backend return URL validation rejects arbitrary non-HTTPS schemes and only allows - the expected native callback target. diff --git a/docs/plans/2026-04-23-11-27-WIB-node-token-copy-openclaw-commands.md b/docs/plans/2026-04-23-11-27-WIB-node-token-copy-openclaw-commands.md deleted file mode 100644 index 7f95156c..00000000 --- a/docs/plans/2026-04-23-11-27-WIB-node-token-copy-openclaw-commands.md +++ /dev/null @@ -1,68 +0,0 @@ -# Node token copy OpenClaw commands - -## Background - -The Nodes settings screen can create node-scoped platform tokens for OpenClaw -plugin installations. Its current copy text is a human-readable summary with -Node, Plugin ID, Base URL, Scopes, and Token fields. Users still need to -manually translate that summary into OpenClaw CLI commands before configuring -the plugin. - -OpenClaw 2026.4.15 supports targeted non-interactive configuration through -`openclaw config set`, which lets users configure only the Bricks channel -without running the broad `openclaw configure` flow. - -## Goals - -- Make the Nodes token copy action produce commands that can be pasted directly - into a shell. -- Configure only `channels.dev-askman-bricks`. -- Preserve the generated token, plugin id, and base URL from the backend token - bundle. -- Keep the copy flow simple and user-observable. - -## Implementation Plan (phased) - -### Phase 1: Command generation - -- Replace the Nodes screen install summary with a shell command block. -- Use: - - `openclaw config set channels.dev-askman-bricks.BRICKS_BASE_URL ...` - - `openclaw config set channels.dev-askman-bricks.BRICKS_PLUGIN_ID ...` - - `openclaw config set channels.dev-askman-bricks.BRICKS_PLATFORM_TOKEN ...` - - `openclaw config validate` - - `openclaw gateway restart` - - `openclaw plugins inspect dev-askman-bricks` -- Quote shell values safely for pasted command execution. - -### Phase 2: UI copy text - -- Keep the generated command block visible after token generation. -- Change the copy button label and success message to reflect that commands are - copied, not generic install information. - -### Phase 3: Tests and validation - -- Update the Nodes settings widget test so token generation shows OpenClaw - commands. -- Add clipboard assertion that the copied text contains executable - `openclaw config set` commands with the generated token bundle values. - -Validation commands: - -```bash -./tools/init_dev_env.sh -cd apps/mobile_chat_app -flutter test test/node_settings_screen_test.dart -flutter analyze -``` - -## Acceptance Criteria - -- After generating a node token, the Nodes screen shows paste-ready OpenClaw - commands. -- Copying the generated instructions writes those commands to the clipboard. -- The commands configure only `channels.dev-askman-bricks`. -- The copied commands include the generated base URL, plugin id, and platform - token. -- The targeted widget test passes. diff --git a/docs/plans/2026-04-23-11-29-WIB-release-api-base-url-default.md b/docs/plans/2026-04-23-11-29-WIB-release-api-base-url-default.md deleted file mode 100644 index 309c9ef5..00000000 --- a/docs/plans/2026-04-23-11-29-WIB-release-api-base-url-default.md +++ /dev/null @@ -1,47 +0,0 @@ -# Release API base URL default - -## Background - -Native app GitHub login and backend-backed chat/config calls use -`LlmConfigService.resolveBaseUrl()` to choose the API server. The existing -non-web default is `http://localhost:3000`, which is useful for local debug builds -but wrong for a release iOS app with a fixed production server. - -## Goals - -- Make release non-web builds default to `https://bricks.askman.dev`. -- Preserve explicit `BRICKS_API_BASE_URL` overrides for all build modes. -- Preserve local `http://localhost:3000` defaults for non-release development builds. -- Keep web behavior unchanged by using `Uri.base.origin`. - -## Implementation Plan (phased) - -### Phase 1: Update base URL resolution - -- Add a stable production API base URL constant. -- Update `resolveBaseUrl()` so precedence is: - 1. `BRICKS_API_BASE_URL` dart-define - 2. Web origin - 3. Native release production URL - 4. Native non-release localhost - -### Phase 2: Add focused tests - -- Add unit coverage for the default native release URL constant and current test-mode - resolution behavior. -- Keep tests independent of real network calls. - -### Phase 3: Validate - -- Run `dart format` on touched Dart files. -- Run targeted Flutter tests for LLM config/base URL behavior. -- Run `flutter analyze`. -- Re-run iOS simulator build because the GitHub OAuth native implementation consumes - this base URL. - -## Acceptance Criteria - -- A release iOS build without `BRICKS_API_BASE_URL` uses `https://bricks.askman.dev`. -- A build with `--dart-define=BRICKS_API_BASE_URL=...` uses the provided value. -- Debug/profile native builds can still use localhost by default. -- Existing web deployment behavior remains unchanged. diff --git a/docs/plans/2026-04-23-14-42-openclaw-package-metadata.md b/docs/plans/2026-04-23-14-42-openclaw-package-metadata.md deleted file mode 100644 index d5c1d68f..00000000 --- a/docs/plans/2026-04-23-14-42-openclaw-package-metadata.md +++ /dev/null @@ -1,31 +0,0 @@ -# Background - -The OpenClaw plugin package at `apps/node_openclaw_plugin/package.json` is missing ClawHub publish metadata under the `openclaw` field. ClawHub rejects the package upload when `openclaw.compat.pluginApi` and `openclaw.build.openclawVersion` are absent. - -# Goals - -1. Add the required OpenClaw publish metadata to the plugin package. -2. Align the metadata with the plugin's current OpenClaw dependency version. -3. Keep the package ready for external ClawHub publishing without changing runtime behavior. - -# Implementation Plan - -## Phase 1 - -Inspect the existing plugin package metadata and confirm the ClawHub-required `openclaw.compat` and `openclaw.build` fields from the OpenClaw docs. - -## Phase 2 - -Update `apps/node_openclaw_plugin/package.json` to add the missing compatibility and build metadata using the current OpenClaw package version as the baseline. - -## Phase 3 - -Run the existing plugin validation commands to ensure the package still builds and its tests continue to pass after the metadata-only change. - -# Acceptance Criteria - -1. `apps/node_openclaw_plugin/package.json` includes `openclaw.compat.pluginApi`. -2. `apps/node_openclaw_plugin/package.json` includes `openclaw.compat.minGatewayVersion`. -3. `apps/node_openclaw_plugin/package.json` includes `openclaw.build.openclawVersion`. -4. `apps/node_openclaw_plugin/package.json` includes `openclaw.build.pluginSdkVersion`. -5. The plugin's existing test, type-check, and build commands still complete successfully. diff --git a/docs/plans/2026-04-23-15-33-openclaw-selected-node-routing.md b/docs/plans/2026-04-23-15-33-openclaw-selected-node-routing.md deleted file mode 100644 index f4e7e436..00000000 --- a/docs/plans/2026-04-23-15-33-openclaw-selected-node-routing.md +++ /dev/null @@ -1,41 +0,0 @@ -# Background - -The current OpenClaw integration supports multiple platform nodes per user, but OpenClaw-routed chat messages are not addressed to a specific node. Because `/api/v1/platform/events` currently scopes pending user messages only by user identity, multiple OpenClaw nodes can receive and respond to the same chat message. - -# Goals - -1. Route each OpenClaw chat scope to exactly one selected node. -2. Ensure only the targeted node can read and process the pending chat event. -3. Preserve backward compatibility for scopes that do not yet have an explicit node selection. -4. Keep node attribution visible in the chat experience and validated by tests. - -# Implementation Plan - -## Phase 1 - -Extend backend chat scope settings to store an optional `nodeId` for OpenClaw scopes, with migration coverage and route/service updates. - -## Phase 2 - -Update chat respond handling to accept a selected `nodeId`, resolve it to the owning platform node/plugin, and stamp OpenClaw-bound user messages with target node/plugin metadata used by platform event delivery. - -## Phase 3 - -Update platform event and message-write paths so node/plugin isolation is enforced for reads and follow-up writes. - -## Phase 4 - -Update the mobile chat UI to load available platform nodes, let the user choose the active OpenClaw node for the current scope, persist that selection through scope settings, and include it in `/api/chat/respond`. - -## Phase 5 - -Add or update backend and Flutter tests, then update the code maps because routing entry points, persisted scope behavior, and test coverage will change. - -# Acceptance Criteria - -1. OpenClaw chat scopes can persist an optional selected `nodeId` per channel or thread. -2. `/api/chat/respond` forwards OpenClaw work to only the selected node, or the default node when no selection exists. -3. `/api/v1/platform/events` returns a pending chat message only to the targeted node/plugin scope. -4. Platform message writes remain node/plugin-scoped for the targeted conversation flow. -5. The mobile chat UI exposes and persists the active OpenClaw node for the current scope. -6. Validation commands complete successfully: `cd apps/node_backend && npm test`, `./tools/init_dev_env.sh`, and `cd apps/mobile_chat_app && flutter test`. diff --git a/docs/plans/2026-04-23-17-13-WIB-openclaw-agents-capture-display.md b/docs/plans/2026-04-23-17-13-WIB-openclaw-agents-capture-display.md deleted file mode 100644 index 4906c7fe..00000000 --- a/docs/plans/2026-04-23-17-13-WIB-openclaw-agents-capture-display.md +++ /dev/null @@ -1,77 +0,0 @@ -# OpenClaw agents capture and display - -## Background - -The task target is to capture agents available inside OpenClaw and surface them in -the Bricks chat interaction so users can reference them during conversation. - -This work may optionally be isolated in a dedicated local worktree to avoid -interfering with other active task worktrees. - -## Goals - -- Optionally work from the dedicated worktree at `.worktree/openclaw-agents-capture-display`. -- Identify where OpenClaw internal agent metadata can be observed or queried. -- Add a reliable path to persist or expose those agents to the mobile chat app. -- Display the captured agents in the conversation UI in a way users can reference. -- Keep code maps updated if feature entries, business logic, tests, or docs change. - -## Implementation Plan (phased) - -### Phase 1: Requirements intake - -Wait for the detailed product and technical requirements, including expected agent -identity fields, source of truth, refresh behavior, and UI interaction model. - -### Phase 2: Current-flow mapping - -Inspect the OpenClaw plugin, backend platform routes, chat routing logic, and mobile -chat UI to map where agent information should enter and where it should be rendered. - -Findings: - -- The mobile composer already has a route-bound `@` menu. -- The OpenClaw route currently shows a disabled placeholder item in that menu. -- Platform node tokens already bind `userId + pluginId`, and the backend can map - that plugin ID back to a node. -- There is not yet a selected-node router state in this branch, so this change uses - the first/default platform node while keeping the API nodeId-scoped for future - selected-node routing integration. - -### Phase 3: Data contract - -Define or reuse a stable agent data contract that separates display naming from -storage identity. Include any required IDs, labels, source platform metadata, and -reference syntax. - -### Phase 4: Implementation - -Implement the smallest end-to-end path that captures OpenClaw agents and makes them -available in the chat interaction. Keep existing selected-node-routing worktree -changes isolated. - -Implemented path: - -1. Add config `GET /api/config/nodes/:nodeId/agents` for the authenticated mobile - app to read agents for the matching node. -2. For `sourcePlatform=openclaw`, resolve agents on demand via - `openclaw agents list --json` instead of backend persistence. -3. Replace the OpenClaw route `@` placeholder with reported agents and insert an - `@agentId` reference into the composer when selected. - -### Phase 5: Validation - -Run targeted tests from the relevant package directories. For mobile app Flutter -checks, run commands from `apps/mobile_chat_app` as required by `AGENTS.md`. - -## Acceptance Criteria - -- The task uses `.worktree/openclaw-agents-capture-display` and does not modify - other active worktrees. -- OpenClaw agents are captured from the agreed source of truth. -- Users can see and reference captured OpenClaw agents from the conversation - interaction. -- Agent display labels and stable storage IDs are kept separate. -- Existing OpenClaw routing and chat behavior continue to pass targeted tests. -- Code maps are updated if the implementation changes feature entries, logic - indexes, tests, or documentation indexes. diff --git a/docs/plans/2026-04-24-12-15-dispatch-avatar-placeholder.md b/docs/plans/2026-04-24-12-15-dispatch-avatar-placeholder.md deleted file mode 100644 index 7ae46fb5..00000000 --- a/docs/plans/2026-04-24-12-15-dispatch-avatar-placeholder.md +++ /dev/null @@ -1,24 +0,0 @@ -# Background -The chat UI currently marks the user message as accepted immediately after `/api/chat/respond` returns, which is correct. However, assistant identity only becomes visible once a real assistant message arrives. For async routers and model streaming, that creates a visible gap where the user knows the task was accepted but cannot yet see which assistant has taken over. - -# Goals -1. Show assistant identity as soon as backend dispatch begins, before assistant text arrives. -2. Keep the user-message acceptance checkmark driven by `/api/chat/respond`. -3. Reuse the existing SSE message pipeline instead of introducing a separate event channel. -4. Preserve stable ordering between the accepted user message and the dispatch placeholder assistant row. - -# Implementation Plan -## Phase 1 -Emit a backend-owned assistant dispatch placeholder using the existing `chat_messages` + SSE flow for both OpenClaw and default-router async execution paths. - -## Phase 2 -Render assistant dispatch placeholders in the mobile chat UI as avatar/header + processing indicator without showing an empty assistant text bubble. - -## Phase 3 -Add regression tests for backend dispatch placeholder emission, frontend ordering, and placeholder rendering behavior. Update code maps because user-visible chat behavior and test indexes change. - -# Acceptance Criteria -1. After `/api/chat/respond` returns, the user message still shows accepted state immediately. -2. Before assistant text is available, SSE can deliver a placeholder assistant row that shows the receiving assistant identity. -3. When the real assistant reply for the same `messageId` arrives, the placeholder becomes a normal assistant message instead of rendering as a separate extra row. -4. Relevant backend and Flutter tests pass. diff --git a/docs/plans/2026-04-26-00-57-switch-to-message-bubble-background-branch.md b/docs/plans/2026-04-26-00-57-switch-to-message-bubble-background-branch.md deleted file mode 100644 index 17a41cd1..00000000 --- a/docs/plans/2026-04-26-00-57-switch-to-message-bubble-background-branch.md +++ /dev/null @@ -1,34 +0,0 @@ -## Background - -The repository needs to be synced with the latest remote `main`, and the existing remote branch for message bubble color changes needs to be identified and checked out locally. - -## Goals - -- Refresh remote refs from `origin`. -- Update local `main` to match `origin/main` without overwriting unrelated local changes. -- Find the remote branch associated with message bubble color changes. -- Switch the workspace to that branch with proper tracking. - -## Implementation Plan - -### Phase 1 - -Fetch and prune remote refs from `origin` to ensure the branch list and `main` pointer are current. - -### Phase 2 - -Identify the message bubble color branch by searching remote branch names and commit subjects, then confirm the intended branch. - -### Phase 3 - -Checkout local `main`, fast-forward it to `origin/main`, and switch to the identified remote branch with tracking. - -## Acceptance Criteria - -- `origin/main` is fetched and local `main` is fast-forwarded to it. -- The remote bubble color branch is identified. -- The current checked-out branch is the local tracking branch for the bubble color change. -- Validation commands: - - `git fetch --all --prune` - - `git status --short --branch` - - `git branch -vv` diff --git a/docs/plans/2026-04-26-02-45-UTC-jump-to-latest-message.md b/docs/plans/2026-04-26-02-45-UTC-jump-to-latest-message.md deleted file mode 100644 index d96fe683..00000000 --- a/docs/plans/2026-04-26-02-45-UTC-jump-to-latest-message.md +++ /dev/null @@ -1,25 +0,0 @@ -# Background -The chat UI needs a dedicated “jump to latest message” control above the composer so users can quickly return to the newest content after scrolling upward. - -# Goals -- Add a visible, tappable control that appears when the user is away from the bottom of the message list. -- Scroll directly to the latest message area when the control is tapped. -- Cover behavior with widget tests. - -# Implementation Plan (phased) -1. **Message list behavior** - - Add scroll position tracking in `MessageList`. - - Compute whether the list is far enough from the bottom to show the jump button. - - Add a button overlay near the lower center of the message list. -2. **Interaction** - - Implement animated scroll-to-bottom behavior when the button is tapped. - - Ensure visibility state updates after auto-scroll and manual scroll. -3. **Validation** - - Add widget tests for button visibility and tap-to-jump behavior. - - Run `./tools/init_dev_env.sh` and `cd apps/mobile_chat_app && flutter test test/message_list_test.dart`. - -# Acceptance Criteria -- When the user scrolls up away from the bottom, a downward-arrow jump button appears above the composer area. -- Tapping the button scrolls to the latest message. -- The button hides again when the user is near the bottom. -- `flutter test test/message_list_test.dart` passes in `apps/mobile_chat_app`. diff --git a/docs/plans/2026-04-26-02-45-dark-theme-semantic-token-refactor.md b/docs/plans/2026-04-26-02-45-dark-theme-semantic-token-refactor.md deleted file mode 100644 index ad4a6887..00000000 --- a/docs/plans/2026-04-26-02-45-dark-theme-semantic-token-refactor.md +++ /dev/null @@ -1,35 +0,0 @@ -# Background -The chat UI dark theme currently mixes direct `ColorScheme` fields and hard-coded color values, which causes inconsistent hierarchy across user bubbles, assistant text, composer UI, and metadata. The design direction requires an X-style dark palette with semantic token usage so product surfaces remain neutral while brand blue is reserved for actionable states. - -# Goals -1. Introduce a semantic dark color token set aligned with the provided palette (pure black base, grayscale surfaces, single brand blue, isolated status colors). -2. Ensure chat components consume semantic tokens instead of raw colors or misused `colorScheme.primary` for non-brand surfaces. -3. Update key components (message list + composer) to reflect the new visual hierarchy in dark mode. -4. Keep code-map docs synchronized for changed feature logic/index references. - -# Implementation Plan (phased) -## Phase 1: Token foundation -- Update `packages/design_system/lib/src/tokens.dart` with semantic palette constants (background/surface/border/text/accent/status). -- Expand `packages/design_system/lib/src/chat_colors.dart` semantic fields to cover message/composer/meta/link/badge roles. -- Update `packages/design_system/lib/src/bricks_theme.dart` dark `ColorScheme` to keep Material base semantics minimal and consistent with the new palette. - -## Phase 2: Chat UI migration -- Refactor `apps/mobile_chat_app/lib/features/chat/widgets/message_list.dart` to consume updated `ChatColors` semantics for: - - user bubble/background/text/meta - - assistant text/meta/accent - - badge/pill styling - - markdown inline link color and code/quote container styling -- Refactor `apps/mobile_chat_app/lib/features/chat/widgets/composer_bar.dart` to use semantic composer colors for container, border, placeholder, and send/stop actions. - -## Phase 3: Validation and docs -- Run environment bootstrap and targeted Flutter checks. -- Update `docs/code_maps/feature_map.yaml` and `docs/code_maps/logic_map.yaml` to reflect token/theme and chat widget mapping changes. -- Verify YAML format and summarize regression-smoke focus. - -# Acceptance Criteria -- Dark mode uses black background + grayscale surfaces + single accent blue for interactive emphasis. -- User message bubble no longer uses Material `primary`; it uses semantic message container tokens. -- Meta text (time/thread/handle-like info) uses a unified secondary token. -- Composer default border is subtle, with stronger emphasis only on focus/action states. -- Business widgets avoid raw hex colors and direct color misuse for chat hierarchy. -- Validation commands complete successfully: `./tools/init_dev_env.sh`, `cd apps/mobile_chat_app && flutter analyze`, `npx js-yaml docs/code_maps/feature_map.yaml`, and `npx js-yaml docs/code_maps/logic_map.yaml`. diff --git a/docs/plans/2026-04-26-07-39-UTC-message-typography-placeholder.md b/docs/plans/2026-04-26-07-39-UTC-message-typography-placeholder.md deleted file mode 100644 index 84a3e1b7..00000000 --- a/docs/plans/2026-04-26-07-39-UTC-message-typography-placeholder.md +++ /dev/null @@ -1,17 +0,0 @@ -# Background -User requested two UI token adjustments in chat: increase message body typography and switch placeholder color to a white-family token from the existing design system. - -# Goals -1. Use larger text style for chat message body content. -2. Replace composer placeholder color with an existing design-system white-family semantic color. -3. Validate with focused Flutter tests. - -# Implementation Plan (phased) -1. Update message body text styles in `message_list.dart` from `bodyMedium` to `bodyLarge` (assistant and user message text). -2. Update composer placeholder semantic color in `chat_colors.dart` to use a design-system token from `AppColors` without introducing new values. -3. Run environment bootstrap and targeted tests for chat widgets. - -# Acceptance Criteria -- User and assistant message body text render using `bodyLarge` in chat message list. -- Composer placeholder no longer uses tertiary text; uses the selected white-family design-system token. -- Relevant Flutter widget tests pass. diff --git a/docs/plans/2026-04-26-07-53-UTC-conflict-sync-and-placeholder-adjustment.md b/docs/plans/2026-04-26-07-53-UTC-conflict-sync-and-placeholder-adjustment.md deleted file mode 100644 index c889cca3..00000000 --- a/docs/plans/2026-04-26-07-53-UTC-conflict-sync-and-placeholder-adjustment.md +++ /dev/null @@ -1,20 +0,0 @@ -# Background -The previous change was not accepted. We need to ensure the requested typography/token behavior remains implemented while attempting to sync with latest `main` for conflict resolution. - -# Goals -1. Attempt to sync current branch with latest remote `main`. -2. Keep chat message body on `bodyLarge`. -3. Adjust composer placeholder to the most suitable existing white-family design-system token. -4. Re-run focused tests. - -# Implementation Plan (phased) -1. Run Git remote sync commands and report any environment constraints. -2. Update `ChatColors.composerPlaceholder` to a stronger white-family semantic token from `AppColors`. -3. Verify message typography remains on `bodyLarge`. -4. Run Flutter widget tests in `apps/mobile_chat_app`. - -# Acceptance Criteria -- Sync attempt to remote main is executed and result is documented. -- Message body text style remains `bodyLarge` for user and assistant chat text. -- Placeholder uses an existing white-family design-system token (no new color constants). -- Targeted widget tests pass. diff --git a/docs/plans/2026-04-26-10-20-UTC-docusaurus-doc-ia-restructure.md b/docs/plans/2026-04-26-10-20-UTC-docusaurus-doc-ia-restructure.md deleted file mode 100644 index 3adbbc21..00000000 --- a/docs/plans/2026-04-26-10-20-UTC-docusaurus-doc-ia-restructure.md +++ /dev/null @@ -1,32 +0,0 @@ -# Background - -The repository documentation had strong technical content but weak information architecture for product-facing navigation. The task is to restructure docs into a Docusaurus-friendly layout based only on currently implemented capabilities. - -# Goals - -1. Reorganize documentation entry points into product-first sections. -2. Keep all content grounded in currently implemented repository state. -3. Avoid roadmap, pricing, or operations planning content. - -# Implementation Plan (phased) - -## Phase 1: Entry and navigation -- Update `README.md` to point to a clear docs IA. -- Add `docs/intro.md` as the documentation entry page. - -## Phase 2: Section pages -- Add product overview page under `docs/product/`. -- Add quickstart page under `docs/get-started/`. -- Add OpenClaw integration summary under `docs/integrations/`. -- Add architecture overview page under `docs/architecture/`. -- Add FAQ page under `docs/faq/`. - -## Phase 3: Documentation index alignment -- Update code map `doc_index` references where relevant so new docs are discoverable. - -# Acceptance Criteria - -1. Repository contains a Docusaurus-friendly docs folder structure with entry page and section pages. -2. `README.md` links users to the new docs structure. -3. New pages describe only currently implemented capabilities/workflows. -4. Code map document indexes include newly introduced docs references where relevant. diff --git a/docs/plans/2026-04-26-11-08-UTC-jump-button-threshold-and-scroll-target.md b/docs/plans/2026-04-26-11-08-UTC-jump-button-threshold-and-scroll-target.md deleted file mode 100644 index 60a99942..00000000 --- a/docs/plans/2026-04-26-11-08-UTC-jump-button-threshold-and-scroll-target.md +++ /dev/null @@ -1,25 +0,0 @@ -# Background -The chat message list currently shows the "Jump to latest" button when the user is only a short distance from the latest content, and tapping the button animates to the list max extent. With extra list bottom padding, this can feel too eager and can visually overshoot/rebound when far away. - -# Goals -- Show the jump button only when the user is significantly far from the latest content. -- Update jump animation target so it lands directly at the intended latest-content position without rebound-like behavior. -- Keep or improve existing widget test coverage. - -# Implementation Plan (phased) -1. Update `MessageList` scroll-distance logic: - - Replace fixed reveal threshold with a dynamic threshold based on viewport height (2 screens). - - Compute distance from current offset to the latest-content anchor (max extent minus reserved bottom padding). -2. Update jump action behavior: - - Animate to the latest-content anchor offset instead of absolute max extent. - - Use a duration scaled by distance to keep motion stable over long jumps. -3. Update tests: - - Adjust jump-to-latest assertion to anchor offset behavior. - - Add coverage that button stays hidden when far distance is below the 2-screen threshold. -4. Validate: - - Run repository bootstrap and targeted Flutter widget tests. - -# Acceptance Criteria -- Jump button appears only when distance to latest-content anchor exceeds two full viewport heights. -- Tapping jump button animates directly to the latest-content anchor position (not absolute bottom padding extent). -- Message list widget tests pass, including updated jump behavior checks. diff --git a/docs/plans/2026-04-26-12-03-UTC-chat-bubble-contrast-spacing.md b/docs/plans/2026-04-26-12-03-UTC-chat-bubble-contrast-spacing.md deleted file mode 100644 index b640235c..00000000 --- a/docs/plans/2026-04-26-12-03-UTC-chat-bubble-contrast-spacing.md +++ /dev/null @@ -1,26 +0,0 @@ -# Background -The user requested two dark-mode chat UI adjustments in the mobile chat app: (1) make user message bubbles use a clear white-like color similar to a competitor screenshot, and (2) increase spacing between user and assistant messages. The user also asked why the current color feels visually "weak"/"hazy". - -# Goals -1. Increase visual clarity of user bubbles in dark mode by using a higher-luminance bubble background and preserving text contrast. -2. Increase perceived separation between user turns and assistant turns in the message list. -3. Keep behavior stable and cover the style adjustments with widget tests where practical. - -# Implementation Plan (phased) -## Phase 1: Inspect current tokens and message layout -- Locate chat color tokens and message-list spacing logic in `packages/design_system` and `apps/mobile_chat_app`. - -## Phase 2: Apply visual updates -- Update dark-mode user bubble token(s) to a light surface with strong text contrast. -- Adjust message list spacing rules to increase the gap after user bubbles. -- Tune user bubble metadata color so it remains legible on the lighter bubble. - -## Phase 3: Validate -- Run environment bootstrap: `./tools/init_dev_env.sh`. -- Run targeted widget tests from mobile package directory, focusing on `message_list_test.dart`. -- If needed, update/add tests that assert bubble color and spacing behavior. - -# Acceptance Criteria -- In dark theme, user bubbles render with a light, high-contrast background and readable dark text. -- The vertical gap between a user message block and following assistant content is visibly larger than before. -- Widget tests covering the modified message-list behavior pass (`cd apps/mobile_chat_app && flutter test test/message_list_test.dart`). diff --git a/docs/plans/2026-04-26-12-10-UTC-dark-composer-visual-unification-todo.md b/docs/plans/2026-04-26-12-10-UTC-dark-composer-visual-unification-todo.md deleted file mode 100644 index ff58a8f7..00000000 --- a/docs/plans/2026-04-26-12-10-UTC-dark-composer-visual-unification-todo.md +++ /dev/null @@ -1,78 +0,0 @@ -# Dark Mode Composer Visual Unification — Code-Fit TODO Plan - -## Background -The current dark-mode chat UI already has a design-system foundation (`AppColors`, `ChatColors`) but key composer and status visuals are still inconsistent with a premium minimal style: - -- Accent blue is overused for non-critical controls (e.g., `/`, `@`, send icon, stop button backgrounds). -- Colored emoji (`🦞`) is used as a delivery/status icon in multiple places. -- Agent attribution text/icon color and composer action colors are not separated by semantic intent. -- Composer container sizing/padding leaves a “large console” impression instead of a compact ChatGPT/X-like composer. - -In addition, earlier token-level redundancy/ambiguity has been reduced: - -- Legacy `AppColors` aliases such as `surface2`/`surface3` were removed in favor of explicit surface-layer names. -- `ChatColors` now favors a smaller set of aliases that map back to core surface/text/accent layers. -- `agentAccent` is still overloaded for running/active emphasis and should be narrowed in a later pass. - -## Goals -1. Keep dark mode minimal and premium by converging action icon colors to a white/gray scale. -2. Restrict blue accent to true state/activation semantics only. -3. Remove colorful emoji from production chat/status UI. -4. Reduce composer default visual weight (height/padding/button emphasis) while keeping usability. -5. Land changes in a way that matches existing code structure and tests, avoiding disruptive refactors. - -## Implementation Plan (phased) - -### Phase 1 — Token cleanup and semantic tightening (design system first) -- Keep the current compact token set and avoid reintroducing one-off action-state aliases unless a widget actually consumes them. -- Continue using existing roles where they match current behavior: - - `composerActionIdle` - - `sendIdle` - - `sendActive` - - `agentIdentity` -- Add `statusRunning` / `statusCompleted` only if status UI needs product-specific meaning beyond existing accent/status tokens. -- Map dark defaults to neutral white-gray scale; reserve accent blue only for activated/explicit state cues. - -### Phase 2 — ComposerBar visual convergence -- In `composer_bar.dart`: - - Replace `/` and `@` trigger text colors from `chatColors.agentAccent` to neutral token (`composerActionIdle`). - - Replace config/tune icon, send icon, and stop control with state-driven neutral/active mapping: - - Empty input -> `sendIdle` (neutral) - - Non-empty input -> `sendActive` (clear active cue) - - Disabled -> existing disabled Material state or a new token only if the component needs product-specific disabled styling - - Introduce `ValueListenableBuilder`/controller listener for input non-empty state to drive send visual activation. - - Reduce composer perceived height: - - lower vertical paddings - - slightly smaller action tap target visuals (without violating minimum hit area) - - cap multiline expansion more tightly (e.g., keep `maxLines: 4` unless product requires 5) - -### Phase 3 — Status icon and agent attribution de-colorization -- In `message_list.dart` and `chat_screen.dart`: - - Replace lobster emoji delivery indicators and router emoji entry with monochrome icon(s) (custom glyph or Material icon fallback). - - Move assistant attribution icon/text color to neutral `agentIdentity` token. - - Keep blue only for explicit “running/active route selected” states when needed. - -### Phase 4 — Redundancy hardening and migration safety -- Audit call sites still directly relying on overloaded accent tokens and migrate to existing semantic tokens. -- Do not add component-specific color aliases unless they represent stable product semantics and map to the core surface-layer model. -- Ensure theme extension fallback behavior remains stable for tests running plain `MaterialApp`. - -### Phase 5 — Validation and regression coverage -- Update/add widget tests in `composer_bar_test.dart` and `message_list` related tests to assert: - - neutral default send icon state - - activated send visual state only when input has content - - no lobster emoji rendered in delivery/status UI - - composer menu/actions disabled states remain correct - -## Acceptance Criteria -1. In dark mode, bottom composer action controls use a consistent white/gray hierarchy in idle state. -2. Send control is not permanently highlighted; it visibly enters active state only when input is non-empty. -3. No colorful emoji is used for delivery/router status UI in chat surfaces. -4. Agent name/icon in message attribution no longer uses bright blue in idle identity display. -5. Composer appears visually more compact than before while retaining accessibility and functional parity. -6. Existing composer behavior tests pass; new state-color behavior tests pass. - -## Validation Commands -- `./tools/init_dev_env.sh` -- `cd apps/mobile_chat_app && flutter test test/composer_bar_test.dart` -- `cd apps/mobile_chat_app && flutter test test/message_list_test.dart` diff --git a/docs/plans/2026-04-26-12-19-docs-subpath-vercel-integration.md b/docs/plans/2026-04-26-12-19-docs-subpath-vercel-integration.md deleted file mode 100644 index 7e6e687a..00000000 --- a/docs/plans/2026-04-26-12-19-docs-subpath-vercel-integration.md +++ /dev/null @@ -1,37 +0,0 @@ -# Background -The repository currently deploys Flutter Web from `apps/mobile_chat_app/build/web` on Vercel, while the Docusaurus docs site is configured at root (`baseUrl: '/'`). We need to mount docs at `/docs/` without breaking existing Flutter SPA routing or API rewrites. - -# Goals -1. Configure Docusaurus so generated links and assets are rooted at `/docs/`. -2. Update Vercel build flow to package docs output under Flutter Web output at `build/web/docs`. -3. Update Vercel rewrites so `/docs` and `/docs/*` are served by Docusaurus, while all other app routes continue to Flutter SPA. -4. Keep API routing (`/api/*`) unchanged. - -# Implementation Plan (phased) -## Phase 1: Docs site config -- Update `apps/docs_site/docusaurus.config.ts`: - - Hardcode `baseUrl: '/docs/'` — no env var needed. - - Keep `routeBasePath: '/'` to preserve docs-only mode semantics. - - Resolve `url` from `DOCS_URL` (optional override) → `VERCEL_PROJECT_PRODUCTION_URL` → `VERCEL_URL` → fail-fast error. No manual configuration required on Vercel. - -## Phase 2: Build pipeline integration -- Update `tools/vercel-build.sh`: - - Add `install_docs_site` and `build_docs_site` helpers. - - Run docs dependency install in `--install-only` mode. - - After Flutter web release build, build docs and copy to `apps/mobile_chat_app/build/web/docs`. - - Ensure copy happens after Flutter build to avoid output overwrite. - -## Phase 3: Vercel route dispatch -- Update `vercel.json` rewrites: - - Keep `/api/:path* -> /api/index` first. - - Add `/docs -> /docs/index.html` and `/docs/:path* -> /docs/index.html` before Flutter fallback. - - Keep catch-all fallback to `/index.html` last. - -## Phase 4: Regression metadata -- Review and update `docs/code_maps/feature_map.yaml` and `docs/code_maps/logic_map.yaml` if docs entry/routing capabilities are impacted. - -# Acceptance Criteria -1. Running Docusaurus build generates docs site paths under `/docs/` (baseUrl is hardcoded; no env var required). -2. Running `bash ./tools/vercel-build.sh` produces `apps/mobile_chat_app/build/web/docs/index.html`. -3. `vercel.json` rewrites route `/docs` and `/docs/*` to docs index before Flutter fallback. -4. Existing `/api/*` and Flutter fallback routing remain present and ordered correctly. diff --git a/docs/plans/2026-04-26-13-20-UTC-docusaurus-site-implementation.md b/docs/plans/2026-04-26-13-20-UTC-docusaurus-site-implementation.md deleted file mode 100644 index 82b0e45a..00000000 --- a/docs/plans/2026-04-26-13-20-UTC-docusaurus-site-implementation.md +++ /dev/null @@ -1,33 +0,0 @@ -# Background - -The repository documentation was reorganized into a Docusaurus-friendly structure, but it still lacked an executable Docusaurus site and clickable docs links in README. - -# Goals - -1. Convert README docs path listings into clickable Markdown links with valid targets. -2. Add an independent `website/` Docusaurus project that renders docs from repository root `docs/`. -3. Keep existing Flutter, melos, and Vercel project structures untouched. - -# Implementation Plan (phased) - -## Phase 1: README link usability -- Replace code-style docs paths in README with Markdown links. -- Add a short section showing how to run and build the docs site from `website/`. - -## Phase 2: Docusaurus project bootstrap -- Create `website/package.json` with Docusaurus dependencies and npm scripts. -- Add `website/docusaurus.config.ts` with docs source path set to `../docs`. -- Add `website/sidebars.ts` grouped by Introduction, Product, Get Started, Integrations, Architecture, FAQ, and Plans. -- Add basic CSS and ignore files needed for local development. - -## Phase 3: Docs metadata and validation -- Add minimal frontmatter to key docs pages used by sidebar. -- Run `npm install` and `npm run build` under `website/`. -- Review `git diff --name-status` to ensure expected scope. - -# Acceptance Criteria - -1. README docs entries are clickable and point to real files. -2. `cd website && npm run build` succeeds. -3. Sidebar groups map to current docs information architecture. -4. Existing app and backend build structures remain unaffected. diff --git a/docs/plans/2026-04-26-14-05-UTC-move-docusaurus-site-under-apps.md b/docs/plans/2026-04-26-14-05-UTC-move-docusaurus-site-under-apps.md deleted file mode 100644 index 1242a467..00000000 --- a/docs/plans/2026-04-26-14-05-UTC-move-docusaurus-site-under-apps.md +++ /dev/null @@ -1,29 +0,0 @@ -# Background - -The previous implementation created the Docusaurus project at repository root (`website/`), but stakeholder feedback requires the docs site to live under `apps/`. - -# Goals - -1. Relocate the Docusaurus project from `website/` to `apps/docs_site/`. -2. Keep the site functional with docs sourced from repository root `docs/`. -3. Update README run/build instructions to the new location. - -# Implementation Plan (phased) - -## Phase 1: Relocation -- Move `website/` to `apps/docs_site/` with full history-preserving rename. - -## Phase 2: Configuration fixes -- Update Docusaurus docs source path for new directory depth (`../../docs`). -- Update README commands to run from `apps/docs_site`. - -## Phase 3: Validation -- Run `npm install` and `npm run build` from `apps/docs_site`. -- Verify changed file scope with `git diff --name-status`. - -# Acceptance Criteria - -1. No `website/` remains at repo root. -2. `apps/docs_site` contains runnable Docusaurus project. -3. `cd apps/docs_site && npm run build` succeeds. -4. README points to the new docs site location. diff --git a/docs/plans/2026-04-26-23-24-+08-chat-dark-colors.md b/docs/plans/2026-04-26-23-24-+08-chat-dark-colors.md deleted file mode 100644 index 53590be3..00000000 --- a/docs/plans/2026-04-26-23-24-+08-chat-dark-colors.md +++ /dev/null @@ -1,31 +0,0 @@ -# Chat Dark Color Updates - -## Background -The mobile chat screen uses design-system tokens for both its page scaffold background and its bottom composer input. The chat input composer is rendered by `ComposerBar`, which reads `ChatColors.composerBackground`; the dark page scaffold reads `AppColors.backgroundBase` through `BricksTheme.dark()`. - -## Goals -- Identify the existing chat input color path before changing it. -- Change the dark chat composer input background to `#303030`. -- Change the dark page scaffold background to `#212121`. -- Match the observed responsive layering where compact dark chat uses `#000000 -> #212121` and wide dark chat uses `#212121 -> #303030`. -- Reuse a small set of generic background/surface tokens instead of adding component-specific color tokens. - -## Implementation Plan (phased) -1. Trace the chat input widget from `ChatScreen` to `ComposerBar` and confirm it uses `ChatColors.composerBackground`. -2. Use generic `AppColors.surfaceElevated` with `Color(0xFF303030)` for elevated surfaces such as the wide composer. -3. Point dark `ChatColors.composerBackground` to `AppColors.surfaceElevated`. -4. Update `AppColors.backgroundBase` to `Color(0xFF212121)` so dark `scaffoldBackgroundColor` resolves to the requested page background. -5. Use compact dark chat layout to apply `AppColors.backgroundChrome` as the page background and `AppColors.backgroundBase` as the composer background. -6. Keep wide dark chat layout on `AppColors.backgroundBase` for the page and `AppColors.surfaceElevated` for the composer. -7. Use the default surface token for recessed content blocks rather than elevating markdown/code blocks. -8. Run formatting and mobile Flutter analysis. - -## Acceptance Criteria -- In dark mode, the chat input composer background resolves to `#303030`. -- In dark mode, the page scaffold background resolves to `#212121`. -- In compact dark chat, the page background resolves to `#000000` and the composer background resolves to `#212121`. -- In wide dark chat, the page background resolves to `#212121` and the composer background resolves to `#303030`. -- `ComposerBar` uses `chatColors.composerBackground` by default and receives only a compact-layout override when needed. -- The chat AppBar continues to follow `Theme.of(context).scaffoldBackgroundColor`. -- No component-specific input color token is introduced. -- `dart format` and `flutter analyze` complete successfully. diff --git a/docs/plans/2026-04-27-00-04-+08-pr-branch-sync-skill.md b/docs/plans/2026-04-27-00-04-+08-pr-branch-sync-skill.md deleted file mode 100644 index af3dc579..00000000 --- a/docs/plans/2026-04-27-00-04-+08-pr-branch-sync-skill.md +++ /dev/null @@ -1,22 +0,0 @@ -# PR Branch Sync Skill - -## Background -Recent PR work exposed a repeatable workflow: push can fail because network access is restricted or because the remote branch changed after the local branch was created. Codex should preserve remote work, check whether a PR already exists, rebase local changes when the remote branch is newer, push again, and update the PR summary. - -## Goals -- Add a repository-local Codex skill under `.codex/skills/`. -- Capture the PR branch recovery workflow concisely. -- Emphasize preserving remote commits and using `--force-with-lease` only when intentionally rewriting a branch. - -## Implementation Plan (phased) -1. Create `.codex/skills/github-pr-branch-sync/SKILL.md`. -2. Document checks for current branch, existing PR, remote branch existence, and remote-ahead state. -3. Document safe rebase, push, and PR summary update steps. -4. Validate the skill metadata and markdown content by inspecting the created file. - -## Acceptance Criteria -- A new Codex skill exists under `.codex/skills/github-pr-branch-sync/`. -- The skill tells Codex what to do after a failed push. -- The skill includes checks for existing PRs and remote branches. -- The skill instructs Codex to rebase local changes when the remote branch is newer. -- The skill instructs Codex to update the PR summary after pushing. diff --git a/docs/plans/2026-04-27-00-19-+08-design-system-token-alignment.md b/docs/plans/2026-04-27-00-19-+08-design-system-token-alignment.md deleted file mode 100644 index 793f4f87..00000000 --- a/docs/plans/2026-04-27-00-19-+08-design-system-token-alignment.md +++ /dev/null @@ -1,23 +0,0 @@ -# Design System Token Alignment - -## Background -The current color KB emphasizes component-level tokens, while the latest chat dark-mode work uses a smaller surface-layer model inspired by ChatGPT: base/chrome/recessed/elevated surfaces with thin component aliases. Code and documentation need to describe the same model. - -## Goals -- Identify redundant or stale color token definitions. -- Update the color KB so it matches the intended surface-layer-first design. -- Keep useful component aliases where they improve readability. -- Remove unused token definitions that encourage over-specific naming. - -## Implementation Plan (phased) -1. Audit actual token usage in `AppColors`, `ChatColors`, and chat widgets. -2. Remove unused legacy aliases and unused `ChatColors` fields. -3. Revise `docs/kb/color-theme-architecture.md` to define the surface-layer-first rule. -4. Run formatting and Flutter analysis. -5. Check whether code maps need an update because the KB/design-token index changed. - -## Acceptance Criteria -- The KB says to prefer existing surface/text/border/accent/status layers before adding component tokens. -- Code no longer exposes unused `BricksColorTokens`, legacy aliases, or unused `ChatColors` fields. -- Existing chat UI behavior remains unchanged. -- Validation includes `dart format` and `flutter analyze`. diff --git a/docs/plans/2026-04-27-00-27-+08-design-system-skill.md b/docs/plans/2026-04-27-00-27-+08-design-system-skill.md deleted file mode 100644 index c6fb06cb..00000000 --- a/docs/plans/2026-04-27-00-27-+08-design-system-skill.md +++ /dev/null @@ -1,21 +0,0 @@ -# Design System Skill - -## Background -The color/theme KB now defines a surface-layer-first approach. Future Codex runs need a repository-local skill that reminds agents to use that model before modifying UI colors, adding tokens, or changing chat surfaces. - -## Goals -- Add a repository-local design-system skill under `.codex/skills/`. -- Make the skill route agents to `docs/kb/color-theme-architecture.md` before color/theme edits. -- Capture the current token decision rules: reuse core layers first, add component aliases sparingly, and keep docs/code maps aligned. - -## Implementation Plan (phased) -1. Create `.codex/skills/bricks-design-system/SKILL.md`. -2. Document the inspection workflow for token/theme/UI color changes. -3. Include guardrails for redundant tokens, responsive surface roles, and validation. -4. Add the new skill to the relevant code-map documentation index. - -## Acceptance Criteria -- A new Codex skill exists for Bricks design-system work. -- The skill instructs agents to read the color/theme KB before design-token edits. -- The skill encodes the surface-layer-first rule and component-alias criteria. -- The code maps reference the new skill where design-system docs are indexed. diff --git a/docs/plans/2026-04-27-00-40-+08-responsive-sidebar-surface.md b/docs/plans/2026-04-27-00-40-+08-responsive-sidebar-surface.md deleted file mode 100644 index 8c6e1394..00000000 --- a/docs/plans/2026-04-27-00-40-+08-responsive-sidebar-surface.md +++ /dev/null @@ -1,23 +0,0 @@ -# Responsive Sidebar Surface - -## Background -The chat navigation drawer currently expands to the full viewport width and relies on implicit Material/Scaffold background behavior. The desired dark-mode behavior is a black navigation surface on both compact and wide layouts, with compact retaining full-width navigation and wide layouts using a fixed sidebar width so it contrasts with the near-black chat canvas. - -## Goals -- Make the chat navigation drawer background explicit. -- Use black/chrome background for the dark sidebar in compact and wide layouts. -- Keep compact drawer full-width. -- Use a fixed wide-layout drawer width aligned with the ChatGPT-like 260px reference. - -## Implementation Plan (phased) -1. Add responsive drawer width and background role selection in `ChatScreen`. -2. Apply the drawer background to both `Drawer` and the nested navigation content theme so the inner `Scaffold` cannot paint a different surface. -3. Update code maps to capture the sidebar width/surface smoke check. -4. Run formatting, focused navigation tests, Flutter analysis, and code-map YAML validation. - -## Acceptance Criteria -- In dark compact chat, the sidebar drawer is full-width and black. -- In dark wide chat, the sidebar drawer is fixed-width and black. -- In dark wide chat, opening the sidebar creates visible contrast against the `#212121` chat background. -- No new color token is introduced. -- Validation commands pass. diff --git a/docs/plans/2026-04-27-00-42-+08-quiet-composer-focus.md b/docs/plans/2026-04-27-00-42-+08-quiet-composer-focus.md deleted file mode 100644 index 2f2d1a60..00000000 --- a/docs/plans/2026-04-27-00-42-+08-quiet-composer-focus.md +++ /dev/null @@ -1,19 +0,0 @@ -# Quiet Composer Focus Border - -## Background -The chat composer currently switches to a stronger border token when the text field gains focus. The requested visual direction is quieter: focusing the input should not create a highlighted border. - -## Goals -- Keep the composer border visually stable across focused and unfocused states. -- Remove now-unused focus-border color alias if no other widget consumes it. -- Avoid adding new color tokens. - -## Implementation Plan (phased) -1. Change `ComposerBar` to always use `chatColors.composerBorder`. -2. Remove `ChatColors.composerBorderFocus` if it becomes unused. -3. Run formatting and mobile Flutter validation. - -## Acceptance Criteria -- Focusing the composer input does not change the border color. -- `ChatColors` does not expose unused focus-border aliases. -- Validation commands pass. diff --git a/docs/plans/2026-04-27-00-51-+08-composer-action-color-alignment.md b/docs/plans/2026-04-27-00-51-+08-composer-action-color-alignment.md deleted file mode 100644 index fb21e2dc..00000000 --- a/docs/plans/2026-04-27-00-51-+08-composer-action-color-alignment.md +++ /dev/null @@ -1,20 +0,0 @@ -# Composer Action Color Alignment - -## Background -The composer bottom-row controls currently use mixed enabled colors in dark mode: slash, mention, tune, and idle send controls are gray, while active send is near-white. The desired visual direction is that clickable, non-disabled, non-accent controls use the same quiet white-family color unless intentionally highlighted. - -## Goals -- Align dark composer bottom action icon/text colors. -- Reuse the existing `AppColors.textPrimary` token without adding new tokens or hardcoded color values. -- Keep the composer controls quiet and avoid brand-blue emphasis. - -## Implementation Plan (phased) -1. Update dark `ChatColors.composerActionIdle` and `sendIdle` to map to `AppColors.textPrimary`. -2. Keep active send mapped to `AppColors.textPrimary` so enabled controls are visually consistent. -3. Update code-map smoke coverage for composer bottom controls. -4. Run formatting, focused composer tests, and mobile Flutter analysis. - -## Acceptance Criteria -- Slash, mention, tune, and idle send controls use the same dark enabled action color. -- Enabled controls reuse `AppColors.textPrimary`, not brand blue or hardcoded white. -- No new color token is introduced. diff --git a/docs/plans/2026-04-27-13-47-UTC-light-mode-color-adjustments.md b/docs/plans/2026-04-27-13-47-UTC-light-mode-color-adjustments.md deleted file mode 100644 index f93a1f63..00000000 --- a/docs/plans/2026-04-27-13-47-UTC-light-mode-color-adjustments.md +++ /dev/null @@ -1,28 +0,0 @@ -# Background -用户反馈亮色模式仍存在灰蓝倾向,且与目标视觉不一致。需要依据设计手册(`docs/kb/color-theme-architecture.md`)将亮色模式关键区域调整为中性白/黑体系:页面背景、用户消息气泡、以及“定位到最底部”圆形按钮。 - -# Goals -1. 亮色模式页面背景去蓝化,改为中性白系。 -2. 亮色模式用户气泡改为黑底白字,提升对比度并符合视觉要求。 -3. 亮色模式“定位到最新”圆形按钮改为白色系;暗色模式蓝色按钮保持不变。 -4. 不引入业务组件内硬编码色值,优先通过 design system token/主题层实现。 - -# Implementation Plan (phased) -## Phase 1: 主题与语义色调整 -- 在 `ChatColors.light` 中调整 `messageUserBackground` 与 `onMessageUser`,实现亮色模式黑底白字气泡。 -- 在 `BricksTheme.light()` 明确设置亮色 `scaffoldBackgroundColor` 与 `appBarTheme`,去除由 `colorSchemeSeed` 带来的灰蓝背景倾向。 - -## Phase 2: 定位按钮语义色调整 -- 通过 `ChatColors.jumpToLatestBackground` 与 `ChatColors.onJumpToLatest` 统一定义 jump-to-latest 按钮样式,不在消息列表组件内按 `Theme.brightness` 额外分支。 -- 在主题 token 层分别配置亮暗模式取值: - - Light: `jumpToLatestBackground` 为白色系,`onJumpToLatest` 为深色箭头/图标。 - - Dark: 保持现有蓝色主色填充按钮及对应前景色。 - -## Phase 3: 校验 -- 运行格式化和 Flutter 分析命令,确保代码风格与静态检查通过。 - -# Acceptance Criteria -1. 亮色模式聊天页面背景为中性白系,不再呈现灰蓝底感。 -2. 亮色模式右侧用户消息气泡呈现黑底白字,时间与状态图标在气泡上清晰可读。 -3. 亮色模式 jump-to-latest 按钮呈白色系;暗色模式该按钮继续为蓝色。 -4. `cd apps/mobile_chat_app && flutter analyze` 无错误。 diff --git a/docs/plans/2026-04-27-17-28-UTC-markdown-table-rendering.md b/docs/plans/2026-04-27-17-28-UTC-markdown-table-rendering.md deleted file mode 100644 index 6cadaef4..00000000 --- a/docs/plans/2026-04-27-17-28-UTC-markdown-table-rendering.md +++ /dev/null @@ -1,26 +0,0 @@ -# Background -The mobile chat assistant currently supports a lightweight markdown subset (headings, lists, code fences, block quotes, inline styles/links) but does not render markdown table syntax. As a result, table source text is shown literally in messages, which harms readability for structured answers. - -# Goals -- Render GitHub-style markdown tables (`|` row syntax + separator row) as visual table widgets in assistant messages. -- Keep existing markdown features and styles intact. -- Add regression coverage for table rendering in widget tests. - -# Implementation Plan (phased) -## Phase 1: Parser and renderer extension -- Extend `_AssistantMarkdownText` line-walking logic to detect table blocks before paragraph/list parsing. -- Add a `_MarkdownTable` helper that parses a header row + separator row + data rows and normalizes column counts. -- Render parsed tables with Flutter `Table` wrapped in horizontal scroll for narrow screens. - -## Phase 2: Validation and regression tests -- Add widget test coverage to verify table syntax is transformed into a rendered `Table` and cell text appears as expected. -- Ensure literal raw markdown table line text is no longer displayed for parsed table messages. - -## Phase 3: Verification commands -- Initialize repo dev environment with `./tools/init_dev_env.sh`. -- Run targeted mobile app tests from package directory: `cd apps/mobile_chat_app && flutter test test/message_list_test.dart`. - -# Acceptance Criteria -- Assistant messages containing markdown tables render visually as table UI, not literal raw `|`-delimited text. -- Existing markdown behaviors (headings/lists/code/quote) remain functional. -- New widget tests for markdown table rendering pass. diff --git a/docs/plans/2026-04-28-17-26-+08-bricks-issue-template-skill.md b/docs/plans/2026-04-28-17-26-+08-bricks-issue-template-skill.md deleted file mode 100644 index a23c0055..00000000 --- a/docs/plans/2026-04-28-17-26-+08-bricks-issue-template-skill.md +++ /dev/null @@ -1,26 +0,0 @@ -# Bricks Issue Template Skill - -## Background - -The repository has project-specific skills under `.agents/skills/`, but no GitHub issue template was found under `.github/ISSUE_TEMPLATE`. Product requirement discussions still need a consistent structure for issue drafts. - -## Goals - -- Add a repository skill that helps agents draft Bricks GitHub issues using a consistent product-facing structure. -- Include Context, Motivation, Requirement, and Acceptance Criteria sections. -- Require Acceptance Criteria to use GWT form. -- Keep implementation details out of the default issue body unless the user explicitly asks for them. - -## Implementation Plan (phased) - -1. Create `.agents/skills/bricks-issue-template/SKILL.md`. -2. Document the trigger conditions and issue structure. -3. Keep the skill concise and self-contained so it can be loaded cheaply. - -## Acceptance Criteria - -- The new skill exists at `.agents/skills/bricks-issue-template/SKILL.md`. -- The skill frontmatter includes a clear `name` and `description`. -- The skill instructs agents to use Context, Motivation, Requirement, and Acceptance Criteria. -- The skill requires GWT-style acceptance criteria. -- The default issue template avoids implementation suggestions unless requested. diff --git a/docs/plans/2026-04-28-23-49-+08-change-implementation-plan-skill.md b/docs/plans/2026-04-28-23-49-+08-change-implementation-plan-skill.md deleted file mode 100644 index c1466b29..00000000 --- a/docs/plans/2026-04-28-23-49-+08-change-implementation-plan-skill.md +++ /dev/null @@ -1,34 +0,0 @@ -# Change Implementation Plan Skill - -## Background - -The repository requires implementation plans to be persisted under `docs/plans/`, but the desired plan-writing structure has been refined through discussion. - -The plan format should support concise plans for small changes and more structured plans for larger work. `Context`, `Problem`, and `Motivation` should be available as optional Background subsections, not mandatory headings. - -## Goals - -- Add a reusable skill for implementation plan drafting and persistence. -- Require every implementation plan to be saved under `docs/plans/` with a timestamped filename. -- Standardize plan sections while keeping Background subsections optional. -- Keep validation commands in a dedicated section. - -## Implementation Plan - -1. Create `.agents/skills/change-implementation-plan/SKILL.md`. -2. Document the purpose of the skill as implementation planning for repository changes. -3. Define the default plan structure and optional Background subsection rules. -4. Capture persistence and validation-command requirements. - -## Acceptance Criteria - -- The skill exists at `.agents/skills/change-implementation-plan/SKILL.md`. -- The skill name does not include repository-specific branding. -- The skill description explains that it is for implementation plans for repository changes. -- The skill requires plans to be saved under `docs/plans/` with timestamped filenames. -- The skill defines `Context`, `Problem`, and `Motivation` as optional Background subsections. -- The skill requires `Validation Commands` as a dedicated section. - -## Validation Commands - -- `git status --short` diff --git a/docs/plans/2026-04-29-00-01-+08-issue-template-background-update.md b/docs/plans/2026-04-29-00-01-+08-issue-template-background-update.md deleted file mode 100644 index c4d2434a..00000000 --- a/docs/plans/2026-04-29-00-01-+08-issue-template-background-update.md +++ /dev/null @@ -1,32 +0,0 @@ -# Issue Template Background Update - -## Background - -The repository now has a dedicated issue drafting skill at `.agents/skills/bricks-issue-template/SKILL.md`. Its current template uses separate top-level `Context` and `Motivation` sections. - -The implementation plan skill uses a more flexible `Background` section with optional `Context`, `Problem`, and `Motivation` subsections. That structure is a better fit for issue drafting because not every issue needs separate top-level context and motivation sections. - -## Goals - -- Align the issue template's background structure with the plan template's flexible Background model. -- Replace top-level `Context` and `Motivation` issue sections with a single `Background` section. -- Allow `Context`, `Problem`, and `Motivation` as optional Background subsections. -- Preserve the issue template's `Requirement` and GWT-style `Acceptance Criteria` sections. - -## Implementation Plan - -1. Update `.agents/skills/bricks-issue-template/SKILL.md` frontmatter to describe the new Background-based structure. -2. Replace the default issue template's top-level `Context` and `Motivation` sections with `Background`. -3. Document optional `Context`, `Problem`, and `Motivation` subsections under `Background`. -4. Update writing rules and checklist language to reference `Background`. - -## Acceptance Criteria - -- The issue template skill uses `Background`, `Requirement`, and `Acceptance Criteria` as the default top-level issue sections. -- The skill no longer requires top-level `Context` or `Motivation` sections. -- The skill allows optional `Context`, `Problem`, and `Motivation` subsections under `Background`. -- The skill still requires GWT-style acceptance criteria. - -## Validation Commands - -- `git status --short` diff --git a/docs/plans/2026-04-29-16-22-UTC-chat-appbar-channel-section-menu.md b/docs/plans/2026-04-29-16-22-UTC-chat-appbar-channel-section-menu.md deleted file mode 100644 index dc6d05f9..00000000 --- a/docs/plans/2026-04-29-16-22-UTC-chat-appbar-channel-section-menu.md +++ /dev/null @@ -1,21 +0,0 @@ -# Background -The chat screen AppBar currently uses the title dropdown to switch sub-sections and uses a right-side three-dot menu for section management. Product now needs the title dropdown to switch channels, and section switching/management should move into a dedicated AppBar menu near the title. - -# Goals -- Make the AppBar title dropdown switch channels. -- Add a section menu in the AppBar that is left-aligned near the channel title. -- Convert the previous three-dot control into a section-name dropdown with grouped menu content. -- Keep section list synchronized with the active channel after channel switching. - -# Implementation Plan (phased) -1. Update the title PopupMenuButton to list channels and call `_switchChannel`. -2. Replace the right-side three-dot actions menu with an inline section dropdown button rendered next to the channel title. -3. Build section dropdown groups in this order: action items (new sub-section, rename placeholder, archive placeholder), divider, section navigation list. -4. Ensure displayed section label and menu list resolve from `_activeSubSection` and `_activeSubSections` so they refresh naturally after `_switchChannel`. - -# Acceptance Criteria -- Clicking AppBar channel title opens a channel list and selecting one switches active channel. -- A new section dropdown appears near the channel title (not on the right edge). -- Section dropdown shows a function group first, then a divider, then the section list group. -- Existing placeholder actions for section rename/archive remain non-functional and show the existing "功能暂未实现" feedback. -- When channel changes, section label and section list reflect the target channel. diff --git a/docs/plans/2026-04-29-16-29-UTC-pc-sidebar-inline-navigation.md b/docs/plans/2026-04-29-16-29-UTC-pc-sidebar-inline-navigation.md deleted file mode 100644 index b73a91e2..00000000 --- a/docs/plans/2026-04-29-16-29-UTC-pc-sidebar-inline-navigation.md +++ /dev/null @@ -1,24 +0,0 @@ -# Background -当前 `ChatScreen` 在 PC/宽屏下点击左上角 navigation 按钮会打开 `Drawer` 浮层。需求是仅修改 PC 行为:打开后显示为常驻左侧边栏,并与右侧对话区并存;折叠后恢复隐藏。移动端行为保持不变。 - -# Goals -- 仅在宽屏(非 compact)下把 navigation 改为“内联侧边栏 + 右侧聊天区”的并存布局。 -- 保持移动端 `Drawer` 交互不变。 -- 保持原有 `ChatNavigationPage` 功能与主题样式一致。 - -# Implementation Plan (phased) -1. 在 `ChatScreen` 新增桌面侧边栏展开状态(默认折叠)。 -2. 提取侧边导航内容构建函数,避免 `Drawer` 与桌面内联侧边栏重复实现。 -3. 构建响应式布局: - - mobile/compact:继续使用 `Scaffold.drawer` + `openDrawer()`。 - - desktop/non-compact:`Scaffold.body` 使用 `Row`,左侧按状态显示固定宽度导航,右侧 `Expanded` 承载聊天区域。 -4. 更新代码地图中 chat_session 的 smoke check/关键词,补充“宽屏侧边栏内联并存”行为。 - -# Acceptance Criteria -- 在宽屏下点击导航按钮后,左侧导航以内联侧边栏形式出现,聊天区被挤到右侧并继续可用。 -- 在宽屏下再次点击导航按钮后,左侧导航隐藏。 -- 在移动端下导航仍通过抽屉浮层打开,不受本次改动影响。 - -# Validation Commands -- `./tools/init_dev_env.sh` -- `cd apps/mobile_chat_app && flutter test test/chat_navigation_page_test.dart` diff --git a/docs/plans/2026-04-30-17-06-+08-desktop-channel-navigation-stays-open.md b/docs/plans/2026-04-30-17-06-+08-desktop-channel-navigation-stays-open.md deleted file mode 100644 index ad6e5a5d..00000000 --- a/docs/plans/2026-04-30-17-06-+08-desktop-channel-navigation-stays-open.md +++ /dev/null @@ -1,33 +0,0 @@ -# Desktop Channel Navigation Stays Open - -## Background - -The chat navigation page is reused for both compact mobile drawers and wide desktop inline navigation. Channel taps currently request navigation close before switching channels. That is correct on compact screens, where the drawer covers the conversation, but it is not correct on wide screens where the inline navigation pushes the conversation area to the right and should remain available for repeated channel switching. - -## Goals - -- Keep compact drawer behavior unchanged: tapping a channel closes the drawer. -- Keep wide inline navigation open when a channel is selected. -- Preserve existing close behavior for explicit navigation close actions such as the back arrow and settings/actions. -- Cover the behavior with focused widget tests. - -## Implementation Plan - -1. Add an explicit channel-selection close policy to `ChatNavigationPage`. -2. Use the default close-on-channel-select policy for mobile drawer usage. -3. Disable close-on-channel-select for the desktop inline navigation call site. -4. Add widget tests for both default close behavior and desktop-style non-closing channel selection. -5. Update code maps if the navigation behavior index needs to reflect the new wide-screen behavior. - -## Acceptance Criteria - -- On compact/mobile navigation, tapping a channel closes the drawer and switches the selected channel. -- On wide/desktop inline navigation, tapping a channel switches the selected channel without closing the navigation panel. -- The explicit close control still closes wide/desktop inline navigation. -- Existing channel rename/archive behavior is unchanged. - -## Validation Commands - -- `./tools/init_dev_env.sh` -- `cd apps/mobile_chat_app && flutter test test/chat_navigation_page_test.dart` -- `cd apps/mobile_chat_app && flutter analyze` diff --git a/docs/plans/2026-04-30-17-51-+08-scope-instructions-config.md b/docs/plans/2026-04-30-17-51-+08-scope-instructions-config.md deleted file mode 100644 index 5820b199..00000000 --- a/docs/plans/2026-04-30-17-51-+08-scope-instructions-config.md +++ /dev/null @@ -1,49 +0,0 @@ -# Scope Instructions Config - -## Background - -The chat screen currently has multiple conversation scopes: a channel and, when selected, a sub-section under that channel. A channel can define broad context, while a sub-section can narrow the current topic further. Prompt-style configuration should therefore belong to the active conversation scope model rather than only to channels. - -The entry point should not be placed inside the channel switcher, because the action configures the current chat context and may include both channel-level and sub-section-level settings. A stable action in the conversation area's top-right controls better communicates that this is a configuration action for the current chat view. - -## Goals - -- Add a top-right conversation config action in the chat area. -- Provide a config UI with tabs for channel configuration and current sub-section configuration. -- Allow channel-level `Instructions` to describe the broad context/topic for the channel. -- Allow sub-section-level `Instructions` to describe the narrower context/topic for the current sub-section. -- Hide or disable sub-section configuration when the current sub-section is the main section. -- Apply the relevant instructions to default-route chat requests without replacing the selected Agent prompt. -- Keep OpenClaw routing behavior unchanged. - -## Implementation Plan - -1. Add a conversation config action to the chat app bar's right-side controls, separate from the channel dropdown and sub-section dropdown. -2. Build a configuration dialog or sheet with two tabs: `Channel` and `Current Section`. -3. In the `Channel` tab, show an `Instructions` text field for the active channel with helper copy explaining that it sets context and customizes what target/topic the channel discusses. -4. In the `Current Section` tab, show an `Instructions` text field only when the active section is a sub-section; for the main section, show a non-editable state explaining that main uses channel instructions only. -5. Extend chat scope settings persistence so both `channel` scope rows and `thread`/sub-section scope rows can store optional instructions independently of router and node settings. -6. Hydrate channel and sub-section instructions in the Flutter chat client, save edits optimistically with rollback on failure, and preserve existing router/node configuration when instructions are saved or cleared. -7. Compose runtime default-route prompts from the selected Agent prompt plus active channel instructions plus active sub-section instructions, with sub-section instructions acting as additional narrower context. -8. Add focused backend and Flutter tests for persistence, hydration, request payloads, and prompt composition behavior. -9. Update code maps because this changes chat feature entry points and chat request logic. - -## Acceptance Criteria - -- Given the user is in any chat scope, when they look at the conversation area's top-right controls, then they can open a config action that is not part of the channel dropdown. -- Given the config UI is open, when the active scope is any channel, then the `Channel` tab lets the user edit and save channel `Instructions`. -- Given the config UI is open on the main section, when the user opens the section tab, then section instructions are not editable and the UI indicates that the main section uses channel instructions only. -- Given the config UI is open on a sub-section, when the user opens the section tab, then they can edit and save instructions for that current sub-section. -- Given saved channel instructions exist, when the app reloads, then the channel tab restores the saved value. -- Given saved sub-section instructions exist, when the user returns to that sub-section, then the section tab restores the saved value. -- Given both channel and sub-section instructions exist, when a default-route message is sent from that sub-section, then the request includes Agent prompt context plus both instruction scopes. -- Given only channel instructions exist, when a default-route message is sent from the main section, then the request includes Agent prompt context plus channel instructions. -- Given instructions are cleared, when the user saves and reloads, then the cleared scope no longer contributes prompt context. -- Given a scope uses OpenClaw routing, when instructions are saved or messages are sent, then OpenClaw routing and node selection behavior remains unchanged. - -## Validation Commands - -- `./tools/init_dev_env.sh` -- `npm test -- --runInBand` -- `cd apps/mobile_chat_app && flutter test test/chat_history_api_service_test.dart` -- `cd apps/mobile_chat_app && flutter analyze` diff --git a/docs/plans/2026-05-06-10-13-UTC-default-router-agent-architecture.md b/docs/plans/2026-05-06-10-13-UTC-default-router-agent-architecture.md deleted file mode 100644 index ba7c9f01..00000000 --- a/docs/plans/2026-05-06-10-13-UTC-default-router-agent-architecture.md +++ /dev/null @@ -1,57 +0,0 @@ -# Background - -In the current chat routing model, the `openclaw` router can bind to a concrete node (`nodeId`) and carries agent-style semantics through that node. By contrast, the `default` router mostly follows built-in logic (the default branch in `/api/chat/respond`) and is not modeled as a first-class node/agent entity. - -This creates product-level inconsistencies: - -- Channel and thread instructions can be saved, but they are not represented consistently under a unified “router == agent target” model. -- Observability metadata for default routing is not aligned between frontend and backend. -- Future AI-assisted instruction management (inheritance, overrides, auditing) is constrained by the lack of default-router identity. - -The target is to make the default router equivalent to a built-in agent node: always available, no import, no plugin installation. - -# Goals - -1. Model the default router as a built-in virtual node with stable ID and display name. -2. Preserve current UX: every user has it by default, with no plugin setup. -3. Align expressiveness with OpenClaw node routing to support future AI instruction editing for channels/threads. -4. Keep existing OpenClaw routing/plugin behavior intact. - -# Implementation Plan (phased) - -## Phase 1: Domain model alignment (Backend) - -1. Define built-in default node constants (e.g. `node_builtin_default`) and source platform (e.g. `builtin`). -2. Introduce a unified resolved target shape in routing resolution: - - `router` - - `nodeId` - - `nodeKind` (`builtin_default` / `platform_openclaw`) - - `agentName` -3. In `/api/chat/respond`, emit consistent user/assistant metadata (including `targetNodeId/targetNodeName`) for both default and OpenClaw. - -## Phase 2: Config and presentation alignment (Config + Chat UI) - -1. Add a read-only built-in default node to node-list APIs (not renameable or deletable). -2. Present default in the route picker with node-style UI (e.g. “Bricks Default (Built-in)”), while still being always available. -3. Keep existing channel/thread instruction editors; optionally return `resolvedNodeId` from scope-setting reads (built-in node ID for default). - -## Phase 3: Instruction governance - -1. Add explicit inheritance/override semantics: global(agent) → channel → thread. -2. Add service endpoints for AI-assisted instruction edits (default first, then OpenClaw). -3. Add instruction audit metadata (who changed, when, scope, before/after diff summary). - -# Acceptance Criteria - -1. Without any OpenClaw node configured, users can still use default chat and routing resolves to a stable default-node identity. -2. Channel and thread instructions continue to save/read and apply correctly under default routing. -3. Default appears as a built-in node in routing UI without adding install/import steps. -4. OpenClaw routing behavior and plugin flows do not regress. -5. Chat message metadata exposes unified target fields across default/OpenClaw (`targetNodeId/targetNodeName`). - -# Validation Commands - -- `./tools/init_dev_env.sh` -- `cd apps/node_backend && npm test` -- `cd apps/mobile_chat_app && flutter test` -- `cd apps/mobile_chat_app && flutter analyze` diff --git a/docs/plans/2026-05-06-11-03-UTC-openclaw-default-router-agent-benchmark.md b/docs/plans/2026-05-06-11-03-UTC-openclaw-default-router-agent-benchmark.md deleted file mode 100644 index ad093939..00000000 --- a/docs/plans/2026-05-06-11-03-UTC-openclaw-default-router-agent-benchmark.md +++ /dev/null @@ -1,105 +0,0 @@ -# Background - -You requested that before upgrading Bricks default router into an agent-equivalent node architecture, we should first study OpenClaw (especially OpenClaw + Discord) in depth, including technology choices, architecture, and prompt design, then produce an implementation-ready plan. - -Based on repository/documentation research of `openclaw/openclaw`, this document converts benchmark findings into a practical Bricks migration blueprint. - -# Goals - -1. Extract key OpenClaw mechanisms for control plane, session routing, agent identity, and prompt assembly. -2. Map those findings to current Bricks gaps and define what default router must gain. -3. Produce a phased implementation plan for built-in default-node semantics and AI-governed channel/thread instructions. - -# OpenClaw Findings (fact extraction) - -## 1) Technology choices (control plane + channel plugins) - -- OpenClaw uses a **single long-lived Gateway daemon + typed WebSocket protocol** as a unified control plane. -- All messaging surfaces (including Discord) attach to the Gateway; clients and nodes connect through the same WS surface, separated by role/capability. -- Discord integration follows official gateway + bot token + account-aware config and behaves as a channel plugin surface. - -Implication for Bricks: default router should not remain an `if/else` branch; it should be a first-class routing target inside the control-plane model. - -## 2) Architecture (routing as a first-class object) - -- OpenClaw is driven by **session route metadata + stable agent identity**, not raw provider passthrough. -- Different channels (Discord/Telegram/etc.) converge on consistent `agent::...` semantics. -- Channel ingress and agent execution are decoupled but composable. - -Implication for Bricks: - -- default router needs stable built-in `nodeId/agentId` included in resolved routing. -- scope settings should return resolved target semantics, not only a router string. - -## 3) Prompt design (OpenClaw-owned prompt assembly) - -- OpenClaw uses a **platform-owned system prompt assembly** model, not transparent passthrough. -- Prompt sections are structured with cache-stable prefixes and runtime-dynamic suffixes. -- Workspace bootstrap files (`AGENTS.md`, `SOUL.md`, `TOOLS.md`, etc.) are injected context; sub-agents use smaller prompt modes. - -Implication for Bricks: - -- default router agentization should move to layered prompt assembly: - - agent layer (default built-in) - - channel layer - - thread layer -- explicit governance is required: inheritance, override, truncation, audit. - -## 4) Why OpenClaw + Discord feels “agent-native” - -- Discord is only the ingress/egress surface; the durable layer is agent/session routing inside Gateway. -- What users perceive as editable instructions, continuity, and cross-channel consistency comes from the unified agent abstraction. - -Implication for Bricks: - -- the correct target is “default router as built-in agent node,” not “default emulating a plugin.” - -# Implementation Plan (phased) - -## Phase 1: Default-router node identity (Backend first) - -1. Introduce built-in default node constants: - - `nodeId: node_builtin_default` - - `sourcePlatform: builtin` - - `displayName: Bricks Default` -2. Extend resolved routing output: - - `router`, `nodeId`, `nodeKind`, `agentName`, `sourcePlatform` -3. In `/api/chat/respond`, emit unified metadata for both default/openclaw: - - `targetNodeId`, `targetNodeName`, `targetPluginId?`, `resolvedRouteKind` - -## Phase 2: Scope setting + UI alignment - -1. Add `resolvedNodeId/resolvedNodeName` to scope-setting query responses. -2. Show default as a built-in node in router menus (no install, read-only). -3. For thread-follow-channel mode, return resolved target semantics to avoid ambiguous inheritance. - -## Phase 3: Prompt assembly + instruction governance - -1. Build a layered prompt assembler: - - base system - - built-in agent instructions - - channel instructions - - thread instructions -2. Add AI instruction-edit service paths (default first, OpenClaw later). -3. Add instruction audit fields: `changedBy`, `changedAt`, `scope`, `diffSummary`. - -## Phase 4: OpenClaw interoperability boundaries - -1. Keep built-in default node independent of plugin lifecycle. -2. Keep OpenClaw node behavior on existing plugin path. -3. Standardize observability fields for mixed-router troubleshooting. - -# Acceptance Criteria - -1. With no OpenClaw nodes configured, default remains usable with stable node identity. -2. Channel/thread instructions inherit/override correctly under default and participate in prompt assembly. -3. UI shows default as built-in node with no import/install workflow. -4. OpenClaw async placeholder/backfill path remains unchanged. -5. In mixed routing (default ↔ openclaw), metadata fields remain consistently observable. - -# Validation Commands - -- `./tools/init_dev_env.sh` -- `cd apps/node_backend && npm test` -- `cd apps/mobile_chat_app && flutter test` -- `cd apps/mobile_chat_app && flutter analyze` diff --git a/docs/plans/2026-05-06-11-14-UTC-default-router-builtin-node-coding.md b/docs/plans/2026-05-06-11-14-UTC-default-router-builtin-node-coding.md deleted file mode 100644 index 5eda81ae..00000000 --- a/docs/plans/2026-05-06-11-14-UTC-default-router-builtin-node-coding.md +++ /dev/null @@ -1,36 +0,0 @@ -# Background - -The request moved from planning to coding: implement built-in agent/node semantics for the default router, not only documentation. - -# Goals - -1. Introduce a stable built-in node identity for default router on backend. -2. Emit unified route-target metadata in `/api/chat/respond` for default and OpenClaw. -3. Validate no regression through focused backend tests. - -# Implementation Plan (phased) - -## Phase 1 -- Add built-in default node constants and helper in `chatRouterService`. - -## Phase 2 -- Unify metadata output in chat respond path: - - `targetNodeId` - - `targetNodeName` - - `targetSourcePlatform` - - `resolvedRouteKind` - -## Phase 3 -- Update and pass related Vitest suites: - - `src/routes/chat.test.ts` - - `src/services/chatRouterService.test.ts` - -# Acceptance Criteria - -1. Default route writes stable `node_builtin_default` metadata. -2. OpenClaw route continues to write real node metadata. -3. Related tests pass. - -# Validation Commands - -- `cd apps/node_backend && npm test -- --run src/routes/chat.test.ts src/services/chatRouterService.test.ts` diff --git a/docs/plans/2026-05-06-22-09-+08-branch-feature-checklist-analysis.md b/docs/plans/2026-05-06-22-09-+08-branch-feature-checklist-analysis.md deleted file mode 100644 index f5504d8d..00000000 --- a/docs/plans/2026-05-06-22-09-+08-branch-feature-checklist-analysis.md +++ /dev/null @@ -1,32 +0,0 @@ -# Branch Feature Checklist Analysis - -## Background - -The current branch needs to be compared with `main` to identify the planned feature scope, determine which implementation pieces already exist, and list the remaining work. - -## Goals - -- Read branch-added plan files and summarize the intended feature scope. -- Compare branch changes against `main`. -- Map planned acceptance criteria to current code and tests. -- Produce a checklist of completed and remaining work without changing feature behavior. - -## Implementation Plan - -1. Fetch `origin/main`, then compare `origin/main...HEAD` to identify changed files and branch-added plans. -2. Read the added plan files under `docs/plans/`. -3. Inspect backend, Flutter, migration, test, and code-map diffs. -4. Report completed, partial, missing, and risky items with concrete file references. - -## Acceptance Criteria - -- The analysis identifies all branch-added plan files. -- The checklist separates completed work from remaining work. -- The checklist calls out implementation risks where code behavior does not meet the plan. -- No product behavior is changed by this analysis task. - -## Validation Commands - -- `git fetch origin main` -- `git diff --name-status origin/main...HEAD` -- `git diff --stat origin/main...HEAD` diff --git a/docs/plans/2026-05-06-22-42-+08-router-strategy-rename.md b/docs/plans/2026-05-06-22-42-+08-router-strategy-rename.md deleted file mode 100644 index f16ed3fa..00000000 --- a/docs/plans/2026-05-06-22-42-+08-router-strategy-rename.md +++ /dev/null @@ -1,58 +0,0 @@ -# Router Strategy Rename - -## Background - -The current chat routing model uses `router: default | openclaw`. This mixes two concepts: dispatch strategy and concrete external platform. `default` currently means Bricks handles the response locally, while `openclaw` means the message is dispatched to a plugin-backed remote node. Future plugin-backed platforms such as Hermes should use the same remote plugin dispatch strategy without adding platform names to the router enum. - -The branch also adds metadata fields for default-router built-in node identity. The next step should simplify those metadata fields while preserving current business behavior. - -## Goals - -- Rename router semantics from platform-specific values to dispatch-strategy values. -- Use `local` for Bricks-managed direct execution and `plugin` for plugin-backed remote dispatch. -- Keep concrete platform identity, such as OpenClaw or future Hermes, on platform node/plugin configuration instead of the router enum. -- Preserve existing default and OpenClaw behavior during migration. -- Remove redundant target metadata fields that duplicate router semantics. - -## Implementation Plan - -1. Define the target router vocabulary: - - Canonical `ChatRouter` values become `local` and `plugin`. - - Legacy input values `default` and `openclaw` remain accepted during migration and normalize to `local` and `plugin`. -2. Update backend router parsing and persistence: - - Normalize `default -> local` and `openclaw -> plugin` at API boundaries. - - Continue to support existing database rows during read/resolve until a migration is applied. - - Add or update migration logic for stored `chat_scope_settings.router` values. -3. Simplify chat respond target metadata: - - Keep `targetNodeId`, `targetNodeName`, and `targetPluginId`. - - Remove `targetSourcePlatform` and `resolvedRouteKind` from newly written metadata. - - Do not add `targetNodeKind` unless a future router maps to multiple target node categories. -4. Keep platform identity in node/plugin records: - - OpenClaw-specific behavior remains attached to platform node/plugin configuration. - - Future Hermes nodes should also use `router: plugin` and differ by plugin/platform metadata, not router enum. -5. Update Flutter router models and API payloads: - - Rename UI enum values to local/plugin semantics. - - Preserve labels such as `Bricks Default` and `OpenClaw` at presentation boundaries. - - Keep thread inheritance behavior unchanged. -6. Update tests and code maps: - - Backend tests should assert `local/plugin` router behavior and metadata shape. - - Flutter tests should assert legacy parsing compatibility and canonical outgoing values. - - Update `docs/code_maps/feature_map.yaml` and `docs/code_maps/logic_map.yaml` because this changes chat routing business logic and API contracts. - -## Acceptance Criteria - -- Existing saved `default` scope settings resolve as `local` without changing user-visible behavior. -- Existing saved `openclaw` scope settings resolve as `plugin` and still dispatch to the configured OpenClaw node. -- New scope-setting writes use canonical `local` or `plugin` router values. -- Default Bricks chat still responds through local backend execution. -- OpenClaw chat still creates the dispatch placeholder and waits for plugin-backed remote handling. -- Chat message metadata still includes target node identity, but newly written messages no longer include `targetSourcePlatform` or `resolvedRouteKind`. -- Future plugin-backed platforms can reuse `router: plugin` without adding a new router enum value. -- Code maps are updated for the routing contract change. - -## Validation Commands - -- `./tools/init_dev_env.sh` -- `cd apps/node_backend && npm test -- --run src/routes/chat.test.ts src/services/chatRouterService.test.ts` -- `cd apps/mobile_chat_app && flutter test test/chat_history_api_service_test.dart test/chat_topology_and_task_protocol_test.dart` -- `cd apps/mobile_chat_app && flutter analyze` diff --git a/docs/plans/2026-05-06-22-44-+08-post-router-refactor-followup.md b/docs/plans/2026-05-06-22-44-+08-post-router-refactor-followup.md deleted file mode 100644 index 22c940c6..00000000 --- a/docs/plans/2026-05-06-22-44-+08-post-router-refactor-followup.md +++ /dev/null @@ -1,74 +0,0 @@ -# Post Router Refactor Follow-up - -## Background - -The router model should first be clarified separately so that `router` means dispatch strategy only: `local` for Bricks-managed direct execution and `plugin` for plugin-backed remote dispatch. This plan assumes that router refactor is already complete and does not include the router rename or migration work. - -After that refactor, the remaining feature work is not to add another router kind. The remaining work is to make Bricks local execution and plugin-backed execution share a clearer target model, expose built-in Bricks targets consistently, and prepare instruction governance without coupling those concepts to OpenClaw-specific names. - -## Goals - -- Expose Bricks built-in local targets consistently in backend and frontend. -- Keep plugin platform identity outside the router enum. -- Preserve current local Bricks response behavior and plugin/OpenClaw dispatch behavior. -- Make scope settings and target metadata easier to consume after router semantics are clarified. -- Continue instruction governance work without changing the already implemented channel/thread instructions behavior. - -## Implementation Plan - -1. Define the built-in Bricks target model after router refactor. - - Keep `node_builtin_default` as the stable built-in target id. - - Keep `Bricks Default` as the display name. - - Treat this as the default local execution target, not as a separate router value. - - Add a compact shared type/helper for local target identity if needed. - -2. Normalize target metadata after router refactor. - - Newly written chat message metadata should include `targetNodeId`, `targetNodeName`, and `targetPluginId`. - - Do not reintroduce `targetSourcePlatform`, `resolvedRouteKind`, or `targetNodeKind` unless a later feature creates a real need. - - Ensure local responses write the built-in target id/name and plugin responses write the plugin node id/name/plugin id. - -3. Expose built-in Bricks target in target-selection surfaces. - - Backend node/target listing should make the built-in local target available as a read-only target. - - Frontend route/target menus should present `Bricks Default` as the local target and plugin nodes separately. - - The UI should still label concrete plugin nodes by their display name and plugin platform where useful, for example OpenClaw. - -4. Align scope-setting responses with resolved target semantics. - - Scope-setting reads should keep the persisted router strategy and node id. - - Where the UI needs display-ready information, return or derive the resolved target id/name after thread inheritance and fallback. - - For thread-follow-channel mode, make the effective target unambiguous without storing duplicate thread settings. - -5. Preserve existing instruction behavior and layer future governance on top. - - Channel and section instructions already save, hydrate, and apply to local/default responses. - - Keep that behavior unchanged. - - Add a future-ready prompt assembly boundary for local Bricks execution: base system prompt, selected agent prompt, channel instructions, section instructions. - - Do not apply local prompt assembly to plugin dispatch unless plugin-specific support is explicitly designed. - -6. Add instruction governance as a separate capability. - - Define inheritance/override semantics for local execution: built-in target defaults -> channel -> section. - - Add audit fields or audit events for instruction changes. - - Add AI-assisted instruction edit endpoints only after the storage and audit model is clear. - -7. Update tests and code maps. - - Add backend tests for built-in target listing/read-only behavior and metadata shape. - - Add frontend tests for target menu presentation and legacy hydrated scope behavior after router refactor. - - Add tests for effective target resolution with thread inheritance and missing plugin nodes. - - Update `docs/code_maps/feature_map.yaml` and `docs/code_maps/logic_map.yaml` because target resolution and chat routing entry points are affected. - -## Acceptance Criteria - -- Bricks local execution still works with no plugin nodes configured. -- Plugin/OpenClaw dispatch still works when a valid plugin node is configured. -- When a plugin node is missing, existing fallback/error behavior remains unchanged. -- New chat message metadata records the actual target id/name/plugin id without redundant route-kind/source-platform fields. -- The built-in Bricks target is visible where users choose or inspect chat targets, but it is read-only and requires no import/install flow. -- Thread-follow-channel target resolution is clear to the UI and does not require duplicated settings. -- Existing channel and section instructions continue to save, hydrate, clear, and apply to local Bricks responses. -- Instruction governance is introduced behind clear storage/audit semantics, not mixed into router naming. -- Code maps are updated after implementation. - -## Validation Commands - -- `./tools/init_dev_env.sh` -- `cd apps/node_backend && npm test -- --run src/routes/chat.test.ts src/routes/config.test.ts src/services/chatRouterService.test.ts` -- `cd apps/mobile_chat_app && flutter test test/chat_history_api_service_test.dart test/chat_topology_and_task_protocol_test.dart` -- `cd apps/mobile_chat_app && flutter analyze` diff --git a/docs/plans/2026-05-07-18-29-fix-ios-photo-library-usage-description.md b/docs/plans/2026-05-07-18-29-fix-ios-photo-library-usage-description.md deleted file mode 100644 index be739369..00000000 --- a/docs/plans/2026-05-07-18-29-fix-ios-photo-library-usage-description.md +++ /dev/null @@ -1,15 +0,0 @@ -# Background -Apple rejected iOS build 1 (version 0.1.0) for missing `NSPhotoLibraryUsageDescription` in the Runner app Info.plist. - -# Goals -- Add the required privacy usage string so App Store validation passes ITMS-90683. -- Keep the change minimal and user-facing text clear. - -# Implementation Plan (phased) -1. Inspect iOS Runner Info.plist for existing privacy keys. -2. Add `NSPhotoLibraryUsageDescription` with a clear purpose string. -3. Run a lightweight repo check (git diff) to verify only intended files changed. - -# Acceptance Criteria -- `apps/mobile_chat_app/ios/Runner/Info.plist` contains `NSPhotoLibraryUsageDescription` with a non-empty, user-facing explanation. -- Diff contains only the intended privacy key addition and task plan documentation. diff --git a/docs/plans/2026-05-08-08-43-UTC-composer-focus-keep.md b/docs/plans/2026-05-08-08-43-UTC-composer-focus-keep.md deleted file mode 100644 index e7c88455..00000000 --- a/docs/plans/2026-05-08-08-43-UTC-composer-focus-keep.md +++ /dev/null @@ -1,22 +0,0 @@ -# Background -用户反馈聊天输入框在发送消息(点击发送按钮或按回车)后会丢失焦点,导致无法连续输入下一条消息,影响高频对话体验。 - -# Goals -- 修复发送后输入框失焦问题。 -- 保持现有发送逻辑与流式状态控制不被破坏。 -- 增加回归测试,覆盖“不可发送但可继续输入”的行为。 - -# Implementation Plan (phased) -1. 检查 `ComposerBar` 的输入框可用性与提交后状态切换逻辑,定位导致失焦的条件。 -2. 调整输入框 `enabled` 条件,确保发送动作触发后在非流式阶段仍可保持可输入状态。 -3. 新增/更新 widget test,验证在 `onSend == null`(发送不可用)但非流式时输入框仍可编辑。 -4. 运行相关测试命令并记录结果。 - -# Acceptance Criteria -- 点击发送按钮或按回车发送后,输入框仍可保持焦点并继续输入。 -- 在 `isStreaming == false` 时,即便 `onSend == null`,输入框仍可输入文本。 -- 现有 `composer_bar_test` 全部通过。 - -# Validation Commands -- `./tools/init_dev_env.sh` -- `cd apps/mobile_chat_app && flutter test test/composer_bar_test.dart` diff --git a/docs/plans/2026-05-08-09-44-UTC-fix-channel-name-refresh-loss.md b/docs/plans/2026-05-08-09-44-UTC-fix-channel-name-refresh-loss.md deleted file mode 100644 index 29ea74dc..00000000 --- a/docs/plans/2026-05-08-09-44-UTC-fix-channel-name-refresh-loss.md +++ /dev/null @@ -1,18 +0,0 @@ -# Background -Users reported that when creating a channel and entering a custom channel name, refreshing the page causes that channel name to be lost. - -# Goals -- Persist the channel display name at channel creation time so a page refresh restores the custom name. -- Keep behavior consistent with channel rename persistence. - -# Implementation Plan (phased) -1. Inspect channel creation flow in `chat_screen.dart` and confirm whether `saveChannelName` is called when creating channels. -2. Update channel creation logic to persist the newly entered name through `ChatHistoryApiService.saveChannelName` when an auth token is available. -3. Run focused static checks/tests relevant to the modified file. - -# Acceptance Criteria -- Creating a channel with a custom name triggers persistence of that name via the chat history API when authenticated. -- After refresh/reload, the channel retains the user-provided name rather than reverting. - -# Validation Commands -- `cd apps/mobile_chat_app && flutter test test/chat_navigation_page_test.dart` diff --git a/docs/plans/2026-05-08-13-46-UTC-disable-navigation-tab-swipe.md b/docs/plans/2026-05-08-13-46-UTC-disable-navigation-tab-swipe.md deleted file mode 100644 index a769f823..00000000 --- a/docs/plans/2026-05-08-13-46-UTC-disable-navigation-tab-swipe.md +++ /dev/null @@ -1,26 +0,0 @@ -# Background -The chat navigation panel currently contains two tabs (Channels and Nodes). A horizontal swipe inside the panel switches tabs by default via `TabBarView`, which conflicts with the desired UX for drawer-like navigation. - -# Goals -- Prevent horizontal swipe from switching tabs inside chat navigation. -- Make a right-to-left swipe in the navigation trigger close behavior. -- Preserve existing tab switching via tab taps. -- Add/adjust tests for the new gesture behavior. - -# Implementation Plan (phased) -1. Update `ChatNavigationPage` gesture handling: - - Disable tab paging gestures in `TabBarView`. - - Add a horizontal drag-end handler that closes navigation on right-to-left swipe. -2. Add widget tests: - - Verify horizontal swipe no longer switches tabs. - - Verify right-to-left swipe requests close. -3. Run targeted Flutter tests for `chat_navigation_page_test.dart`. - -# Acceptance Criteria -- Swiping horizontally inside navigation no longer changes active tab. -- Right-to-left swipe on navigation triggers close callback. -- Existing interactions (tab taps, back button, channel tap logic) continue to work. - -# Validation Commands -- `./tools/init_dev_env.sh` -- `cd apps/mobile_chat_app && flutter test test/chat_navigation_page_test.dart` diff --git a/docs/plans/2026-05-09-05-45-streaming-scroll-ux-plan.md b/docs/plans/2026-05-09-05-45-streaming-scroll-ux-plan.md deleted file mode 100644 index 3bfab457..00000000 --- a/docs/plans/2026-05-09-05-45-streaming-scroll-ux-plan.md +++ /dev/null @@ -1,24 +0,0 @@ -# Background -当前 `MessageList` 在消息列表发生变化时,会重新聚焦“最新用户消息”。在流式输出阶段,若尾消息标识变化(例如 messageId 补齐或快照字段变化),会触发再次滚动,造成可视区域被不断“往上顶”,底部出现大面积空白,影响阅读体验。 - -# Goals -- 发送消息瞬间:自动将“最新用户消息”定位到靠上位置(保持现有对齐策略)。 -- 流式输出阶段:不再因 assistant 增量输出强制滚动。 -- 用户手动滚动后:不被后续 assistant 输出干预。 -- 页面初次加载/刷新后:仍自动定位到最后一个用户问题。 - -# Implementation Plan (phased) -1. 调整 `MessageList.didUpdateWidget` 的自动滚动触发条件。 - - 仅在“新增的尾消息是 user(用户刚发送)”时触发自动聚焦。 - - 保留 `initState` 的初次定位逻辑,用于页面刷新后的自动定位。 -2. 补充/调整 `message_list_test.dart`。 - - 新增测试:assistant 新消息追加时不应改变当前滚动位置。 - - 保留并验证:streaming 同尾增量时不重滚动。 -3. 运行目标测试确保行为符合预期。 - -# Acceptance Criteria -- 当用户发送一条新消息时,列表自动将该用户消息定位到可视区靠上位置。 -- 当 assistant 流式输出持续更新时,列表位置保持不变,除非用户主动点击“Jump to latest”。 -- 页面首次进入(含刷新)时,仍自动定位到最后一个用户消息。 -- 相关 widget tests 通过。 - diff --git a/docs/plans/2026-05-09-06-10-UTC-chat-navigation-swipe-follow-up.md b/docs/plans/2026-05-09-06-10-UTC-chat-navigation-swipe-follow-up.md deleted file mode 100644 index 5f0675d7..00000000 --- a/docs/plans/2026-05-09-06-10-UTC-chat-navigation-swipe-follow-up.md +++ /dev/null @@ -1,21 +0,0 @@ -# Background -The initial drawer swipe update disabled `TabBarView` paging and added swipe-to-close behavior, but review feedback identified two follow-ups: the close direction should respect RTL layouts, and the non-tab-switch test should avoid accidentally proving the drawer-close path instead. - -# Goals -- Make swipe-to-close respect `Directionality`. -- Keep the “no tab switch on swipe” test focused on tab behavior without triggering close. -- Preserve existing drawer close behavior for LTR navigation. - -# Implementation Plan (phased) -1. Update `ChatNavigationPage` drag-end handling to choose the close swipe direction from the current text direction. -2. Refine widget tests to use a non-fling drag for the tab-stability case and assert no close request occurs. -3. Add RTL widget coverage for swipe-to-close. - -# Acceptance Criteria -- In LTR, a right-to-left fling closes navigation. -- In RTL, a left-to-right fling closes navigation. -- A horizontal drag no longer changes tabs and does not request close. - -# Validation Commands -- `./tools/init_dev_env.sh` -- `cd apps/mobile_chat_app && flutter test test/chat_navigation_page_test.dart` diff --git a/docs/plans/2026-05-09-10-40-UTC-agent-loop-internal-tools-p0.md b/docs/plans/2026-05-09-10-40-UTC-agent-loop-internal-tools-p0.md deleted file mode 100644 index 804545be..00000000 --- a/docs/plans/2026-05-09-10-40-UTC-agent-loop-internal-tools-p0.md +++ /dev/null @@ -1,64 +0,0 @@ -# Background - -Bricks chat backend currently supports async message transport with SSE synchronization and local/plugin routing. We need to start implementing an internal-tool-first agent loop for local routing, while keeping database changes minimal and reusing existing chat task/message persistence. - -# Goals - -1. Introduce a local agent loop service entrypoint for `/api/chat/respond` local route. -2. Define P0 internal tool names: - - `chat.channel.instruction.set` - - `chat.thread.instruction.set` - - `chat.channel.create` - - `chat.thread.create` -3. Implement instruction tools using existing scope-setting persistence. -4. Keep unsupported tools explicit and structured (not silently ignored). -5. Avoid DB schema changes in this phase. - -# Implementation Plan (phased) - -## Phase 1: Add agent-loop service skeleton -- Add a dedicated service module that defines: - - loop result/status contract - - supported tool constants - - tool execution results -- Keep this module independent from route handler internals. - -## Phase 2: Implement internal tool execution -- Implement `chat.channel.instruction.set` and `chat.thread.instruction.set` by calling `upsertChatScopeSetting`. -- Implement `chat.channel.create` and `chat.thread.create` using `upsertChatScopeSetting` with `scopeType` set appropriately. -- Return deterministic structured results for all tools. - -## Phase 3: Integrate local respond path -- Wire local `/api/chat/respond` to use a model-driven agent loop (`streamWithAgentToolsAndUserConfig`) for slash commands, exposing tools defined in `buildAgentTools`. -- Normal (non-slash) messages use the existing `streamWithUserConfig` path for incremental streaming. -- Preserve current async transport behavior and SSE visibility. - -**Note:** `executeInternalToolSequence` and `inferInternalToolCallsFromMessage` are exported from the service but are **not** currently called by the route. The route instead delegates tool selection and execution to the model via `buildAgentTools` / `streamText`. These functions remain available for future explicit or server-side-inferred tool dispatch. - -## Phase 4: Add focused tests -- Add unit tests for tool dispatch and unsupported-tool behavior. - -# Acceptance Criteria - -1. The codebase contains a local agent-loop service with stable contracts and tool names. -2. Two instruction tools persist settings through existing scope-setting service. -3. Unsupported tools return structured `not_implemented` errors. -4. Existing local respond behavior remains functional. -5. No database migration is introduced. - -# Execution Checklist (live status) - -- [x] Added internal tool constants and allowlist service entrypoint. -- [x] Implemented instruction tools: - - `chat.channel.instruction.set` - - `chat.thread.instruction.set` -- [x] Implemented scope-creation tools: - - `chat.channel.create` - - `chat.thread.create` -- [x] Added bounded internal tool sequence execution (`executeInternalToolSequence`) with stop-on-failure behavior. *(exported from service; not currently called by route — reserved for future server-side dispatch)* -- [x] Added inferred internal tool calls from slash-like user commands (`inferInternalToolCallsFromMessage`). *(exported from service; not currently called by route — reserved for future use)* -- [x] Added route/service tests for tool execution, inference, and argument validation. -- [x] Replaced request-driven tool execution with model-driven multi-step think→call→observe→final loop controller. (`buildAgentTools` + `streamWithAgentToolsAndUserConfig` with AI SDK `streamText` tools + `maxSteps`) -- [x] Gated agent tool exposure to slash-command requests only, preventing unintended tool calls during ordinary conversation. -- [x] Add first-class loop controls (`maxSteps`, `maxToolCalls`, `timeout`) to route-level configuration and response metadata. -- [x] Add step-by-step assistant message updates for each loop phase (not only summary message) to maximize SSE observability. (`onStepFinish` writes per-step tool-call messages with bounded `stepMessageId` ≤ 255 chars) diff --git a/docs/plans/2026-05-15-16-21-+08-chat-scroll-position-gwt.md b/docs/plans/2026-05-15-16-21-+08-chat-scroll-position-gwt.md deleted file mode 100644 index 8b70fcc4..00000000 --- a/docs/plans/2026-05-15-16-21-+08-chat-scroll-position-gwt.md +++ /dev/null @@ -1,240 +0,0 @@ -# Chat Scroll Position GWT - -## Background - -The chat message list needs a precise scroll contract for three related cases: -new user messages, assistant streaming updates, and initial entry into a channel -or section with existing history. - -Current branch work tried to prevent assistant streaming from repeatedly moving -the viewport, while still focusing the latest user message after sends and -history entry. The expected behavior is more nuanced: sending a message should -make a one-time decision from the current viewport, not from a persistent -"locked" flag or from whether the user ever scrolled earlier. Historical entry -must also be based only on the initial recent batch, not on an assumption that -all history has been loaded. - -Chinese message rendering introduces an additional test and UX risk. If the -runtime downloads fonts after the first frame, the message layout can change -after the first scroll calculation, which makes scroll positioning appear -correct in tests but wrong after fonts finish loading. This repository does not -currently show an obvious app-level `google_fonts` or explicit runtime font -loader dependency, so the font-download behavior may come from Flutter web, -CanvasKit, or platform fallback behavior. It should be investigated before it is -treated as part of the scroll fix scope. - -## Goals - -- Define user-observable scroll behavior before changing implementation code. -- Preserve the user's reading position whenever the current viewport indicates - the user is still reading older assistant output. -- Anchor new or historical conversation views around the latest relevant user - message when the send-time viewport indicates the user has already reached - the end of the current assistant output. -- Treat initial historical entry as a recent-batch problem: the app initially - loads only the latest 20 messages, and older history is loaded only after user - scroll/pull actions. -- Avoid relying on every history item being loaded, laid out, or mounted. -- Track Chinese runtime font downloads as a scroll-test risk, but do not treat - the font pipeline as committed implementation scope until ownership is known. - -## Implementation Plan - -1. Classify the viewport at the moment of sending. - - Do not model this as a durable locked/unlocked flag. - - The decision depends on the viewport at send time. - - If the current viewport does not show the end of the current assistant - response, preserve the reading position after the follow-up send. - - If the user previously scrolled but the current viewport now shows the end - of the assistant response, anchor the next sent user message near the top - again. - - Keep assistant streaming updates from changing the viewport while the - current viewport is preserving older content. - -2. Define the send-message anchoring behavior. - - When a new conversation starts, or when the send-time viewport shows the - end of the current assistant response, anchor the newly sent user message - near the top of the viewport. - - For long user messages, align so only approximately the final two lines of - the user message remain visible and the earlier text may be above the - viewport. - - When the send-time viewport does not show the end of the current assistant - response, do not scroll after the follow-up message is sent. - -3. Define initial historical entry behavior. - - On channel switch, section switch, or page refresh restore, load the latest - 20 messages as the initial history batch. - - Search only that initial batch for the latest user message. - - If the initial batch contains a user message, anchor the latest user - message near the top of the viewport and show subsequent assistant, tool, - and status messages below it. - - If the initial batch contains no user message, naturally show the end of - the initial batch without applying user-message top anchoring or extra - waiting-space offset. - -4. Add focused regression coverage. - - Verify actual message visibility and placement, not only that scroll - pixels are greater than zero. - - Cover long user messages, assistant streaming updates, send-time viewport - preservation for follow-up sends, and initial history batches with and - without user messages. - - Include a large-history scenario that proves behavior does not depend on - all history being loaded or mounted. - -5. Investigate Chinese font loading as a risk, not as a committed fix. - - Identify whether runtime font downloads are caused by app code, Flutter - web rendering, CanvasKit, or platform fallback behavior. - - If the cause is controlled by this repository, decide whether to use - bundled app fonts or platform/system font fallback. - - If the cause is outside this repository's practical control, keep it as a - known validation risk and avoid adding it as a hard acceptance criterion - for the scroll fix. - - Use Chinese long-message scenarios in visual validation so font-related - layout shifts are visible even if the font pipeline is not solved here. - -6. Update code maps if implementation changes touch feature entry points, - business logic, tests, or documentation indexes. - -## Acceptance Criteria - -### GWT 1: New conversation with a short user message - -Given the user opens a new conversation with no existing messages -When the user sends a short user message -Then the user message is automatically positioned near the top of the viewport -And the area below it is available for assistant streaming output -And assistant streaming updates do not repeatedly change the user message -position - -### GWT 2: New conversation with a long user message - -Given the user opens a new conversation with no existing messages -When the user sends a long, multi-line user message -Then the viewport is positioned around that user message -And earlier text in the message may be above the viewport -And approximately the final two lines of the message remain visible -And the area below remains available for assistant streaming output - -### GWT 3: Follow-up send when the assistant response end is visible - -Given the conversation already has a previous assistant response -And the end of the current assistant response is visible in the conversation -viewport -When the user sends a follow-up message -Then the new user message is automatically positioned near the top of the -viewport -And long follow-up messages follow the same final-two-lines visibility rule -And assistant streaming updates do not repeatedly take over the scroll position - -### GWT 4: Follow-up send while the assistant response end is not visible - -Given the user is reading a previous long assistant response -And at the moment of sending, the viewport does not show the end of that -assistant response -When the user sends a follow-up message while reading -Then the conversation viewport does not automatically scroll to the new user -message -And the current reading position remains stable -And subsequent assistant streaming updates do not disturb the current reading -position - -### GWT 5: Initial history batch contains at least one user message - -Given the user switches channel, switches section, or refreshes into a section -And the section has existing history -And the app initially loads the latest 20 messages -And those 20 messages contain at least one user message -When the initial history batch finishes loading -Then the viewport is positioned at the latest user message within that initial -batch -And that user message is near the top of the viewport -And the user can see assistant, tool, and status messages that follow that user -message -And the behavior is the same whether the full history has fewer than 20, -hundreds, or thousands of messages - -### GWT 6: Initial history batch contains no user message - -Given the user switches channel, switches section, or refreshes into a section -And the section has existing history -And the app initially loads the latest 20 messages -And those 20 messages contain no user message -When the initial history batch finishes loading -Then the viewport does not apply user-message top anchoring -And the viewport naturally shows the end of the initial batch -And the final assistant, tool, or status message is visible near the bottom of -the viewport -And no extra waiting-space offset is forced - -### GWT 7: Initial batch last round contains many assistant, tool, or status messages - -Given the latest 20 loaded messages include a final round started by one user -message -And that user message is followed by multiple assistant, tool, or status -messages -When the initial history batch finishes loading -Then the viewport uses the latest user message in the initial batch as the -anchor -And it does not treat the latest assistant, tool, or status message as the start -of the final round -And the user can review the final question and the subsequent execution flow - -### GWT 8: Very large history - -Given a channel or section has a very large full history, such as hundreds or -thousands of messages -When the user enters that channel or section -Then the app initially loads only the latest 20 messages -And initial positioning is based only on those 20 messages -And the behavior does not depend on all historical messages being loaded, -rendered, laid out, or mounted -And older history participates only after the user explicitly scrolls or pulls -to load more - -### GWT 9: Assistant streaming updates - -Given the viewport has already been positioned by a send-message or initial -history-entry rule -When assistant output is appended or updated during streaming -Then the streaming update itself does not trigger a new user-message anchoring -operation -And changes such as messageId hydration, taskState changes, content deltas, or -tail message field changes do not cause viewport jumps -And only explicit user actions, such as tapping Jump to latest, may move the -viewport to the latest area while the viewport is preserving older content - -### GWT 10: Send-time protection when the assistant response end is not visible - -Given the viewport does not show the end of the current assistant response at -the moment a follow-up message is sent -When follow-up user messages, assistant appends, streaming updates, or tool -status updates occur -Then the app preserves the user's current reading position -And it does not automatically pull the viewport back to the latest message area - -### GWT 11: User scrolled before but has read through to the assistant response end - -Given the user previously scrolled while reading a long assistant response -And the user later scrolls or reads down until the end of that assistant -response is visible -When the user sends a follow-up message -Then the app bases the decision on the current viewport, not on earlier scroll -history -And the new user message is automatically positioned near the top of the -viewport -And the long-message final-two-lines visibility rule applies when relevant - -### Investigation 1: Chinese runtime font downloads - -Given the conversation contains Chinese user and assistant messages -When the chat screen renders in the target runtime -Then record whether font packages are downloaded after the first frame -And identify whether the download is controlled by app code, Flutter web, -CanvasKit, or platform fallback behavior -And do not require the scroll fix to eliminate this download unless ownership -and a reliable fix are confirmed - -## Validation Commands - -- `cd apps/mobile_chat_app && flutter test test/message_list_test.dart` -- `cd apps/mobile_chat_app && flutter analyze` diff --git a/docs/plans/2026-05-15-16-54-+08-local-chat-fixture-db.md b/docs/plans/2026-05-15-16-54-+08-local-chat-fixture-db.md deleted file mode 100644 index 8ff76e0c..00000000 --- a/docs/plans/2026-05-15-16-54-+08-local-chat-fixture-db.md +++ /dev/null @@ -1,51 +0,0 @@ -# Local Chat Fixture Database - -## Background - -Chat scroll behavior needs a repeatable local environment that can load realistic -history without depending on GitHub OAuth or a live production database during -every test run. The production data source is Turso/libSQL, and the backend -already supports libSQL through `TURSO_DATABASE_URL`. - -The local test environment should export a safe subset of chat-related data into -a local `file:` libSQL database, run the backend against that file, and provide a -test JWT for Flutter's existing `BRICKS_TEST_TOKEN` quick-login flow. - -## Goals - -- Build a local fixture database from a remote Turso database without storing - remote credentials in the repository. -- Copy only the tables needed to render chat history, scopes, channel names, and - related node/settings context. -- Provide a repeatable way to generate a dev JWT for a fixture user. -- Let Flutter Web load the local backend and use Quick Login instead of GitHub - OAuth. -- Keep exported fixture files out of git. - -## Implementation Plan - -1. Add a backend script that exports a remote Turso/libSQL database subset into - a local `file:` libSQL database. -2. Add a backend script that prints a local dev JWT for a selected fixture user. -3. Add package scripts for export and token generation. -4. Document the local startup flow with environment variables, backend command, - Flutter command, and quick-login usage. -5. Validate that the exported local DB can answer chat history endpoints through - the normal backend code path. - -## Acceptance Criteria - -- The export command reads remote credentials only from environment variables. -- The export command writes a local DB file under a gitignored cache path. -- The export command does not copy API config secrets by default. -- The local backend can run with `TURSO_DATABASE_URL=file:`. -- The token generation command emits a JWT for the selected fixture user using - the local `JWT_SECRET`. -- Flutter can be launched with `BRICKS_TEST_TOKEN` and `BRICKS_API_BASE_URL` - defines and can enter the app through Quick Login. - -## Validation Commands - -- `cd apps/node_backend && npm run type-check` -- `cd apps/node_backend && npm test` -- `cd apps/mobile_chat_app && flutter analyze` diff --git a/docs/plans/2026-05-15-19-27-+08-evidence-driven-browser-test-env-skill.md b/docs/plans/2026-05-15-19-27-+08-evidence-driven-browser-test-env-skill.md deleted file mode 100644 index 3292ef33..00000000 --- a/docs/plans/2026-05-15-19-27-+08-evidence-driven-browser-test-env-skill.md +++ /dev/null @@ -1,43 +0,0 @@ -# Evidence-Driven Browser Test Environment Skill - -## Background - -Some UI defects cannot be validated confidently through unit tests alone. They -depend on realistic data, historical state variety, browser rendering, scroll -position, user-triggered interactions, asynchronous updates, and screenshot -evidence. - -The repository needs a reusable agent skill that tells future agents how to -construct a trustworthy local or CI validation environment before fixing these -types of issues. - -## Goals - -- Add a repository skill that guides data-backed browser validation work. -- Make the workflow explicit about fixture data, local auth bypasses, real - browser interactions, screenshots, and safe persistence rules. -- Keep the skill general enough for future complex UI/data tasks, not only the - current chat scroll issue. - -## Implementation Plan - -1. Create `.agents/skills/evidence-driven-browser-test-env/SKILL.md`. -2. Define clear trigger conditions for data-dependent, browser-rendered, or - interaction-triggered behavior. -3. Document a practical workflow for building local fixture-backed environments. -4. Document evidence requirements and failure checks. -5. Document what should and should not be committed to Git. - -## Acceptance Criteria - -- The new skill exists under `.agents/skills/`. -- The skill describes when to use it and how to validate interaction-triggered - browser behavior. -- The skill explicitly separates safe committed artifacts from local-only or - sensitive artifacts. -- Future agents can use the skill without needing context from this chat. - -## Validation Commands - -- `test -f .agents/skills/evidence-driven-browser-test-env/SKILL.md` -- `sed -n '1,240p' .agents/skills/evidence-driven-browser-test-env/SKILL.md` diff --git a/docs/plans/2026-05-16-02-20-+00-todos-tables-highlights-tools.md b/docs/plans/2026-05-16-02-20-+00-todos-tables-highlights-tools.md deleted file mode 100644 index d82844a6..00000000 --- a/docs/plans/2026-05-16-02-20-+00-todos-tables-highlights-tools.md +++ /dev/null @@ -1,160 +0,0 @@ -# Todos, Asset Tables, Text Highlights – Unified MCP Tool Architecture - -## Background - -### Context - -The platform already has an agent loop (`localAgentLoopService.ts`) with a small set of -internal tools for chat-management actions (channel rename, create, instruction-set, etc.). -The `buildAgentTools()` function returns an AI-SDK-compatible tool registry closed over -`userId`, and `executeInternalTool()` dispatches each named tool to a typed handler. -The pattern is clean and proven; new domains should follow it exactly. - -The Flutter UI shows AI tool-call progress via `agentLoopPhase = 'tool_call_start'` -messages, and renders tool results as Markdown in `_AssistantMarkdownText`. - -### Problem - -Three new user-facing features are needed, none of which have any backend or UI code yet: - -1. **Todo list** – users want to say "create a todo: buy milk" and have the AI call a - tool, storing it persistently. Querying ("show my todos") should return a Markdown list. -2. **Asset tables** – users want AI-driven dynamic tables: add/remove columns, add/update/delete - rows, all via natural language. The JSONB sparse-storage pattern (separate column registry + - `cell_data JSONB` on rows) allows schema changes without touching row data. -3. **Text highlights** – users want to select text in a message bubble, right-click (or - long-press), choose "Highlight", and have that selection persist across reloads. The AI - should be able to answer "what are my highlights?" by calling a `highlight_list` tool. - -### Motivation - -All three features share the same integration surface: Postgres for persistence, typed -service modules, new `INTERNAL_TOOL_*` constants, and Flutter API services. Building them -together under one plan keeps the architecture consistent and avoids ad-hoc divergence. - -## Goals - -- Implement persistent todo-list CRUD entirely via the existing internal-tool infrastructure. -- Implement JSONB-backed asset tables with column-level schema operations (add/remove column) - that are O(1) in row count. -- Implement text-highlight storage and a Flutter context-menu entry that persists selections. -- Expose all three domains via both the AI agent loop (tool calls visible to the user) and - a REST API so the Flutter client can perform direct CRUD without going through the AI. -- Maintain the existing single-file tool registry pattern (`localAgentLoopService.ts`). - -## Implementation Plan - -### Phase 1 – Database Migrations - -1. **`015_create_user_todos.sql`** – `user_todos` table: - ``` - id UUID PK, user_id UUID FK, title TEXT NOT NULL, notes TEXT, - is_completed BOOLEAN DEFAULT FALSE, display_order INT DEFAULT 0, - created_at TIMESTAMP, updated_at TIMESTAMP - ``` - Index on `(user_id, is_completed)`. - -2. **`016_create_asset_tables.sql`** – three tables: - - `asset_tables`: `id UUID PK, user_id UUID FK, resource_id VARCHAR(255), title TEXT, created_at, updated_at` – UNIQUE `(user_id, resource_id)` - - `asset_table_columns`: `id UUID PK, user_id UUID FK, resource_id VARCHAR(255), column_key VARCHAR(255), display_name TEXT, column_order INT DEFAULT 0, created_at, updated_at` – UNIQUE `(user_id, resource_id, column_key)` - - `asset_table_rows`: `id UUID PK, user_id UUID FK, resource_id VARCHAR(255), display_number INT, cell_data JSONB DEFAULT '{}', is_deleted BOOLEAN DEFAULT FALSE, created_at, updated_at` - Index on `(user_id, resource_id)` for all three tables. - -3. **`017_create_text_highlights.sql`** – `text_highlights` table: - ``` - id UUID PK, user_id UUID FK, message_id VARCHAR(255) NOT NULL, - selected_text TEXT NOT NULL, start_offset INT, end_offset INT, - color VARCHAR(32) DEFAULT 'yellow', - created_at TIMESTAMP, updated_at TIMESTAMP - ``` - Index on `(user_id, message_id)`. - -### Phase 2 – Backend Services - -4. **`todoService.ts`** – `createTodo`, `listTodos`, `updateTodo`, `deleteTodo`, - `completeTodo`. Each function takes `userId` as first arg, returns typed DTOs. - -5. **`assetTableService.ts`** – functions: - - `createTable`, `listTables`, `getTable` (with columns + rows joined) - - `addColumn`, `removeColumn` (DELETE from `asset_table_columns`) - - `addRow` (INSERT with cell_data), `updateRow` (JSONB merge UPDATE), - `deleteRow` (soft-delete `is_deleted = true`) - Reading rows always JOINs `asset_table_columns` as the authority for active columns; - `cell_data` keys not in the column registry are silently ignored. - -6. **`textHighlightService.ts`** – `createHighlight`, `listHighlights`, - `listHighlightsByMessageId`, `deleteHighlight`. - -### Phase 3 – Internal Tool Registry - -7. **Add constants** to `localAgentLoopService.ts`: - - Todo: `todo_create`, `todo_list`, `todo_complete`, `todo_update`, `todo_delete` - - Table: `table_create`, `table_list`, `table_get`, `table_add_column`, - `table_remove_column`, `table_add_row`, `table_update_row`, `table_delete_row` - - Highlight: `highlight_list` (read-only; manual create is done from Flutter) - -8. **Extend `executeInternalTool()`** with `switch` cases for every new constant, - delegating to the service functions added in Phase 2. - -9. **Extend `buildAgentTools()`** with AI-SDK tool definitions (description + parametersSchema) - for every new constant. Tool results for todo/table should return data that the model can - format as Markdown for the user. - -### Phase 4 – REST Routes - -10. **`resources.ts`** – Express router at `/api/resources`, authenticated: - - `GET /todos`, `POST /todos`, `PATCH /todos/:id`, `DELETE /todos/:id` - - `GET /tables`, `POST /tables`, `GET /tables/:resourceId`, - `POST /tables/:resourceId/columns`, `DELETE /tables/:resourceId/columns/:columnKey`, - `POST /tables/:resourceId/rows`, `PATCH /tables/:resourceId/rows/:rowId`, - `DELETE /tables/:resourceId/rows/:rowId` - - `GET /highlights`, `POST /highlights`, `DELETE /highlights/:id` - -11. **Register** in `app.ts`: `app.use('/api/resources', resourcesRoutes)`. - -### Phase 5 – Flutter Client - -12. **`todo_api_service.dart`** – service class mirroring the REST surface; - methods: `listTodos`, `createTodo`, `updateTodo`, `completeTodo`, `deleteTodo`. - -13. **`asset_table_api_service.dart`** – `listTables`, `getTable`, `createTable`, - `addColumn`, `removeColumn`, `addRow`, `updateRow`, `deleteRow`. - -14. **`text_highlight_api_service.dart`** – `listHighlights`, `createHighlight`, - `deleteHighlight`. - -15. **Text highlight context menu in `message_list.dart`**: - - Each assistant message bubble wraps content in a `SelectionArea` child. Use - `ContextMenuController` / `AdaptiveTextSelectionToolbar` to intercept the platform - context menu, inject a "Highlight" button. - - On "Highlight": read the selected text + approximate character range, POST to - `/api/resources/highlights`, then call `setState` to add the highlight to an in-memory - map and re-render the message with a `TextSpan(background: highlightColor)`. - - On screen load: `GET /api/resources/highlights` filtered by current channel/session, - populate the in-memory highlight map before first render. - -## Acceptance Criteria - -- User types "add a todo: finish the report" and sees the AI emit a `tool_call_start` bubble - followed by a confirmation message listing the new todo item. -- User types "show my todos" and the AI calls `todo_list`, then responds with a Markdown - checklist of todos. -- User types "create a table called Tasks with columns Name and Status" and the AI calls - `table_create` then `table_add_column` twice; a subsequent "show the Tasks table" - returns a Markdown table. -- User types "add a column Priority to Tasks table" and the AI calls `table_add_column` - with O(1) DB cost (no row UPDATE required). -- User selects text in an assistant message bubble and sees a "Highlight" option in the - context menu. After tapping, the selected text appears with a yellow background. -- After reloading the app (or reopening the channel), the highlight is still visible on - the same message. -- User types "what are my highlights?" and the AI calls `highlight_list`, returning the - stored highlighted texts in a readable list. -- All existing tests pass; no regressions on chat, channel, or agent-loop behaviour. - -## Validation Commands - -- `cd apps/node_backend && npm run build` -- `cd apps/node_backend && npm test` -- `cd apps/mobile_chat_app && flutter analyze` -- `cd apps/mobile_chat_app && flutter test` diff --git a/docs/plans/2026-05-16-08-13-+00-highlight-floating-toolbar.md b/docs/plans/2026-05-16-08-13-+00-highlight-floating-toolbar.md deleted file mode 100644 index ea702ad6..00000000 --- a/docs/plans/2026-05-16-08-13-+00-highlight-floating-toolbar.md +++ /dev/null @@ -1,105 +0,0 @@ -# Highlight Floating Toolbar - -## Background - -### Context - -The text highlight feature has partial infrastructure in place: - -- `TextHighlightApiService` — CRUD API client for highlights (create / delete / list by messageId). -- `HighlightSpan` / `_AssistantMarkdownText` — renders highlights as a semi-transparent yellow background over matching text spans inside assistant messages. -- `MessageList` — accepts `highlights: Map>` and `onHighlight` callback, and inserts a "划线" button into the system `AdaptiveTextSelectionToolbar` when text is selected. - -**However**, `ChatScreen` does not wire up these props — it passes `MessageList(messages: _messages)` with no highlights map and no `onHighlight` callback, so the highlight system is completely disconnected end-to-end. - -### Problem - -1. The `ChatScreen` never loads highlights from the API and never passes them to `MessageList`, so no persisted highlights are ever shown. -2. The current selection toolbar is the platform's default `AdaptiveTextSelectionToolbar`, which is positioned by the OS and visually inconsistent. The custom "划线" entry is appended to the end of whatever system buttons the OS provides. -3. There is no **copy** or **hide** button in the custom toolbar (copy exists in the default system buttons, but is not part of any custom-styled bar). -4. There is no **delete highlight** action — a user cannot remove an existing highlight. -5. Tapping an already-highlighted span does nothing. - -### Motivation - -The user expects a cohesive, discoverable experience: -- Slide-select → custom floating toolbar with 划线 / 复制 / 隐藏. -- Tap an already-highlighted span → floating toolbar with 删除划线 / 复制 / 隐藏. -- Toolbar is visually positioned near the selection (above or below), not in a system default corner. - ---- - -## Goals - -- Wire up the highlight feature end-to-end: load, render, create, and delete highlights. -- Replace the system `AdaptiveTextSelectionToolbar` with a compact custom floating toolbar. -- Add **复制** (copy to clipboard) and **隐藏** (hide/fold message) actions to the toolbar. -- Show **删除划线** instead of **划线** when the selected text is already fully highlighted. -- Allow tapping any highlighted span to trigger the delete toolbar without needing a long-press/drag selection. - ---- - -## Implementation Plan - -### Phase 1 — Wire up ChatScreen - -1. Add `TextHighlightApiService` instance to `_ChatScreenState` (alongside `_chatHistoryApiService`). -2. Add state field `Map> _highlights = {}`. -3. In `_loadMessagesForActiveScope`, after messages are loaded and `_authToken` is available, call `TextHighlightApiService.listHighlights` for every `messageId` in the loaded messages that belongs to an assistant role. Group results into `_highlights` and update via `setState`. Use the same scope-staleness guard pattern already used for message loading. -4. Add `_handleCreateHighlight(String messageId, String selectedText, int? start, int? end)` method — calls `createHighlight`, then calls `listHighlights` for that `messageId` and refreshes `_highlights` via `setState`. -5. Add `_handleDeleteHighlight(String highlightId, String messageId)` method — calls `deleteHighlight`, then refreshes `_highlights` for that messageId. -6. Add `_handleHideMessage(String messageId)` stub — adds the messageId to a `Set _hiddenMessageIds` state field; the message list can filter or collapse those messages (out of scope for this plan; just the stub needed here). -7. Pass `highlights`, `onHighlight`, `onDeleteHighlight`, and `onHideMessage` into the `MessageList` constructor in the `chatContent` build. - -### Phase 2 — Custom floating toolbar - -1. Replace `AdaptiveTextSelectionToolbar.buttonItems` in `MessageList.contextMenuBuilder` with a new private widget `_SelectionToolbar`. -2. `_SelectionToolbar` is a `Material`-wrapped `Row` of `InkWell` text buttons (pill-shaped, compact), using `ChatColors` or `Theme` tokens. -3. Buttons: **划线** (or **删除划线** if already highlighted), **复制**, **隐藏**. -4. Position using `selectableRegionState.contextMenuAnchors.primaryAnchor` (or `secondaryAnchor` if it would be off-screen). -5. To detect **"already highlighted"**: check if `_lastSelectedText` is a substring of any `HighlightSpan.selectedText` in the highlights map for any assistant message that contains the selection. Pass the relevant `highlights` map as a parameter so `contextMenuBuilder` can evaluate it synchronously. -6. **划线** action: call `widget.onHighlight` (existing callback). -7. **删除划线** action: find the matching `HighlightSpan` for the selected text and call a new `widget.onDeleteHighlight` callback. -8. **复制**: call `Clipboard.setData(ClipboardData(text: _lastSelectedText))` then `ContextMenuController.removeAny()`. -9. **隐藏**: call new `widget.onHideMessage` callback with the messageId of the message containing the selection, then dismiss. - -### Phase 3 — Tap-on-highlighted-span - -1. Add `onDeleteHighlight` and `onHideMessage` parameters to `_AssistantMarkdownText`. -2. In `_splitSpanByHighlights`, for each highlighted `TextSpan`, attach a `TapGestureRecognizer` that: - - Locates the `HighlightSpan` matching that text fragment. - - Shows a lightweight `OverlayEntry` positioned at the tap's global offset containing a `_SelectionToolbar` in "delete mode" (showing 删除划线, 复制, 隐藏 for the highlighted text). Dismiss the overlay on second tap or on any other gesture. - -### Phase 4 — MessageList API additions - -Add to `MessageList`'s constructor (and propagate through `_MessageListState` / `_AssistantMarkdownText`): - -``` -onDeleteHighlight: void Function(String highlightId, String messageId)? -onHideMessage: void Function(String messageId)? -``` - -The existing `onHighlight` signature is unchanged. - ---- - -## Acceptance Criteria - -- After sliding to select text in an assistant message, a floating toolbar appears **near the selection** (not in a system corner) with buttons labeled **划线**, **复制**, and **隐藏**. -- Tapping **划线** creates a highlight via the API; the selected text gains a yellow background immediately (optimistic update or reload). -- Tapping **复制** copies the selected text to the clipboard and dismisses the toolbar. -- Tapping **隐藏** invokes the hide callback and dismisses the toolbar. -- When text that is already highlighted is selected, the **划线** button changes to **删除划线**. -- Tapping **删除划线** calls the delete API and removes the yellow background from the affected span. -- Tapping directly on a yellow-highlighted span (without dragging) shows the same toolbar in delete mode. -- After navigating away and back to a conversation, previously created highlights are still rendered. -- The floating toolbar does not appear for user-bubble messages (highlight only applies to assistant messages). - ---- - -## Validation Commands - -```sh -cd apps/mobile_chat_app && flutter analyze -cd apps/mobile_chat_app && flutter test -``` diff --git a/docs/plans/2026-05-16-08-20-+00-resources-tab-in-navigation-sidebar.md b/docs/plans/2026-05-16-08-20-+00-resources-tab-in-navigation-sidebar.md deleted file mode 100644 index e3c2d699..00000000 --- a/docs/plans/2026-05-16-08-20-+00-resources-tab-in-navigation-sidebar.md +++ /dev/null @@ -1,73 +0,0 @@ -# Resources Tab in Navigation Sidebar - -## Background - -### Context - -The chat navigation sidebar (`ChatNavigationPage`) currently has two tabs: **Channels** and **Nodes**. It is mounted inside a `Drawer` (or inline sidebar on desktop) and is the primary navigation surface of the mobile chat app. - -There are already two resource services in the codebase: -- `TodoApiService` – manages todo-lists and their items -- `AssetTableApiService` – manages asset tables (columns + rows) - -Both services return summary objects via their list endpoints: `listTodoLists()` returns `TodoList` (without items) and `listTables()` returns `AssetTableSummary`. - -### Problem - -Users have no way to browse or open their todo-lists and asset tables from the navigation sidebar. There is no entry point for resource discovery in the current two-tab layout. - -### Motivation - -A dedicated **Resources** tab between Channels and Nodes gives users a single place to see all their structured data resources at a glance. Clicking any item opens a preview page, establishing the navigation pattern for richer resource views in future iterations. - -## Goals - -- Add a single **Resources** tab to the navigation sidebar, positioned between Channels and Nodes. -- Display all todo-lists and all asset tables as a flat list (no section headers) in that tab, distinguished by icon. -- Tapping any resource item opens a preview page showing the resource's summary information. -- Load resource summaries during app initialisation alongside the existing platform-node loading. -- Keep the change backward-compatible: existing tests pass with minimal updates. - -## Implementation Plan - -1. **Data models** (`chat_navigation_page.dart`) - - Add `enum ChatResourceType { todoList, assetTable }`. - - Add `class ChatResourceItem` with fields: `id`, `type`, `title`, `notes` (nullable). - -2. **Navigation page – Resources tab** (`chat_navigation_page.dart`) - - Add `resources` parameter (`List`, defaults to `const []`) and optional `onResourceSelected` callback. - - Change `TabController(length: 2)` → `length: 3`. - - Update `TabBar` to show three tabs: **Channels**, **Resources**, **Nodes**. - - Insert a new `ListView` as the middle `TabBarView` child that renders each `ChatResourceItem` with a type-specific icon (`Icons.checklist_outlined` for todo-lists, `Icons.table_chart_outlined` for tables), the title, and an optional subtitle from `notes`. - - On tap: call `onResourceSelected` if provided; otherwise push `_ResourcePreviewPage` internally via `Navigator.of(context).push(...)`. - - Update class doc-comment to mention the new Resources tab. - -3. **Resource preview page** (`chat_navigation_page.dart`) - - Add private `_ResourcePreviewPage` widget (like `_NodeDetailPage`) that receives a `ChatResourceItem` and displays its type icon, title, and notes in a `Scaffold` with `AppBar`. - -4. **Load resources in ChatScreen** (`chat_screen.dart`) - - Import `todo_api_service.dart` and `asset_table_api_service.dart`. - - Add service instances `_todoApiService` and `_assetTableApiService`. - - Add state lists `_todoLists` (type `List`) and `_assetTables` (type `List`). - - Inside `_loadAgents()`, after auth token resolves, load both resource lists best-effort (same pattern as platform nodes) and include them in the final `setState`. - - Build a combined `resources` list in `_buildNavigationContent` and pass it to `ChatNavigationPage`. - -5. **Update tests** (`chat_navigation_page_test.dart`) - - Rename the existing "two tabs" test to verify three tabs (Channels, Resources, Nodes). - - Add a test for Resources tab empty state. - - Add a test for Resources tab showing items (mixed todo-list and table items). - - Add a test verifying tapping a resource opens the preview page. - -## Acceptance Criteria - -- The navigation sidebar shows three tabs: Channels, Resources, Nodes (in that order). -- The Resources tab displays a flat list of all loaded todo-lists (checklist icon) and asset tables (table icon) without section headers. -- Tapping a resource item navigates to a preview page showing the item's title and notes (if present). -- When no resources are loaded the Resources tab shows a "No resources" empty state message. -- Existing Channels and Nodes tab behaviour is unchanged. -- All existing and new widget tests pass. - -## Validation Commands - -- `cd apps/mobile_chat_app && flutter test test/chat_navigation_page_test.dart` -- `cd apps/mobile_chat_app && flutter analyze` diff --git a/docs/plans/2026-05-16-17-40-+08-ios-testflight-pr-workflow.md b/docs/plans/2026-05-16-17-40-+08-ios-testflight-pr-workflow.md deleted file mode 100644 index fcc08ba2..00000000 --- a/docs/plans/2026-05-16-17-40-+08-ios-testflight-pr-workflow.md +++ /dev/null @@ -1,47 +0,0 @@ -# iOS TestFlight PR Workflow - -## Background - -The `go-puzzle` repository has a GitHub Actions workflow that builds an iOS IPA, -uploads it to TestFlight, and labels the associated PR after a successful -upload. - -Bricks needs the same release-support workflow for the mobile Flutter app in -this monorepo. The workflow should be limited to pull requests targeting a -designated release branch so it does not run expensive macOS packaging for -every pull request. - -## Goals - -- Add a GitHub Actions workflow that builds and uploads the iOS app to - TestFlight. -- Trigger the expensive build only for PRs whose target branch is - `testflight-beta`. -- Add a `testflight-uploaded` label to the associated PR only after upload - succeeds. -- Keep secrets and signing configuration in GitHub Actions secrets. - -## Implementation Plan - -1. Add `.github/workflows/ios-testflight.yml`. -2. Scope the workflow to same-repository pull requests targeting `testflight-beta` - and manual dispatch. -3. Run Flutter and CocoaPods commands from `apps/mobile_chat_app`. -4. Configure iOS bundle id and team through existing `Release.xcconfig` - variables using GitHub secrets. -5. Use Codemagic CLI tools to fetch signing files and publish the IPA. -6. Create or update the PR label after a successful upload. - -## Acceptance Criteria - -- A PR targeting `testflight-beta` triggers the TestFlight workflow. -- PRs targeting other branches do not run the expensive iOS packaging job. -- A successful TestFlight upload adds `testflight-uploaded` to the associated - PR. -- The workflow can also be run manually through `workflow_dispatch`. -- No App Store Connect credentials, certificate keys, or bundle identifiers are - hardcoded beyond secret names and existing local defaults. - -## Validation Commands - -- `ruby -e 'require "yaml"; YAML.load_file(".github/workflows/ios-testflight.yml"); puts "workflow yaml ok"'` diff --git a/docs/plans/2026-05-16-17-58-+08-mobile-agents-directory-setup-fix.md b/docs/plans/2026-05-16-17-58-+08-mobile-agents-directory-setup-fix.md deleted file mode 100644 index 78c392c0..00000000 --- a/docs/plans/2026-05-16-17-58-+08-mobile-agents-directory-setup-fix.md +++ /dev/null @@ -1,40 +0,0 @@ -# Mobile Agents Directory Setup Fix - -## Background - -After GitHub login on the locally installed iOS app, chat setup fails with: - -`Unsupported operation: agentsDirectory is not supported on this platform.` - -Chat setup creates the local agents repository before loading persisted scopes -and messages. Because `PlatformPathsImpl.agentsDirectory()` only supported -desktop platforms, iOS threw before history hydration could run, making the app -look like the signed-in account had no history. - -## Goals - -- Support mobile sandbox paths for the local agents directory. -- Avoid blocking chat history hydration on iOS/Android. -- Keep desktop and web path behavior unchanged. - -## Implementation Plan - -1. Extend `PlatformPathsImpl` to return iOS sandbox paths under - `Library/Application Support` and `Library/Caches`. -2. Extend Android paths under the app sandbox inferred from `Directory.systemTemp`. -3. Update `PlatformPaths` documentation. -4. Validate with Dart/Flutter analysis and reinstall the iOS app locally. - -## Acceptance Criteria - -- iOS no longer throws `agentsDirectory is not supported on this platform` - during chat setup. -- GitHub-authenticated mobile users can proceed to scope/message hydration after - login. -- Existing desktop path behavior is preserved. - -## Validation Commands - -- `dart format packages/platform_bridge/lib/src/io/platform_paths_io.dart packages/platform_bridge/lib/src/platform_paths.dart` -- `flutter analyze` -- `flutter run --release -d ` diff --git a/docs/plans/2026-05-16-22-43-+08-ios-debug-api-base-url.md b/docs/plans/2026-05-16-22-43-+08-ios-debug-api-base-url.md deleted file mode 100644 index 7380892a..00000000 --- a/docs/plans/2026-05-16-22-43-+08-ios-debug-api-base-url.md +++ /dev/null @@ -1,36 +0,0 @@ -# iOS Debug API Base URL - -## Background - -Local debug builds currently default non-web platforms to `http://localhost:3000`. -That is useful for desktop development, but it is wrong for iOS device testing: -`localhost` points at the phone, not the developer machine or production API. - -For GitHub OAuth and real account history validation on iPhone, iOS debug builds -should use the same production API as release builds unless a developer -explicitly overrides `BRICKS_API_BASE_URL`. - -## Goals - -- Make iOS debug builds default to `https://bricks.askman.dev`. -- Preserve web debug behavior: use the current browser origin. -- Preserve local desktop/other debug behavior: use `http://localhost:3000`. -- Keep `BRICKS_API_BASE_URL` as the highest-priority explicit override. - -## Implementation Plan - -1. Update `LlmConfigService.resolveBaseUrl()`. -2. Add a focused test for iOS debug platform selection. -3. Reinstall the app on the connected iPhone for validation. - -## Acceptance Criteria - -- iOS debug builds use the production API by default. -- Existing local non-web test builds continue to use localhost. -- Developers can still override the API base URL with `BRICKS_API_BASE_URL`. - -## Validation Commands - -- `flutter test test/llm_config_service_test.dart` -- `flutter analyze` -- `flutter run -d ` diff --git a/docs/plans/2026-05-16-23-21-+08-ios-sandbox-path-without-home.md b/docs/plans/2026-05-16-23-21-+08-ios-sandbox-path-without-home.md deleted file mode 100644 index b2d85144..00000000 --- a/docs/plans/2026-05-16-23-21-+08-ios-sandbox-path-without-home.md +++ /dev/null @@ -1,35 +0,0 @@ -# iOS Sandbox Path Without HOME - -## Background - -The iOS debug app can show `Failed to load chat setup: Unsupported operation: HOME environment variable is not available on this platform.` during startup. The mobile platform path implementation added iOS support but still used `Platform.environment['HOME']`, which is not reliable in the iOS app sandbox. - -## Goals - -- Resolve iOS document, cache, and agents directories without reading `HOME`. -- Keep desktop behavior unchanged. -- Preserve Android mobile path behavior. -- Add a small unit test for the mobile temp-directory-to-app-container path logic. -- Keep chat history loading independent from local custom-agent file availability. - -## Implementation Plan - -1. Add a mobile app data directory resolver based on `Directory.systemTemp.path`. -2. Use that resolver for iOS `Documents`, `Library/Caches/bricks`, and `Library/Application Support/bricks/agents`. -3. Keep Android on the same temp-parent resolver it already effectively used. -4. Make startup custom-agent loading best-effort so local file path failures fall back to built-in agents and do not block remote chat setup. -5. Validate the resolver with platform-independent unit tests. - -## Acceptance Criteria - -- iOS path resolution does not call `_requiredEnv('HOME')`. -- `agentsDirectory()` on iOS resolves under the app sandbox `Library/Application Support/bricks/agents`. -- Startup chat setup no longer fails because `HOME` is missing. -- Startup chat setup continues loading scopes, channel names, and active history even if local custom-agent files cannot be read. -- Existing mobile chat API base URL behavior remains unchanged. - -## Validation Commands - -- `cd packages/platform_bridge && dart test` -- `cd apps/mobile_chat_app && flutter test test/llm_config_service_test.dart` -- `cd apps/mobile_chat_app && flutter analyze` diff --git a/docs/plans/2026-05-17-00-04-+08-table-tool-error-and-turso-compatibility.md b/docs/plans/2026-05-17-00-04-+08-table-tool-error-and-turso-compatibility.md deleted file mode 100644 index 17b0162e..00000000 --- a/docs/plans/2026-05-17-00-04-+08-table-tool-error-and-turso-compatibility.md +++ /dev/null @@ -1,99 +0,0 @@ -# Table Tool Error and Turso Compatibility Fix - -## Background - -The historical chat stream can show a `table_create` tool call stuck in the -"calling" state. Database inspection showed two different failure modes in the -same assistant response: - -- The first `table_create` call returned a normal tool result with - `ok: false` because the model omitted the required `resourceId` argument. -- The second `table_create` call had valid arguments, but no persisted - `tool_call` result row. A direct non-mutating SQL probe showed the underlying - `createTable` query fails on Turso/libSQL because it uses PostgreSQL's - `NOW()` function. - -The UI difference comes from persistence, not from user intent. Business-level -tool failures are persisted as result rows, while thrown tool execution errors -from the AI SDK stream are currently ignored by the backend stream wrapper. - -## Goals - -- Make asset table mutations compatible with the repository's Turso/libSQL - production database. -- Persist tool execution errors so tool starts cannot remain permanently stuck - in `dispatched`. -- Preserve clear UX semantics: started, completed successfully, completed with - business failure, and completed with execution error should be distinguishable - in history. -- Add focused regression coverage for both the SQL compatibility issue and the - missing `tool-error` persistence path. - -## Implementation Plan - -1. Fix Turso-compatible asset table writes. - - Replace PostgreSQL-only timestamp expressions in asset table write paths - with SQL that works under Turso/libSQL. - - Audit nearby table, column, and row mutation queries for other - PostgreSQL-only functions or syntax. - - Keep existing PostgreSQL behavior working if the service is still run with - a PostgreSQL `DATABASE_URL`. - -2. Persist AI SDK tool execution errors. - - Extend the stream event model in `apps/node_backend/src/llm/llm_service.ts` - to handle `tool-error` parts from `result.fullStream`. - - Route tool execution errors through a callback with the same step and call - identity used by `tool-call` start messages. - - Upsert a tool result/error message for the failed step and mark the - matching `:tc::` start message as completed or failed instead - of leaving it `dispatched`. - -3. Recheck step/call identity handling. - - Confirm `onToolCallStart`, `onStepFinish`, and any new error callback use a - single consistent source of step and call indexes. - - Avoid creating synthetic completed rows with IDs that do not match the - original start row. - -4. Update tests. - - Add or extend backend tests for `table_create` against the Turso dialect or - a SQL conversion path that catches SQLite-incompatible expressions. - - Add a stream wrapper test where the SDK emits `tool-call`, then - `tool-error`, then final text, and assert that the persisted history has no - permanently dispatched start row. - - Add a route-level regression test if needed to verify the message IDs and - task states shown to clients. - -5. Validate with realistic data. - - Reproduce the historical `show me a table` scenario locally with a fixture - or test database. - - Confirm the UI/history shows a completed error state rather than a stale - "calling" row if execution fails. - - Confirm a valid `table_create` request creates or updates an asset table in - Turso/libSQL. - -6. Update documentation and maps. - - Keep the Turso compatibility note in `docs/kb/`. - - Update code maps if implementation changes feature entry points, backend - tool logic, or validation indexes. - -## Acceptance Criteria - -- A valid `table_create` call with `resourceId` and `title` succeeds against - Turso/libSQL and leaves an `asset_tables` row. -- A `table_create` execution error is visible in chat history as a completed or - failed tool event, not as an indefinitely "calling" event. -- The first invalid-arguments failure still appears as a normal tool result with - the returned `invalid_args` payload. -- Message ordering remains deterministic for tool start, tool result/error, and - final assistant text. -- Tests cover the Turso SQL compatibility bug and the AI SDK `tool-error` - persistence bug. - -## Validation Commands - -- `./tools/init_dev_env.sh` -- `cd apps/node_backend && npm test -- --run src/services/localAgentLoopService.test.ts` -- `cd apps/node_backend && npm test -- --run src/routes/chat.test.ts` -- `cd apps/node_backend && npm run type-check` -- `cd apps/mobile_chat_app && flutter test` -- `cd apps/mobile_chat_app && flutter analyze` diff --git a/docs/plans/2026-05-18-17-43-CST-ios-build.md b/docs/plans/2026-05-18-17-43-CST-ios-build.md deleted file mode 100644 index 38ba8f15..00000000 --- a/docs/plans/2026-05-18-17-43-CST-ios-build.md +++ /dev/null @@ -1,50 +0,0 @@ -# iOS Build Environment Preparation - -## Background - -The repository contains a Flutter mobile application at `apps/mobile_chat_app`. The task is to install the project dependencies needed locally and build the iOS app. - -## Goals - -- Bootstrap the repository development environment. -- Install Flutter and iOS dependencies for the mobile app. -- Produce or verify an iOS build from the mobile app package directory. - -## Implementation Plan - -### Phase 1: Bootstrap - -- Run `./tools/init_dev_env.sh` from the repository root. -- Inspect dependency files for the mobile app and iOS project. - -### Phase 2: Install Dependencies - -- Run Flutter package resolution from `apps/mobile_chat_app`. -- Install CocoaPods dependencies from the iOS project if required. - -### Phase 3: Build - -- Run the Flutter iOS build from `apps/mobile_chat_app`. -- If signing blocks device builds, retry a simulator-compatible build. - -## Acceptance Criteria - -- Flutter dependencies are resolved for `apps/mobile_chat_app`. -- iOS native dependencies are installed when the iOS project requires them. -- The iOS app build command completes, or any remaining blocker is identified with the exact failing command and reason. - -## Validation Commands - -- `./tools/init_dev_env.sh` -- `cd apps/mobile_chat_app && flutter pub get` -- `cd apps/mobile_chat_app/ios && pod install` -- `cd apps/mobile_chat_app && flutter build ios --no-codesign` - -## Current Outcome - -- Repository bootstrap completed after fixing the local Flutter shim to point at `/Users/admin/.local/tools/flutter/bin/flutter`. -- CocoaPods 1.16.2 is installed through Homebrew. -- `file_picker` was upgraded to a version that no longer emits the desktop default plugin warnings during `flutter pub get`. -- The iOS Podfile now explicitly declares `platform :ios, '13.0'`, removing the CocoaPods platform warning. -- `flutter pub get` and `pod install` complete successfully. -- iOS app build is still blocked by Xcode reporting that iOS 26.5 is not installed as a usable platform/destination, even after downloading the iOS 26.5 Simulator runtime once. A repeated download was stopped because it restarted from 0% and was not making useful progress toward a new build result. diff --git a/docs/plans/2026-05-18-22-22-+08-highlight-selection-offsets.md b/docs/plans/2026-05-18-22-22-+08-highlight-selection-offsets.md deleted file mode 100644 index 4ae387df..00000000 --- a/docs/plans/2026-05-18-22-22-+08-highlight-selection-offsets.md +++ /dev/null @@ -1,35 +0,0 @@ -# Highlight Selection Offsets - -## Background - -PR 255 added highlight creation and deletion through Flutter selection toolbars, but the implementation still identifies highlights by selected text. That is fragile for repeated text, markdown rendering, code blocks, tables, and selections that span multiple rendered blocks. The UI needs to keep the floating toolbar path working while storing enough range information to render a single logical highlight as multiple visual subsegments. - -## Goals - -- Restore a reliable selection toolbar for assistant message highlights. -- Resolve the selected message before creating or deleting a highlight. -- Prefer stored message offsets over text search when rendering highlights. -- Render a logical highlight across markdown paragraphs, code blocks, and table cells as separate visual subsegments. -- Preserve the existing backend API shape while improving client-side behavior for existing highlight records. - -## Implementation Plan - -1. Move highlight selection menu handling from the whole message list to per-assistant-message selection scopes so the selected text is associated with a stable message id. -2. Add message-level selected text resolution that maps rendered selection text back to normalized message offsets when possible, falling back to bounded text search for older or ambiguous cases. -3. Update highlight rendering to use `startOffset` and `endOffset` first, splitting each rendered text run by range intersections instead of searching for matching text inside every span. -4. Apply the same range-splitting path to markdown paragraph text, block quotes, code blocks, and table cell text so one highlight can produce multiple visual subsegments. -5. Add focused widget tests for duplicate text, code block/table/cross-paragraph rendering, and callback offsets. -6. Check whether code maps need updates because this changes feature entry paths and highlight logic. - -## Acceptance Criteria - -- Selecting text in an assistant message shows `划线` in the floating selection toolbar. -- Creating a highlight calls the callback with the selected assistant message id and a non-null range when the selection can be resolved. -- Existing highlights with stored ranges render only at their intended message offsets, even when the same text appears elsewhere. -- A single highlight range can visibly underline multiple rendered pieces when it crosses paragraph, markdown, code, or table boundaries. -- Selecting an existing highlighted range can show `删除划线` and delete the matching highlight without searching other messages first. - -## Validation Commands - -- `./tools/init_dev_env.sh` -- `cd apps/mobile_chat_app && flutter test test/message_list_test.dart` diff --git a/docs/plans/2026-05-19-10-50-+08-unified-authenticated-api-client.md b/docs/plans/2026-05-19-10-50-+08-unified-authenticated-api-client.md deleted file mode 100644 index 93742ac2..00000000 --- a/docs/plans/2026-05-19-10-50-+08-unified-authenticated-api-client.md +++ /dev/null @@ -1,91 +0,0 @@ -# Unified Authenticated API Client - -## Background - -The mobile chat app currently uses several different authentication patterns for API calls. Some UI code caches an auth token and decides whether actions should run. Some API services require callers to pass `required String token` and manually build `Authorization` headers. Other services call `AuthService.getToken()` internally but still duplicate header construction. - -This makes user actions fragile. A visible action can silently do nothing when the UI token cache is stale, and every service has to reimplement token lookup, authorization header formatting, missing-token handling, and unauthorized response handling. - -The target architecture is a single authenticated HTTP path used by every login-required API call. - -## Goals - -- Move token lookup and `Authorization` header injection out of UI widgets and individual feature services. -- Make UI actions call business operations without checking token state first. -- Use one shared authenticated client for missing-token and unauthorized-response behavior. -- Keep API services focused on business parameters and response parsing. -- Preserve testability by allowing an injected underlying `http.Client`. -- Migrate incrementally, starting with the text highlight flow that exposed the current no-op failure. - -## Implementation Plan - -1. Define the authenticated API boundary. - - Add an authenticated HTTP client or request executor near the auth/API infrastructure. - - The client should fetch the current token through `AuthService.getToken()` for each authenticated request. - - The client should inject `Authorization: Bearer ` and merge any caller-provided headers. - - The client should expose request methods needed by existing services, such as `get`, `post`, `put`, `patch`, and `delete`. - - The client should accept an optional underlying `http.Client` so tests can keep using mock clients. - -2. Standardize auth error semantics. - - Introduce a missing-token error for authenticated requests made without a token. - - Introduce an unauthorized error for HTTP 401 responses. - - Keep the first implementation local to API/service boundaries and avoid forcing global navigation behavior into the client. - - Add tests proving missing token does not become a silent no-op. - -3. Migrate the text highlight application flow first. - - Convert `TextHighlightApiService` so callers no longer pass a token. - - Route create/list/delete highlight requests through the authenticated client. - - Keep highlight-specific request bodies, response parsing, and range logic inside the highlight service. - - Update `ChatScreen` highlight handlers so they invoke the service directly and handle typed auth errors with visible feedback. - - Verify the user-visible flow: select text, show the floating toolbar, click the highlight action, send a network request, and render the highlight after success. - -4. Remove token gating from highlight UI actions. - - Ensure toolbar visibility and action availability are based on selection state and callback wiring, not `_authToken != null`. - - Treat authentication failure as an operation result, not as a reason to suppress the UI action. - - Add a regression test for the previous failure mode where the toolbar was visible but the highlight action performed no request. - -5. Migrate shared chat resource services. - - Convert `TodoApiService` and `AssetTableApiService` to use the authenticated client. - - Remove `required String token` from public methods that only need authentication as transport context. - - Update call sites to pass business inputs only. - - Add focused service tests for authorization header injection and missing-token handling through the shared client. - -6. Migrate chat history operations. - - Convert `ChatHistoryApiService` to the authenticated client. - - Remove duplicated `Authorization` header construction from conversation, message, deletion, metadata, and usage-reporting methods. - - Update `ChatScreen` and related call sites so chat history operations no longer depend on cached token state. - - Keep any UI-level logged-in state only for display, routing, or initial loading decisions. - -7. Migrate settings and LLM configuration APIs. - - Convert `LlmConfigService` from direct `AuthService.getToken()` calls and manual headers to the authenticated client. - - Preserve existing settings error messages while routing auth errors through the shared semantics. - - Add or update tests for settings API calls so they validate behavior through the shared client. - -8. Consolidate UI auth state usage. - - Audit `ChatScreen` and other feature screens for `_authToken`, `authToken`, and token-based action gating. - - Remove cached token reads where the value is only used to call an API. - - Keep explicit auth checks only at application boundaries where they control routing, login state display, or session bootstrap. - -9. Update tests, code maps, and documentation. - - Add authenticated-client unit tests. - - Update feature tests for highlight selection and highlight creation. - - Run the relevant Flutter test suites and analyzer. - - Update `docs/code_maps/feature_map.yaml` and `docs/code_maps/logic_map.yaml` because this change touches feature entry points, business logic, and tests. - -## Acceptance Criteria - -- Highlight creation no longer depends on `ChatScreen._authToken` being populated before the toolbar action is wired. -- Clicking the highlight action after selecting text sends an authenticated request when a token exists. -- Missing token produces a visible user-facing error instead of a silent no-op. -- Text highlight, todo, asset table, chat history, and LLM configuration services use the same authenticated request path after their migration phases. -- No feature service manually formats `Authorization: Bearer ` after it has been migrated. -- UI code no longer passes raw token strings into migrated services. -- Tests cover authenticated header injection, missing-token behavior, unauthorized-response behavior, and the highlight toolbar action path. -- Code maps are reviewed and updated for the new authenticated API path and affected feature tests. - -## Validation Commands - -- `./tools/init_dev_env.sh` -- `cd apps/mobile_chat_app && PATH=/Users/admin/.local/tools/flutter/bin:$PATH flutter analyze` -- `cd apps/mobile_chat_app && PATH=/Users/admin/.local/tools/flutter/bin:$PATH flutter test test/message_list_test.dart test/highlight_selection_toolbar_web_test.dart` -- `cd apps/mobile_chat_app && PATH=/Users/admin/.local/tools/flutter/bin:$PATH flutter test -d chrome test/highlight_selection_toolbar_web_test.dart` diff --git a/docs/plans/2026-05-19-11-55-+08-highlight-toolbar-union-ranges.md b/docs/plans/2026-05-19-11-55-+08-highlight-toolbar-union-ranges.md deleted file mode 100644 index 6912a9d8..00000000 --- a/docs/plans/2026-05-19-11-55-+08-highlight-toolbar-union-ranges.md +++ /dev/null @@ -1,63 +0,0 @@ -# Highlight Toolbar And Union Ranges - -## Background - -Text highlight creation now works end to end and persisted highlights survive reloads. Two interaction and rendering gaps remain: - -- Tapping an existing highlighted span should show the same compact floating toolbar style used for selected text, with Copy and Delete Highlight actions. -- When a new highlight overlaps or touches an existing highlight, the visual result should behave like the union of those ranges. The current renderer can process overlapping ranges independently and duplicate text fragments. - -Cross-paragraph selection toolbar behavior is explicitly out of scope for this change. - -## Goals - -- Show a compact floating toolbar with `复制` and `删除划线` when the user taps an existing highlighted span. -- Reuse the same floating toolbar visual language and pointer-down action behavior as the selection toolbar. -- Merge overlapping or adjacent highlight ranges before rendering so repeated highlight coverage never duplicates text. -- Keep persisted highlight records compatible with the existing backend API. -- Document the toolbar behavior in a design-md style design note. - -## Implementation Plan - -1. Update highlight tap interaction. - - Replace the old popup menu for tapped highlighted spans with a transparent `showGeneralDialog` overlay. - - Position the overlay near the tap point and clamp it within the viewport. - - Render the same `_SelectionFloatingToolbar` component with `复制` and `删除划线`. - - Trigger copy/delete on pointer down, matching the fixed selection toolbar behavior. - -2. Merge highlight ranges before rendering. - - Convert stored offset highlights and legacy selected-text highlights into `_HighlightRange` values as before. - - Sort ranges by start/end. - - Coalesce overlapping or adjacent ranges into one visual range. - - Preserve a representative highlight id for delete actions and concatenate selected text only for copy fallback. - -3. Strengthen tests. - - Add a widget test for overlapping highlights that verifies text is not duplicated. - - Add a widget test that tapping a highlighted span shows `复制` and `删除划线` and calls the delete callback. - - Keep the existing Chrome selection toolbar tests. - -4. Add design-md documentation. - - Create a small design note under `docs/design/` describing the floating toolbar's trigger states, actions, placement, dismissal, and visual tokens. - - Reference the design note from code maps for the text highlight feature. - -5. Update code maps and validate. - - Update `docs/code_maps/feature_map.yaml` and `docs/code_maps/logic_map.yaml`. - - Run Flutter format, analyzer, and focused tests. - - Validate code map YAML. - -## Acceptance Criteria - -- Tapping an existing highlighted span opens a floating toolbar with `复制` and `删除划线`. -- Tapping `复制` from that toolbar copies the highlighted text. -- Tapping `删除划线` invokes the delete callback for the tapped highlight. -- Overlapping, contained, or adjacent highlight records render as one continuous visual highlight without duplicated text. -- Existing non-overlapping highlights still render in the correct positions. -- The design note documents the floating toolbar in a reusable design-md format. - -## Validation Commands - -- `cd apps/mobile_chat_app && PATH=/Users/admin/.local/tools/flutter/bin:$PATH dart format lib/features/chat/widgets/message_list.dart test/message_list_test.dart test/highlight_selection_toolbar_web_test.dart` -- `cd apps/mobile_chat_app && PATH=/Users/admin/.local/tools/flutter/bin:$PATH flutter test test/message_list_test.dart test/highlight_selection_toolbar_web_test.dart` -- `cd apps/mobile_chat_app && PATH=/Users/admin/.local/tools/flutter/bin:$PATH flutter test -d chrome test/highlight_selection_toolbar_web_test.dart` -- `cd apps/mobile_chat_app && PATH=/Users/admin/.local/tools/flutter/bin:$PATH flutter analyze` -- `ruby -e "require 'yaml'; YAML.load_file('docs/code_maps/feature_map.yaml'); YAML.load_file('docs/code_maps/logic_map.yaml'); puts 'code maps yaml ok'"` diff --git a/docs/plans/2026-05-19-13-02-CST-highlight-list-and-auth-api-architecture.md b/docs/plans/2026-05-19-13-02-CST-highlight-list-and-auth-api-architecture.md deleted file mode 100644 index 6fdc8ee8..00000000 --- a/docs/plans/2026-05-19-13-02-CST-highlight-list-and-auth-api-architecture.md +++ /dev/null @@ -1,63 +0,0 @@ -# Highlight List Rendering and Authenticated API Architecture - -## Background - -The merged text highlight work made the floating toolbar visible, persisted highlights through the backend, rendered normal stored ranges, and added toolbar actions for existing highlights. Main now has a usable baseline, but two larger follow-up areas remain. - -First, hierarchical Markdown list content can send highlight create requests and receive responses, but the rendered structure does not reliably show the persisted highlight. The current assistant message renderer parses each Markdown list line into a marker and text row, but it does not preserve nested list structure as first-class render metadata. Highlight spans are applied to visible text ranges, while indentation and markers are rendered separately. - -Second, authenticated API calls are not yet architecturally consistent. Text highlight calls now use `AuthenticatedApiClient`, but chat history, todo resources, asset table resources, LLM config, and parts of `ChatScreen` still manually fetch, cache, pass, or format auth tokens. - -## Goals - -- Make text highlights render reliably in hierarchical Markdown list items without losing structure, duplicating text, or depending on selected-text search. -- Represent a logical highlight as one or more visible subsegments when the source range crosses rendered Markdown structures. -- Add regression coverage for nested list highlighting, including persisted highlights after reload-like rendering. -- Move authenticated mobile API calls toward a single `AuthenticatedApiClient` path so UI code no longer formats `Authorization` headers or passes token strings as business parameters. -- Keep the two tracks staged so highlight rendering fixes can ship independently from the broader auth migration. - -## Implementation Plan - -1. Stabilize the highlight renderer for hierarchical lists. - - Add focused widget tests for nested unordered and ordered list content with stored highlight ranges. - - Verify whether failures come from source-offset mapping, list item rendering, or marker/text split boundaries. - - Extend the Markdown block/list metadata so rendered list text keeps source ranges, nesting level, marker identity, and visible text ranges. - - Apply highlight splitting against visible source-backed segments, allowing one persisted highlight to render as multiple subsegments when needed. - - Preserve current behavior for paragraphs, code blocks, tables, overlapping ranges, tapped highlight toolbar, and selection toolbar actions. - -2. Improve highlight selection normalization. - - Normalize selections that include list indentation or markers to a durable visible-text range when the selected text is inside a list item. - - Keep backend payload compatibility with `messageId`, `selectedText`, `startOffset`, `endOffset`, and `color`. - - Avoid creating duplicate visual text when new highlights overlap existing highlight records. - -3. Define the authenticated API migration boundary. - - Treat `AuthenticatedApiClient` as the only transport for logged-in REST APIs in the mobile app. - - Extend it only where needed for migrated services, such as additional HTTP verbs and JSON request helpers. - - Keep auth errors typed through `MissingAuthTokenException` and `UnauthorizedApiException`. - -4. Migrate authenticated API services in low-risk phases. - - First migrate resource services that are close to text highlights: todo lists and asset tables. - - Then migrate chat history calls, which have wider UI impact. - - Then migrate LLM config service away from repeated manual `AuthService.getToken()` calls. - - Finally remove UI-level token plumbing where services no longer require it. - -5. Update documentation and code maps. - - Update `docs/code_maps/feature_map.yaml` and `docs/code_maps/logic_map.yaml` if feature entry points, highlight logic, tests, or API architecture indexes change. - - Keep the highlight floating toolbar design note aligned if toolbar behavior changes. - -## Acceptance Criteria - -- A stored highlight inside a nested Markdown list item renders visibly at the correct text after widget rebuild. -- Nested list indentation and markers remain visually correct while the highlighted text is rendered once. -- A highlight range that overlaps list structure boundaries is normalized or split so visible list text is highlighted instead of silently disappearing. -- Existing highlight tests for paragraphs, code blocks, tables, overlapping ranges, toolbar copy, and delete behavior continue to pass. -- Migrated services no longer accept `required String token` or manually create `Authorization: Bearer ...` headers. -- UI code does not block migrated API actions only because a cached `_authToken` field is null; missing auth is reported through the service error path. -- Code maps identify the highlight renderer and authenticated API client as regression-sensitive areas. - -## Validation Commands - -- `./tools/init_dev_env.sh` -- `cd apps/mobile_chat_app && flutter test test/message_list_test.dart test/highlight_selection_toolbar_web_test.dart test/text_highlight_api_service_test.dart` -- `cd apps/mobile_chat_app && flutter test` -- `npx js-yaml docs/code_maps/feature_map.yaml > /dev/null && npx js-yaml docs/code_maps/logic_map.yaml > /dev/null && echo "code maps yaml ok"` diff --git a/docs/plans/2026-05-19-14-27-CST-tool-call-thinking-group.md b/docs/plans/2026-05-19-14-27-CST-tool-call-thinking-group.md deleted file mode 100644 index a17cd8d0..00000000 --- a/docs/plans/2026-05-19-14-27-CST-tool-call-thinking-group.md +++ /dev/null @@ -1,36 +0,0 @@ -# Tool Call Thinking Group - -## Background - -Tool call result messages can render large JSON payloads inline in the chat stream. Users need to know that the assistant is processing, but the raw tool type, arguments, and payload are diagnostic details rather than primary chat content. - -## Goals - -- Collapse adjacent tool call messages into one compact inline status object. -- Visually attach the assistant answer immediately after a tool group to that group by suppressing the repeated assistant header. -- Use the user-facing labels `正在思考 completed/total` while the group is active and `思考过程 completed/total` when the group is complete. -- Do not show tool names, tool types, arguments, or JSON payloads in the outer chat stream. -- Keep non-adjacent tool groups separate. - -## Implementation Plan - -1. Identify agent-loop tool messages in `MessageList`, including tool start and tool result phases. -2. Group only consecutive tool messages during message list rendering. -3. Render a single status row for each group with completed and total counts. -4. When a plain assistant text message immediately follows a tool group, suppress its repeated agent header and let its metadata close the combined visual turn. -5. Keep reasoning and step-text agent-loop phases on their existing rendering path. -6. Add widget tests for active/completed groups, adjacent merging, non-adjacent separation, and header suppression after a tool group. - -## Acceptance Criteria - -- A single in-progress tool displays `正在思考 0/1`. -- A completed tool displays `思考过程 1/1`. -- Adjacent completed tools display one row, for example `思考过程 2/2`. -- Non-adjacent tool calls display separate rows. -- The assistant text immediately after a tool group does not show a duplicate assistant header. -- Raw `Tool: ...` JSON content is not visible in the chat stream for grouped tool calls. - -## Validation Commands - -- `cd apps/mobile_chat_app && flutter test test/message_list_test.dart` -- `cd apps/mobile_chat_app && flutter analyze` diff --git a/docs/plans/2026-05-19-17-06-CST-navigation-resources-highlights.md b/docs/plans/2026-05-19-17-06-CST-navigation-resources-highlights.md deleted file mode 100644 index 8bf916fd..00000000 --- a/docs/plans/2026-05-19-17-06-CST-navigation-resources-highlights.md +++ /dev/null @@ -1,35 +0,0 @@ -# Navigation Resources Highlights - -## Background - -The chat navigation drawer has a Resources tab that currently lists todo lists and asset tables. Text highlights are persisted through `/api/resources/highlights` and rendered in assistant messages, but they are not visible from the Resources navigation surface. - -## Goals - -- Show saved text highlights in the chat navigation Resources tab alongside other atomic resources. -- Sort Resources tab items by update time descending across all resource types. -- Add a type filter at the top of the Resources tab. -- Preserve the existing todo-list and asset-table resource behavior. -- Update code maps because the change affects a user-visible navigation entry point and highlight resource behavior. - -## Implementation Plan - -1. Extend the chat navigation resource model with a text-highlight resource type and an `updatedAt` sort key. -2. Update the Resources tab to sort atomic resources by update time descending and expose type filter chips. -3. Feed loaded highlights from `ChatScreen` into the navigation resources collection. -4. Add or update widget tests covering highlight resources, sorting, filtering, and preview behavior. -5. Update `docs/code_maps/feature_map.yaml` and `docs/code_maps/logic_map.yaml`. - -## Acceptance Criteria - -- Saved highlights appear in the Resources tab after highlights are loaded. -- Resources are not grouped by type; all resource items are shown as atomic rows sorted by newest update time first. -- The Resources tab has a top filter that can narrow the list by resource type. -- Highlight resources use a distinct highlight icon and show the highlighted text as the resource title. -- Tapping a highlight resource opens a preview page identifying it as a text highlight. -- Existing todo-list and asset-table resources still render with their current labels and icons. - -## Validation Commands - -- `cd apps/mobile_chat_app && flutter test test/chat_navigation_page_test.dart` -- `npx js-yaml docs/code_maps/feature_map.yaml > /dev/null && npx js-yaml docs/code_maps/logic_map.yaml > /dev/null && echo "code maps yaml ok"` diff --git a/docs/plans/2026-05-19-17-38-CST-subsection-rename-menu.md b/docs/plans/2026-05-19-17-38-CST-subsection-rename-menu.md deleted file mode 100644 index 01effcf5..00000000 --- a/docs/plans/2026-05-19-17-38-CST-subsection-rename-menu.md +++ /dev/null @@ -1,36 +0,0 @@ -# Subsection Rename Menu - -## Background - -The chat header subsection dropdown currently shows placeholder actions for subsection rename and archive. Users need the menu to be grouped, hide current-section actions while in the main section, and allow a child subsection to be renamed from the dropdown. - -## Goals - -- Rework the subsection dropdown into the requested groups. -- Enable subsection rename for non-main subsections. -- Keep main section immutable from this menu. -- Persist renamed subsection labels through the existing chat naming API shape without breaking channel rename behavior. - -## Implementation Plan - -1. Update the mobile chat header menu builder so the first group contains only "新建子区", the second group appears only for child subsections with a small "当前组" title and rename/archive actions, and the final group lists main plus child subsections. -2. Add a subsection rename dialog using the same validation style as channel rename. -3. Archive child subsections locally from the current channel list and return the user to the main section. -4. Extend chat name persistence to support optional `threadId` while preserving existing channel-only callers. -5. Update code maps and focused tests for API serialization and route behavior. - -## Acceptance Criteria - -- In the main section, the subsection dropdown shows "新建子区" and the section list but does not show "当前组", "改名", or "归档". -- In a child subsection, the dropdown shows "当前组" as a small muted group title above "改名" and "归档". -- Choosing "改名" opens a dialog, validates blank and duplicate names, and updates the subsection label after saving. -- Choosing "归档" removes the current child subsection from the dropdown and switches back to "主区". -- Subsection display names can be saved and loaded with `threadId` without changing existing channel name behavior. - -## Validation Commands - -- `./tools/init_dev_env.sh --flutter-home /Users/admin/.local/tools/flutter --no-doctor` -- `cd apps/mobile_chat_app && dart format lib/features/chat/chat_screen.dart lib/features/chat/chat_history_api_service.dart test/chat_history_api_service_test.dart` -- `cd apps/mobile_chat_app && flutter test test/chat_history_api_service_test.dart` -- `cd apps/node_backend && npm test -- src/routes/chat.test.ts` -- `npx js-yaml docs/code_maps/feature_map.yaml >/dev/null && npx js-yaml docs/code_maps/logic_map.yaml >/dev/null` diff --git a/docs/plans/2026-05-20-11-13-CST-local-cloud-db-debug.md b/docs/plans/2026-05-20-11-13-CST-local-cloud-db-debug.md deleted file mode 100644 index 94dd3d4a..00000000 --- a/docs/plans/2026-05-20-11-13-CST-local-cloud-db-debug.md +++ /dev/null @@ -1,38 +0,0 @@ -# Local Cloud DB Debug Mode - -## Background - -The backend can start with Turso credentials from `.env.local`, but the Express -app middleware always runs migrations before serving requests. That makes local -debug sessions against the production Turso database risky and can also block -simple health checks when the remote migration request times out. - -## Goals - -- Allow local backend requests to skip migrations when `AUTO_MIGRATE=false`. -- Keep the existing migration behavior unchanged when the flag is absent. -- Preserve the existing Flutter test-token quick login flow for local web - testing against a local API. - -## Implementation Plan - -1. Update the Express migration middleware to treat `AUTO_MIGRATE=false` as an - explicit request to skip migrations. -2. Add focused backend coverage for the skip behavior. -3. Validate that the backend can start against Turso and serve `/api/health` - with migrations disabled. - -## Acceptance Criteria - -- A local backend started with `AUTO_MIGRATE=false` serves `/api/health` without - attempting to run migrations. -- Existing request-time migration behavior remains active when - `AUTO_MIGRATE` is not set to `false`. -- Flutter web can be launched with `BRICKS_API_BASE_URL` and - `BRICKS_TEST_TOKEN` from `.env.local`. - -## Validation Commands - -- `npm test -- src/app.test.ts` -- `AUTO_MIGRATE=false npm run dev` -- `curl http://localhost:3010/api/health` diff --git a/docs/plans/2026-05-21-10-41-CST-channel-new-ui-harness.md b/docs/plans/2026-05-21-10-41-CST-channel-new-ui-harness.md deleted file mode 100644 index 70d1c6e3..00000000 --- a/docs/plans/2026-05-21-10-41-CST-channel-new-ui-harness.md +++ /dev/null @@ -1,46 +0,0 @@ -# Channel New UI Evidence Harness - -## Background - -The New Channel flow is user-visible and spans Flutter UI state, authenticated -API calls, and persisted `chat_channel_names` rows. A final screenshot alone is -not enough to diagnose failures because the same symptom can come from local UI -state, API persistence, database writes, or refresh-time hydration. - -## Goals - -- Provide one reusable command that validates the sidebar New Channel flow in a - real browser against the local backend. -- Capture UI, API, and direct Turso database checkpoints so failures identify - the broken layer. -- Store all generated evidence under ignored `.cache/` paths and keep secrets - in `.env.local`. - -## Implementation Plan - -1. Add a `tools/evidence/channel_new_ui_harness/run.sh` entrypoint that loads - `.env.local`, starts the backend and Flutter web server when needed, prepares - local harness dependencies, and runs the browser validation. -2. Add a Playwright-based Node harness that performs Quick Login, opens the - sidebar, creates a unique channel through the New Channel UI, checks API and - Turso rows, reopens the sidebar, refreshes the page, and writes screenshots - plus JSON checkpoint files. -3. Add a README documenting the single-command workflow, required environment - variables, cloud-DB write behavior, evidence layout, and failure diagnosis. - -## Acceptance Criteria - -- Running `tools/evidence/channel_new_ui_harness/run.sh` produces a - `summary.json` under `.cache/evidence/channel-new-ui//`. -- The harness records UI screenshots before creation, after creation, and after - refresh. -- The harness records `/api/chat/channel-names` responses and a direct - `chat_channel_names` query for the generated channel name. -- A passing run proves the New Channel sidebar flow displays immediately, - persists through the API/database layer, survives sidebar reopen, and survives - browser refresh. - -## Validation Commands - -- `tools/evidence/channel_new_ui_harness/run.sh` -- `test -f .cache/evidence/channel-new-ui//summary.json` diff --git a/docs/plans/2026-05-21-12-03-CST-evidence-checkpoint-testing-docs.md b/docs/plans/2026-05-21-12-03-CST-evidence-checkpoint-testing-docs.md deleted file mode 100644 index 40bc95ca..00000000 --- a/docs/plans/2026-05-21-12-03-CST-evidence-checkpoint-testing-docs.md +++ /dev/null @@ -1,40 +0,0 @@ -# Evidence Checkpoint Testing Docs - -## Background - -The channel New UI harness proved useful because it tested the same path a user -uses in the product, while also checking the API and database state behind that -visible result. The reusable lesson is not specific to channels: browser -evidence is most useful when it is paired with scoped data checkpoints and an -explicit test identity. - -## Goals - -- Document the reusable local browser plus API plus database checkpoint pattern. -- Make the fixture-user boundary explicit so cloud database rows are not - confused with rows for a different production user. -- Link the concrete channel New UI harness to the general testing pattern. -- Keep code maps aligned with the new testing documentation. - -## Implementation Plan - -1. Add a reusable testing note under `docs/testing/` that describes the - environment shape, checkpoint layers, artifact naming, and failure reading. -2. Update the channel New UI harness README to link to the general note and - explain that cloud Turso rows are scoped to `FIXTURE_USER_ID`. -3. Update code maps so future agents can discover the general testing note from - the backend/runtime evidence entry. - -## Acceptance Criteria - -- The documentation explains why the harness can write a row that is not visible - to another logged-in production user. -- The documentation describes UI, API, and direct database checkpoints in a - reusable way. -- The channel harness README points to the reusable documentation. -- YAML code maps remain parseable. - -## Validation Commands - -- `npx js-yaml docs/code_maps/feature_map.yaml >/dev/null` -- `npx js-yaml docs/code_maps/logic_map.yaml >/dev/null` diff --git a/docs/plans/2026-05-21-12-09-CST-scope-config-thread-label.md b/docs/plans/2026-05-21-12-09-CST-scope-config-thread-label.md deleted file mode 100644 index 43a3507c..00000000 --- a/docs/plans/2026-05-21-12-09-CST-scope-config-thread-label.md +++ /dev/null @@ -1,61 +0,0 @@ -# Scope Config Thread Label - -## Background - -The chat conversation config dialog currently presents two tabs as `Channel` and -`Section`. The product concept has moved to `Thread`, and the underlying scope -model already uses `thread` for persisted settings. - -## Goals - -- Rename user-facing `Section` labels in the conversation config dialog to - `Thread`. -- Keep the `Instructions` field label fully visible in the dialog. -- Add English `Rename` and `Archive` actions to the top channel dropdown. -- Keep default channels protected from rename and archive actions. -- Rename the Navigation channel creation button to `New Channel`. -- Constrain the top channel dropdown height so long channel lists scroll inside - the menu. -- Keep storage/API scope names unchanged. -- Update code maps so the UI acceptance notes use the current product language. - -## Implementation Plan - -1. Update the scope config dialog tab, hint text, disabled main-scope message, - and save-error wording from section to thread. -2. Add enough top padding around the multiline instruction fields so their - floating labels are not clipped by the tab view. -3. Add a channel dropdown function group before the channel list and route the - actions to the existing channel rename/archive handlers. -4. Disable channel rename/archive menu entries when the active channel is the - default channel. -5. Update the Navigation channels tab create button and its widget test to use - English copy. -6. Add a responsive max-height constraint to the top channel popup menu. -7. Leave existing private implementation names alone unless they directly render - user-facing copy. -8. Update feature and logic code maps for the `Channel / Thread` wording and - channel dropdown management actions. - -## Acceptance Criteria - -- Opening the top-right conversation config dialog shows `Channel` and `Thread` - tabs. -- In the main thread, the thread tab explains that the main thread uses channel - instructions only and cannot be saved. -- In a child thread, the thread tab describes thread-specific narrower context. -- The `Instructions` label is fully visible above both multiline fields. -- The top channel dropdown shows `Rename` and `Archive` actions above the - channel list. -- `Rename` and `Archive` are disabled for the default channel. -- Non-default channel rename and archive actions use the same persistence paths - as the navigation sidebar actions. -- The Navigation channels tab create button reads `New Channel`. -- Long channel dropdown lists stay within a bounded height and scroll inside the - popup. -- Existing persisted channel/thread instruction behavior is unchanged. - -## Validation Commands - -- `./tools/init_dev_env.sh` -- `cd apps/mobile_chat_app && flutter analyze` diff --git a/docs/plans/2026-05-21-12-38-CST-local-manual-ai-test-env-docs.md b/docs/plans/2026-05-21-12-38-CST-local-manual-ai-test-env-docs.md deleted file mode 100644 index 72141c16..00000000 --- a/docs/plans/2026-05-21-12-38-CST-local-manual-ai-test-env-docs.md +++ /dev/null @@ -1,43 +0,0 @@ -# Local Manual AI Test Environment Docs - -## Background - -The local manual test setup proved useful for validating realistic chat behavior -without deploying: the browser runs a local Flutter Web build, the API is local, -the database is cloud Turso, auth uses the fixture test token, and the fixture -user can have a real Gemini configuration created through the local backend. - -## Goals - -- Document the reusable manual test setup for local API plus local Flutter Web - plus cloud Turso. -- Explain how to provide an AI provider token without pasting it into chat. -- Explain the encryption boundary for fixture-user LLM configs written through - the local backend. -- Update the evidence-driven browser-test skill so future agents reuse this - pattern. - -## Implementation Plan - -1. Add a `docs/testing/` guide for launching the local manual AI test - environment. -2. Include environment variables, LLM config creation, startup commands, - verification probes, and shutdown commands. -3. Update the evidence skill with a manual cloud-DB test mode and token-handling - guidance. -4. Update code maps so future agents can discover the guide. - -## Acceptance Criteria - -- A developer can start the local backend and Flutter Web app from the guide. -- The guide keeps provider tokens in `.env.local` and never asks for them in - conversation. -- The guide explains why fixture-user configs may not be visible to a real - production user. -- The guide explains how `ENCRYPTION_KEY` affects local reads of LLM configs - written to cloud Turso. - -## Validation Commands - -- `npx js-yaml docs/code_maps/feature_map.yaml >/dev/null` -- `npx js-yaml docs/code_maps/logic_map.yaml >/dev/null` diff --git a/docs/plans/2026-05-21-14-17-CST-evidence-case-loop-skill.md b/docs/plans/2026-05-21-14-17-CST-evidence-case-loop-skill.md deleted file mode 100644 index 6ef72169..00000000 --- a/docs/plans/2026-05-21-14-17-CST-evidence-case-loop-skill.md +++ /dev/null @@ -1,37 +0,0 @@ -# Evidence Case Loop Skill - -## Background - -The existing `evidence-driven-browser-test-env` skill describes how to gather -browser, API, database, screenshot, and secret-safe evidence. A separate -case-level workflow is useful for stubborn bugs: create a dedicated evidence -case directory, prove the bug with baseline evidence, fix code, then rerun the -same path to prove the fix. - -## Goals - -- Add a concise repo skill for the case-level evidence loop. -- Keep browser startup, screenshots, auth bypass, API/DB checks, and secret - handling delegated to `evidence-driven-browser-test-env`. -- Standardize the per-case directory contract around `AGENTS.md`, `run.sh`, - flow scripts, checkpoint scripts, and `summary.json`. - -## Implementation Plan - -1. Create `.agents/skills/evidence-case-loop/SKILL.md`. -2. Define the skill trigger, dependency boundary, case directory shape, loop - steps, evidence output convention, and final reporting expectations. -3. Keep the skill self-contained and avoid auxiliary README files. - -## Acceptance Criteria - -- The skill clearly says it does not decide when evidence is required. -- The skill explicitly references `evidence-driven-browser-test-env` for - browser/environment mechanics. -- The skill requires baseline-before-fix and rerun-after-fix evidence. -- The skill uses `AGENTS.md`, not `README.md`, as the case contract. - -## Validation Commands - -- `test -f .agents/skills/evidence-case-loop/SKILL.md` -- `sed -n '1,220p' .agents/skills/evidence-case-loop/SKILL.md` diff --git a/docs/plans/2026-05-21-14-20-CST-channel-dropdown-height-evidence-case.md b/docs/plans/2026-05-21-14-20-CST-channel-dropdown-height-evidence-case.md deleted file mode 100644 index bfba3aee..00000000 --- a/docs/plans/2026-05-21-14-20-CST-channel-dropdown-height-evidence-case.md +++ /dev/null @@ -1,35 +0,0 @@ -# Channel Dropdown Height Evidence Case - -## Background - -The top chat channel dropdown can become too tall when many channels exist. The -fix added a popup menu height constraint, but the behavior needs browser -evidence rather than only static code inspection. - -## Goals - -- Add a case-specific evidence harness following `evidence-case-loop`. -- Verify the current implementation with a real Flutter Web browser run. -- Capture screenshots and numeric checkpoints for menu height and internal - scrolling. - -## Implementation Plan - -1. Create `tools/evidence/channel_dropdown_height/` with an `AGENTS.md` - contract, `run.sh` entrypoint, and Playwright flow script. -2. Ensure the fixture user has enough channel names for the menu to overflow. -3. Open the real app, use test login, click the channel dropdown, capture - screenshots, measure popup height from image differences, and verify menu - content scrolls internally. -4. Write evidence to `.cache/evidence/channel-dropdown-height//`. - -## Acceptance Criteria - -- The harness records `summary.json` with named checks. -- The harness captures before/open/after-scroll screenshots. -- The popup height is bounded below the configured threshold. -- A wheel event changes pixels inside the popup without requiring page scroll. - -## Validation Commands - -- `tools/evidence/channel_dropdown_height/run.sh` diff --git a/docs/plans/2026-05-21-15-49-CST-local-llm-env-config.md b/docs/plans/2026-05-21-15-49-CST-local-llm-env-config.md deleted file mode 100644 index 17bc3211..00000000 --- a/docs/plans/2026-05-21-15-49-CST-local-llm-env-config.md +++ /dev/null @@ -1,46 +0,0 @@ -# Local LLM Env Config - -## Background - -Manual local testing should not require writing a local provider key into the -cloud `api_configs` table. The backend currently resolves LLM runtime config -only from user database rows, which makes local model testing risky when the -selected database is shared with a real user. - -## Goals - -- Add an explicit dev/test-only environment fallback for LLM runtime config. -- Keep production behavior unchanged unless the fallback is explicitly enabled. -- Ignore the fallback in production even if the local env variables are - accidentally present. -- Support the current `.env.local` Gemini variables without writing to Turso. -- Cover provider/model/endpoint/key resolution with backend unit tests. - -## Implementation Plan - -1. Add a `LOCAL_LLM_CONFIG_ENABLED=true` gate in `llm_service.ts` plus a - local/dev runtime guard. -2. Resolve local config from generic `LOCAL_LLM_*` variables, with Gemini - aliases for existing local usage. -3. Prefer the local env config before database configs only when enabled. -4. Export a small test-only resolver wrapper and add `llm_service.test.ts`. -5. Update local manual testing docs to use env fallback instead of creating a - fixture-user database config. - -## Acceptance Criteria - -- Without `LOCAL_LLM_CONFIG_ENABLED=true`, LLM config still comes from - `api_configs`. -- In production, LLM config still comes from `api_configs` even if - `LOCAL_LLM_CONFIG_ENABLED=true` is accidentally set. -- With `LOCAL_LLM_CONFIG_ENABLED=true` and `GEMINI_API_KEY`, runtime config uses - the env key/model only when the backend is running in an explicit local/dev - mode and does not need database rows. -- Invalid local endpoints still fail endpoint validation. -- Local manual testing docs no longer recommend writing provider keys to cloud - Turso as the default path. - -## Validation Commands - -- `cd apps/node_backend && npm test -- src/llm/llm_service.test.ts` -- `cd apps/node_backend && npm run type-check` diff --git a/docs/plans/2026-05-21-15-55-CST-thread-menu-copy.md b/docs/plans/2026-05-21-15-55-CST-thread-menu-copy.md deleted file mode 100644 index 17eef12d..00000000 --- a/docs/plans/2026-05-21-15-55-CST-thread-menu-copy.md +++ /dev/null @@ -1,34 +0,0 @@ -# Thread Menu Copy - -## Background - -The chat app bar thread dropdown still uses Chinese copy for the main thread and -new child thread action. The product language now uses `Thread` instead of -section/subsection wording. - -## Goals - -- Change the main thread label from `主区` to `Thread`. -- Change the create child thread menu item from `新建子区` to `New Thread`. -- Change the default channel display name to `Default Channel`. -- Keep thread identity and storage fields unchanged. - -## Implementation Plan - -1. Update user-facing app bar thread dropdown copy in `chat_screen.dart`. -2. Update default channel display-name copy in `chat_screen.dart` and widget - tests. -3. Update code maps so smoke checks match the current product wording. -4. Run Dart formatting and targeted Flutter analysis. - -## Acceptance Criteria - -- The app bar displays `Thread` when the active thread id is `main`. -- The app bar displays the specific thread name when a child thread is active. -- The thread dropdown create action reads `New Thread`. -- The default channel appears as `Default Channel`. - -## Validation Commands - -- `dart format apps/mobile_chat_app/lib/features/chat/chat_screen.dart` -- `cd apps/mobile_chat_app && flutter analyze` diff --git a/docs/plans/2026-05-21-16-09-CST-thread-sync-and-thinking-details.md b/docs/plans/2026-05-21-16-09-CST-thread-sync-and-thinking-details.md deleted file mode 100644 index 40c1cb10..00000000 --- a/docs/plans/2026-05-21-16-09-CST-thread-sync-and-thinking-details.md +++ /dev/null @@ -1,41 +0,0 @@ -# Thread Sync And Thinking Details - -## Background - -Creating a thread through the AI tool persists the new thread on the backend, but the currently open chat screen only refreshes message history. The thread menu can therefore remain stale until the page is refreshed. The agent-loop status UI also currently renders the generic assistant placeholder before the tool-thinking row because the placeholder is inserted first. - -## Goals - -- Refresh chat scope topology after backend message sync so newly created threads appear in the thread menu without a full page refresh. -- Render tool thinking before the generic processing placeholder for the same task. -- Rename completed thinking rows to `完成思考`. -- Make thinking rows clickable and show their details in a modal dialog. -- Convert remaining Chinese Flutter UI action labels to English. -- Remove leading icons from Navigation channel list items. -- Ignore macOS `.DS_Store` files. - -## Implementation Plan - -1. Add `.DS_Store` ignore rules. -2. Add a guarded scope-topology refresh helper in `ChatScreen` and trigger it after SSE snapshots. -3. Update `MessageList` rendering to swap adjacent placeholder/tool-loop rows for the same task. -4. Update agent-loop group labels and add a modal detail view for tool-loop and reasoning rows. -5. Update remaining Chinese UI labels in chat/navigation/composer/model settings. -6. Remove Navigation channel item leading icons. -7. Extend focused widget tests and run Flutter checks from `apps/mobile_chat_app`. - -## Acceptance Criteria - -- After an AI tool creates a thread, the active channel thread menu can show the new thread after sync without a browser refresh. -- A task with both a tool-loop row and a processing placeholder renders the thinking row first. -- Completed tool-loop rows display `完成思考 n/n`. -- Clicking a thinking/completed-thinking row opens a modal with the underlying details. -- Chat, Navigation, composer, highlight, and model settings action labels use English. -- Navigation channel rows do not show leading channel/default icons. -- `.DS_Store` files are ignored by git. - -## Validation Commands - -- `./tools/init_dev_env.sh --no-bootstrap --no-doctor` -- `cd apps/mobile_chat_app && flutter analyze` -- `cd apps/mobile_chat_app && flutter test test/message_list_test.dart` diff --git a/docs/plans/2026-05-21-16-43-CST-agent-loop-limit-empty-final.md b/docs/plans/2026-05-21-16-43-CST-agent-loop-limit-empty-final.md deleted file mode 100644 index 0c8a04d4..00000000 --- a/docs/plans/2026-05-21-16-43-CST-agent-loop-limit-empty-final.md +++ /dev/null @@ -1,32 +0,0 @@ -# Agent Loop Limit Empty Final - -## Background - -A production task reached the internal tool-call budget and persisted an empty completed assistant message. The database showed tool-call rows were present, but the final assistant message had `content_len = 0`. This is more likely a program-level loop termination case than a model intentionally replying with empty text. - -## Goals - -- Increase the default model-driven agent-loop budget to 10 steps and 50 tool calls. -- Never persist an empty completed assistant response after tool calls. -- Make budget termination visible to users with a clear explanation that the internal step or tool-call limit was reached. -- Preserve metadata that explains the stop reason for future debugging. - -## Implementation Plan - -1. Raise default loop limits in `apps/node_backend/src/routes/chat.ts`. -2. Track completed/failed tool calls while processing the agent loop. -3. Before final assistant upsert, convert an empty post-tool final response into a visible failed assistant message with a specific limit reason. -4. Add backend route tests for default limits and empty post-tool final handling. -5. Update code maps for the agent-loop limit behavior. - -## Acceptance Criteria - -- Default respond requests pass `maxSteps: 10` and `maxToolCalls: 50` to the agent loop. -- If tool calls occurred and no final text is produced, the persisted assistant message has non-empty explanatory content. -- If the tool-call or step limit was reached, the message says which internal limit was reached. -- Tests cover the regression that previously wrote an empty completed assistant message. - -## Validation Commands - -- `cd apps/node_backend && npm test -- src/routes/chat.test.ts` -- `cd apps/node_backend && npm run type-check` diff --git a/docs/plans/2026-05-21-16-48-CST-sidebar-resize-composer-focus.md b/docs/plans/2026-05-21-16-48-CST-sidebar-resize-composer-focus.md deleted file mode 100644 index bc88aa38..00000000 --- a/docs/plans/2026-05-21-16-48-CST-sidebar-resize-composer-focus.md +++ /dev/null @@ -1,29 +0,0 @@ -# Sidebar Resize and Composer Focus - -## Background - -The desktop navigation sidebar has an existing resize handle, but the active hit area is too small and can fail to receive drag gestures reliably. The chat composer also used to keep focus after sending a query, but the current streaming state disables the text field and can drop keyboard focus. - -## Goals - -- Make the desktop navigation sidebar width adjustable again through the divider handle. -- Keep the composer text field enabled and focused after sending a query. -- Add focused regression coverage for composer focus behavior. - -## Implementation Plan - -1. Widen the desktop sidebar resize handle hit target and make its gesture behavior opaque. -2. Keep the composer text field enabled during streaming so focus is not dropped. -3. Request composer focus again after submit on the next frame. -4. Update widget tests for streaming-enabled input and post-send focus retention. - -## Acceptance Criteria - -- Dragging the desktop navigation divider changes the sidebar width within the existing min/max bounds. -- Sending a query clears the draft while the composer text field keeps focus. -- The composer remains focusable while a response is streaming. - -## Validation Commands - -- `cd apps/mobile_chat_app && flutter test test/composer_bar_test.dart` -- `cd apps/mobile_chat_app && flutter analyze` diff --git a/docs/plans/2026-05-21-17-13-CST-sse-topology-refresh-rate-limit.md b/docs/plans/2026-05-21-17-13-CST-sse-topology-refresh-rate-limit.md deleted file mode 100644 index f2faf148..00000000 --- a/docs/plans/2026-05-21-17-13-CST-sse-topology-refresh-rate-limit.md +++ /dev/null @@ -1,34 +0,0 @@ -# SSE Topology Refresh Rate Limit - -## Background - -Manual browser testing showed repeated `429 Too Many Requests` errors for -`/api/chat/scopes`, `/api/chat/channel-names`, and `/api/chat/scope-settings`. -The frontend refreshed scope topology after every SSE snapshot, and each refresh -fans out to three HTTP requests. - -## Goals - -- Keep channel/thread topology updates after tool-created or tool-renamed - scopes. -- Avoid repeated topology refreshes during streaming snapshots. -- Preserve local manual test usability without hitting sync rate limits. - -## Implementation Plan - -1. Detect terminal task snapshots from SSE messages. -2. Refresh topology at most once per completed/failed/cancelled task id. -3. Keep the existing refresh method for manual or future explicit calls. -4. Rebuild the local manual test frontend and verify 429s stop. - -## Acceptance Criteria - -- A streaming task no longer triggers topology refresh on every SSE snapshot. -- A task that reaches a terminal state can still refresh channel/thread lists. -- Local manual testing can stay open without repeatedly hitting the sync rate - limit. - -## Validation Commands - -- `cd apps/mobile_chat_app && flutter analyze` -- `cd apps/mobile_chat_app && flutter build web --debug --dart-define=BRICKS_API_BASE_URL=http://127.0.0.1:3011 --dart-define=BRICKS_TEST_TOKEN="$BRICKS_TEST_TOKEN"` diff --git a/docs/plans/2026-05-21-17-23-CST-typed-chat-invalidations.md b/docs/plans/2026-05-21-17-23-CST-typed-chat-invalidations.md deleted file mode 100644 index 5187b611..00000000 --- a/docs/plans/2026-05-21-17-23-CST-typed-chat-invalidations.md +++ /dev/null @@ -1,44 +0,0 @@ -# Typed Chat Invalidations - -## Background - -The frontend previously refreshed all chat topology endpoints after terminal SSE -task snapshots. That reduced stale channel/thread UI, but it over-fetched -`/api/chat/scopes`, `/api/chat/channel-names`, and `/api/chat/scope-settings` -for ordinary chat replies and could trigger local 429 rate limits. - -## Goals - -- Surface backend tool-driven changes as typed invalidations. -- Let the Flutter client refresh only the affected chat data. -- Keep existing SSE transport shape by carrying invalidations in message - metadata. -- Preserve a coarse fallback for older snapshots that have no invalidation data. - -## Implementation Plan - -1. Add backend invalidation derivation from successful local agent tool results. -2. Persist invalidations on tool-step messages and aggregate them onto final - assistant message metadata. -3. Parse invalidations from server message metadata in Flutter. -4. Replace terminal-task blanket topology refresh with typed invalidation - consumption and targeted endpoint reloads. -5. Add parser tests for SSE invalidations. - -## Acceptance Criteria - -- A channel rename tool call emits `chat.channelNames` and the frontend reloads - channel names only. -- Channel/thread creation emits `chat.scopes`, allowing the frontend to reload - scopes and names as needed. -- Instruction changes emit `chat.scopeSettings` and the frontend reloads scope - settings only. -- Ordinary chat completion without invalidations does not call all topology - endpoints. - -## Validation Commands - -- `cd apps/node_backend && npm test -- src/routes/chat.test.ts` -- `cd apps/node_backend && npm run type-check` -- `cd apps/mobile_chat_app && flutter test test/chat_history_api_service_test.dart` -- `cd apps/mobile_chat_app && flutter analyze` diff --git a/docs/plans/2026-05-21-17-53-CST-agent-loop-empty-final-root-cause.md b/docs/plans/2026-05-21-17-53-CST-agent-loop-empty-final-root-cause.md deleted file mode 100644 index 0a3d97b3..00000000 --- a/docs/plans/2026-05-21-17-53-CST-agent-loop-empty-final-root-cause.md +++ /dev/null @@ -1,73 +0,0 @@ -# Agent Loop Empty Final Root Cause Note - -## Background - -During local manual testing, the message `我有哪些 todo` in -`task-1779354793673-0` showed a failed assistant response: - -`The assistant completed tool calls but did not produce a final answer. Please retry or narrow the request.` - -This note records the confirmed facts and the remaining uncertainty before -changing timeout or stop-reason behavior. - -## Confirmed Facts - -- The task id was `task-1779354793673-0`. -- The user message was written at `2026-05-21T17:13:13.673`. -- The final assistant message was later updated to `task_state = failed`. -- The final metadata had: - - `agentLoopStopReason.type = empty_final_after_tool_calls` - - `toolCallCount = 2` - - `completedToolCallCount = 2` - - `failedToolCallCount = 0` - - `maxToolCalls = 10` - - `stepCount = 1` - - `maxSteps = 10` -- Therefore this incident was not caused by the tool-call limit. -- Therefore this incident was not caused by the step limit. -- Both tool calls were `todo_list`. -- Both tool calls returned successful todo data. -- No provider raw finish reason, abort event, or final-step error was persisted for this task. - -## Root Cause Statement - -The confirmed root cause is: - -The backend completed successful tool calls, but the agent loop ended before a -tool-free final answer was persisted. The current code then converted that state -into `empty_final_after_tool_calls`. - -## Important Uncertainty - -We cannot prove from the current database records that the model itself -intentionally returned an empty final answer. The system did not persist enough -provider/SDK finish metadata to distinguish: - -- the provider returned a tool step and then stopped without final text -- the SDK ended the loop after tool results without another text step -- the AbortController timeout interrupted the loop before final text -- another stream finish/error condition occurred but was not recorded - -The timing makes timeout plausible, because the default loop timeout was 15s and -the task duration was close to that range, but timeout is not proven by the -persisted metadata. - -## Required Fix Direction - -- Do not label all empty post-tool finals as timeout. -- Persist the real stop reason when the SDK/provider exposes it. -- If timeout fires, persist a specific `timeout_reached` stop reason. -- Keep tool-call limit and step limit as separate stop reasons. -- Prefer per-step/model-call timeout semantics instead of one wall-clock timeout - for the whole multi-step tool loop. -- Increase the default timeout from 15s to a safer value such as 60s only as a - mitigation, not as the root-cause proof. - -## Acceptance Criteria - -- Empty final after tools must include a precise persisted stop reason when one - is known. -- Unknown stop reasons must remain marked as unknown/empty-final, not rewritten - as timeout. -- Future investigations can tell whether an incident was caused by timeout, - tool-call limit, step limit, provider finish, or an unclassified empty final. diff --git a/docs/plans/2026-05-21-18-06-CST-agent-loop-real-stop-reason.md b/docs/plans/2026-05-21-18-06-CST-agent-loop-real-stop-reason.md deleted file mode 100644 index 58632caa..00000000 --- a/docs/plans/2026-05-21-18-06-CST-agent-loop-real-stop-reason.md +++ /dev/null @@ -1,32 +0,0 @@ -# Agent Loop Real Stop Reason - -## Background - -The chat agent can complete tool calls and then persist an empty failed assistant response when no final text is produced. A recent production example proved that the failure was not caused by the tool-call limit or step limit, but the stored metadata did not contain enough raw stop information to prove whether the stream timed out, errored, or ended normally with no final text. - -## Goals - -- Increase the per-step agent loop timeout from 15 seconds to 60 seconds. -- Record timeout only when the stream was actually aborted by the timeout path. -- Preserve distinct stop reasons for tool-call limit, step limit, stream errors, SDK finish metadata, and generic empty final responses. -- Keep the timeout scoped to a single model step, while total work remains bounded by max steps and max tool calls. - -## Implementation Plan - -1. Add stream stop metadata to the agent-loop LLM streaming wrapper. -2. Reset the timeout after each completed model step instead of using one whole-loop wall clock. -3. Classify empty post-tool final responses from real stop metadata in the chat route. -4. Add route tests for default timeout configuration and timeout-specific empty-final failure metadata. - -## Acceptance Criteria - -- Default agent-loop requests use `timeoutMs: 60000`. -- A true timeout persists `agentLoopStopReason.type = "timeout_reached"` with `timeoutMs` and `timeoutStepIndex`. -- Tool-call and step limits continue to persist their own stop reason types. -- Empty final responses without timeout or explicit limits remain `empty_final_after_tool_calls`. -- Backend route tests and type checks pass. - -## Validation Commands - -- `cd apps/node_backend && npm test -- src/routes/chat.test.ts` -- `cd apps/node_backend && npm run type-check` diff --git a/docs/plans/2026-05-21-19-57-CST-thread-auto-name-source.md b/docs/plans/2026-05-21-19-57-CST-thread-auto-name-source.md deleted file mode 100644 index f78b3c3a..00000000 --- a/docs/plans/2026-05-21-19-57-CST-thread-auto-name-source.md +++ /dev/null @@ -1,42 +0,0 @@ -# Thread Auto Name Source - -## Background - -Thread display names are stored separately from scope settings in `chat_channel_names`. -New Channels already have a user-entered name, but new Threads can be created -without a display name and then appear as raw thread IDs until a manual rename -or a later refresh path supplies a name. - -## Goals - -- Keep the existing name table model. -- Add source metadata for display names. -- Give unnamed non-main Threads an immediate exact name from the first message. -- After the first assistant reply completes, attempt one generated title for - Threads still named by `first_message_exact`. -- Avoid extra frontend requests and avoid overwriting manual names. - -## Implementation Plan - -1. Extend `chat_channel_names` with `source` and `generated_name_attempted_at`. -2. Add service helpers for exact-name insertion, generation claiming, and - conditional generated-name completion. -3. Invoke the helpers from the backend `/api/chat/respond` async flow. -4. Notify the existing frontend through typed `chat.channelNames` invalidations. -5. Add backend unit coverage and a repeatable evidence harness. - -## Acceptance Criteria - -- Existing UI/API/manual rename paths write `source = manual`. -- For non-main Threads with no name, the first user message inserts - `source = first_message_exact`. -- Only one generated-name attempt can be claimed per Thread. -- A generated name only updates rows still at `source = first_message_exact`. -- Manual names are never overwritten by automatic generation. -- E2E evidence proves exact and generated naming through the real app/API path. - -## Validation Commands - -- `cd apps/node_backend && npm test -- src/routes/chat.test.ts src/services/localAgentLoopService.test.ts` -- `cd apps/node_backend && npm run type-check` -- `tools/evidence/thread_auto_name_source/run.sh` diff --git a/docs/plans/2026-05-26-18-34-CST-scheduled-actions-agent-execution.md b/docs/plans/2026-05-26-18-34-CST-scheduled-actions-agent-execution.md deleted file mode 100644 index 85bd9baf..00000000 --- a/docs/plans/2026-05-26-18-34-CST-scheduled-actions-agent-execution.md +++ /dev/null @@ -1,105 +0,0 @@ -# Scheduled Actions and Agent Execution - -## Background - -### Context - -Bricks already has a conversation-driven Agent loop that can call internal tools, persist chat messages, and update channel/thread resources. The backend is deployed on Vercel as serverless Node functions, so scheduled work should not rely on a long-lived in-process scheduler such as `node-cron`. - -The product direction is to support scheduled AI actions similar to ChatGPT Tasks or Gemini Scheduled actions: users create and manage a scheduled action through conversation, and when the schedule fires Bricks starts normal Agent execution on the user's behalf. - -### Problem - -The schedule layer, the Agent/tool layer, and future scriptable skill runtimes must stay decoupled: - -- Scheduled actions should not be tied directly to Genkit. -- Genkit should be one possible runtime for scriptable skills, not the scheduler runtime. -- Users should manage scheduled actions through conversation tools, not through a separate hidden admin path. -- A scheduled trigger should start ordinary Agent execution so the run can use any available tool or skill. - -### Motivation - -This keeps scheduled actions as a first-class resource while preserving the existing Agent/tool model. It also leaves room for scriptable Agent pipelines later: a scheduled action may trigger a prompt that invokes a skill backed by Genkit, but it may also invoke normal Bricks tools such as todos, resources, channel/thread tools, or future skills. - -## Goals - -- Add scheduled actions as a first-class user resource. -- Let users create, list, update, pause, resume, and delete scheduled actions through Agent tools. -- Use Vercel Cron only as a fixed tick that finds due scheduled actions. -- When an action is due, create a run record and start the existing Agent execution path with the scheduled prompt. -- Keep scheduled actions independent from Genkit and other specific skill runtimes. -- Preserve user identity, channel, and thread context for scheduled executions. -- Make scheduled action execution observable, retryable, and safe against duplicate claims. - -## Implementation Plan - -1. Add the scheduled action storage model. - - Create a migration for `scheduled_actions`. - - Store `user_id`, target `channel_id`, target `thread_id`, `title`, `prompt`, schedule expression, timezone, `next_run_at`, status, and timestamps. - - Use a stable status enum such as `active`, `paused`, and `deleted`. - - Keep display title separate from the schedule identity. - -2. Add scheduled action run tracking. - - Create a migration for `scheduled_action_runs`. - - Record each trigger attempt with `scheduled_action_id`, `user_id`, target scope, scheduled fire time, status, timestamps, error text, and the chat task/message identifiers produced by execution. - - Use run statuses such as `queued`, `running`, `completed`, and `failed`. - - Add enough metadata to inspect what happened after a scheduled trigger. - -3. Implement a scheduled action service. - - Add CRUD helpers for scheduled actions scoped by `user_id`. - - Add due-action querying with a small batch limit. - - Add a claim/lease mechanism so the same due action is not executed twice when multiple cron requests overlap. - - Compute the next run time after each successful claim or completion. - - Keep schedule parsing and timezone handling inside this service boundary. - -4. Expose scheduled action internal tools. - - Add tools such as `scheduled_action_create`, `scheduled_action_list`, `scheduled_action_get`, `scheduled_action_update`, `scheduled_action_pause`, `scheduled_action_resume`, and `scheduled_action_delete`. - - Register them with the existing internal tool mechanism used by the Agent loop. - - The tools write to `scheduled_actions`; the scheduler is not the only writer. - - Tool responses should be concise and structured so the Agent can confirm the action to the user. - -5. Add a scheduler tick endpoint. - - Add a protected backend route such as `GET /api/cron/scheduled-actions`. - - Configure Vercel Cron in the root Vercel configuration to call this path on a fixed cadence. - - The endpoint should query due actions, claim a bounded batch, create `scheduled_action_runs`, and enqueue or start Agent execution for each claimed action. - - The endpoint should return a small operational summary, not user-facing content. - -6. Start normal Agent execution from due actions. - - Convert each due scheduled action into a normal Agent input using the stored `prompt`, `user_id`, `channel_id`, and `thread_id`. - - Persist a scheduled-origin message or task marker so the conversation history shows why the Agent ran. - - Reuse the existing async chat/Agent pipeline rather than adding a separate scheduler-only executor. - - Mark the scheduled action run completed or failed based on the Agent execution result. - -7. Keep skill runtimes pluggable. - - Do not store Genkit-specific fields on `scheduled_actions`. - - If a scheduled prompt should invoke a scriptable pipeline, it should do so through the normal skill/tool selection path. - - Define Genkit-backed skills later as entries in the skill/runtime layer, not as scheduler primitives. - -8. Add UI/API visibility. - - Add REST endpoints for listing and inspecting scheduled actions if the UI needs a management surface. - - The first usable path can remain conversation-first, but the data model should support a future Scheduled Actions screen. - - Show scheduled action results in the target channel/thread as normal conversation output. - -9. Update documentation and code maps. - - Update `docs/backlog/2026-05-26-scheduled-agent-tasks.md` or supersede it with this plan. - - Update `docs/code_maps/feature_map.yaml` and `docs/code_maps/logic_map.yaml` because this change adds feature entry points, business logic, migrations, tools, and scheduled execution. - -## Acceptance Criteria - -- A user can ask Bricks to create a scheduled action through chat, and the Agent calls a scheduled action tool that persists the action. -- A user can list, update, pause, resume, and delete their scheduled actions through chat tools. -- A due scheduled action is claimed once, creates a `scheduled_action_runs` record, and starts normal Agent execution in the configured channel/thread. -- A scheduled action execution can call any normal Agent tool or skill; it is not tied to Genkit. -- Genkit-backed skills, if added later, are invoked through the skill/tool layer rather than through scheduler-specific code. -- Duplicate cron invocations do not execute the same due action twice. -- Scheduled action results are visible in the target conversation context. -- Failed scheduled action runs are recorded with an inspectable failure reason. -- The system remains compatible with Vercel serverless deployment and does not depend on an in-process long-lived scheduler. - -## Validation Commands - -- `cd apps/node_backend && npm test -- src/services/localAgentLoopService.test.ts src/routes/chat.test.ts` -- `cd apps/node_backend && npm test -- src/db/migrate.test.ts` -- `cd apps/node_backend && npm run type-check` -- `npx js-yaml docs/code_maps/feature_map.yaml >/dev/null && npx js-yaml docs/code_maps/logic_map.yaml >/dev/null` -- `git diff --check` diff --git a/docs/tasks/backlog/.gitkeep b/docs/tasks/backlog/.gitkeep new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/docs/tasks/backlog/.gitkeep @@ -0,0 +1 @@ + diff --git a/docs/tasks/backlog/2026-05-26-scheduled-agent-tasks.md b/docs/tasks/backlog/2026-05-26-scheduled-agent-tasks.md index 04353fa7..eb8a12d8 100644 --- a/docs/tasks/backlog/2026-05-26-scheduled-agent-tasks.md +++ b/docs/tasks/backlog/2026-05-26-scheduled-agent-tasks.md @@ -10,4 +10,4 @@ When a scheduled task is triggered, it should run an Agent workflow. - The task creation entry point is conversational. - The scheduled trigger is responsible for starting the Agent workflow. -- Implementation details are not decided yet. +- Superseded for implementation details: see `docs/tasks/backlog/2026-05-26-18-34-CST-scheduled-actions-agent-execution.md`, which is the current source of truth. diff --git a/docs/tasks/backlog/2026-06-17-child-homework-task-breakdown.md b/docs/tasks/backlog/2026-06-17-child-homework-task-breakdown.md new file mode 100644 index 00000000..abda2146 --- /dev/null +++ b/docs/tasks/backlog/2026-06-17-child-homework-task-breakdown.md @@ -0,0 +1,19 @@ +# Child Homework Task Breakdown + +## Confirmed Need + +Children may have homework across math, English, and other subjects, and the total workload can feel too large to start. + +Bricks should explore a tool or workflow that helps order, split, or reframe homework tasks so the next step feels easier and less overwhelming. + +## User Case + +A child has several homework items from different subjects and does not want to begin because everything feels like too much. + +The platform should help turn that pile of work into a clear, approachable sequence so the child has better emotion and motivation to start. + +## Notes + +- The exact solution is not decided yet. +- Possible directions include ordering tasks, splitting large tasks, redefining tasks into smaller goals, or choosing a first easy step. +- The experience should focus on reducing resistance and helping the child feel ready to begin. diff --git a/docs/tasks/backlog/2026-06-17-grammar-bug-collection-database.md b/docs/tasks/backlog/2026-06-17-grammar-bug-collection-database.md new file mode 100644 index 00000000..0b2c3373 --- /dev/null +++ b/docs/tasks/backlog/2026-06-17-grammar-bug-collection-database.md @@ -0,0 +1,13 @@ +# Grammar Bug Collection Database + +## Confirmed Need + +Bricks should be able to collect grammar bugs found in user input. + +The collected bugs should be saved to the database so they can be reviewed, reused, or analyzed later. + +## Notes + +- Reuse the existing database-backed tool/resource architecture where possible. +- The existing table/resource tool architecture is the expected starting point, rather than introducing a separate storage path. +- Exact capture trigger, review UI, schema, and retention behavior are not decided yet. diff --git a/docs/tasks/backlog/2026-06-17-note-purpose-specific-charts-examples.md b/docs/tasks/backlog/2026-06-17-note-purpose-specific-charts-examples.md new file mode 100644 index 00000000..e5c9277e --- /dev/null +++ b/docs/tasks/backlog/2026-06-17-note-purpose-specific-charts-examples.md @@ -0,0 +1,13 @@ +# Purpose-Specific Charts in Notes + +## Confirmed Need + +Notes should support charts or diagrams that are clearly separated by intended use. + +Each chart or diagram type should be able to include example problems so users can understand when and how to use it. + +## Notes + +- The source request mentioned note content, clearly divided chart purposes, and examples. +- Table sizing and layout control are related context, especially for chart-like tables embedded in notes. +- Exact chart types, editing UI, storage format, and rendering rules are not decided yet. diff --git a/docs/tasks/backlog/2026-06-22-selection-toolbar-translate-and-explain.md b/docs/tasks/backlog/2026-06-22-selection-toolbar-translate-and-explain.md new file mode 100644 index 00000000..cb43927c --- /dev/null +++ b/docs/tasks/backlog/2026-06-22-selection-toolbar-translate-and-explain.md @@ -0,0 +1,15 @@ +# Selection Toolbar Translate and Explain Actions + +## Confirmed Need + +The selection toolbar should support a translate action alongside the existing copy and highlight actions. + +After text is highlighted, the user should also see an explain action. Clicking explain should create a new thread and automatically send a prompt that asks the AI to explain the selected text using the surrounding context. + +## Notes + +- Current toolbar actions include copy and highlight. +- The translate action should be available from the selection toolbar. +- The explain action is tied to highlighted text, not just any empty selection state. +- The explain flow should preserve both the selected text and relevant context so the AI response is useful in the new thread. +- Exact toolbar placement, icon labels, prompt wording, context size, and thread naming behavior are not decided yet. diff --git a/docs/tasks/doing/.gitkeep b/docs/tasks/doing/.gitkeep new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/docs/tasks/doing/.gitkeep @@ -0,0 +1 @@ + diff --git a/docs/tasks/done/.gitkeep b/docs/tasks/done/.gitkeep new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/docs/tasks/done/.gitkeep @@ -0,0 +1 @@ + diff --git a/docs/tasks/done/2026-04-18-00-20-UTC-openclaw-message-routing-current-state.md b/docs/tasks/done/2026-04-18-00-20-UTC-openclaw-message-routing-current-state.md index a89460c2..1fd72377 100644 --- a/docs/tasks/done/2026-04-18-00-20-UTC-openclaw-message-routing-current-state.md +++ b/docs/tasks/done/2026-04-18-00-20-UTC-openclaw-message-routing-current-state.md @@ -52,4 +52,4 @@ ### Validation commands -- `git diff -- docs/plans docs/code_maps/logic_map.yaml` +- `git diff -- docs/tasks/done docs/code_maps/logic_map.yaml` diff --git a/docs/plans/2026-05-25-11-14-fix-openclaw-agents-500-error.md b/docs/tasks/done/2026-05-25-11-14-fix-openclaw-agents-500-error.md similarity index 100% rename from docs/plans/2026-05-25-11-14-fix-openclaw-agents-500-error.md rename to docs/tasks/done/2026-05-25-11-14-fix-openclaw-agents-500-error.md diff --git a/docs/plans/2026-05-26-02-30-resource-batch-add-rows.md b/docs/tasks/done/2026-05-26-02-30-resource-batch-add-rows.md similarity index 100% rename from docs/plans/2026-05-26-02-30-resource-batch-add-rows.md rename to docs/tasks/done/2026-05-26-02-30-resource-batch-add-rows.md diff --git a/docs/plans/2026-05-26-10-20-pr-266-review-follow-up.md b/docs/tasks/done/2026-05-26-10-20-pr-266-review-follow-up.md similarity index 100% rename from docs/plans/2026-05-26-10-20-pr-266-review-follow-up.md rename to docs/tasks/done/2026-05-26-10-20-pr-266-review-follow-up.md diff --git a/docs/plans/2026-06-16-15-32-+08-note-content-type-implementation.md b/docs/tasks/done/2026-06-16-15-32-+08-note-content-type-implementation.md similarity index 100% rename from docs/plans/2026-06-16-15-32-+08-note-content-type-implementation.md rename to docs/tasks/done/2026-06-16-15-32-+08-note-content-type-implementation.md diff --git a/docs/tasks/done/2026-06-22-17-40-CST-readme-feature-copy.md b/docs/tasks/done/2026-06-22-17-40-CST-readme-feature-copy.md new file mode 100644 index 00000000..bb4a0c9e --- /dev/null +++ b/docs/tasks/done/2026-06-22-17-40-CST-readme-feature-copy.md @@ -0,0 +1,31 @@ +# README Feature Copy + +## Background + +The README opening currently lists only a few capabilities and does not clearly separate shipped Bricks features from future roadmap ideas. The product positioning line "The agent console for tinkerers." should remain. + +## Goals + +- Keep the opening tagline. +- Add a `What Bricks does today` section for currently implemented product capabilities. +- Add a screenshots area that can receive images later. +- Add a roadmap section that is clearly future-facing. + +## Implementation Plan + +1. Rewrite the README opening bullets under a new `What Bricks does today` heading. +2. Add a `Screenshots` placeholder section without committing image assets. +3. Add a short `What's on the roadmap` section for known future directions. +4. Leave setup and documentation links unchanged. + +## Acceptance Criteria + +- README keeps "The agent console for tinkerers." +- README includes `## What Bricks does today`. +- README includes a screenshot placeholder section. +- README includes a roadmap section separate from shipped features. +- No source code behavior changes. + +## Validation Commands + +- `sed -n '1,140p' README.md` diff --git a/docs/tasks/done/2026-06-22-17-43-CST-readme-resource-copy.md b/docs/tasks/done/2026-06-22-17-43-CST-readme-resource-copy.md new file mode 100644 index 00000000..60cfad98 --- /dev/null +++ b/docs/tasks/done/2026-06-22-17-43-CST-readme-resource-copy.md @@ -0,0 +1,29 @@ +# README Resource Copy + +## Background + +Bricks resources are a differentiating product area because the app turns chat output into durable workspace objects instead of leaving everything inside a linear conversation. The README should explain each resource type with a short description and practical use cases. + +## Goals + +- Add a dedicated resource section to the README. +- Describe each current resource type in product-facing language. +- Include concrete use cases for each resource. +- Keep roadmap-only resource ideas separate from shipped resources. + +## Implementation Plan + +1. Add a `Resources` section after `What Bricks does today`. +2. List Todo Lists, Tables, Notes, and Highlights with descriptions and use cases. +3. Keep the existing screenshot, roadmap, documentation, docs site, and setup sections intact. + +## Acceptance Criteria + +- README explains why resources matter to Bricks. +- README lists each current resource type with use cases. +- README does not present roadmap-only ideas as shipped features. +- No source code behavior changes. + +## Validation Commands + +- `sed -n '1,180p' README.md` diff --git a/docs/tasks/done/2026-06-22-18-07-CST-readme-showcase-screenshots.md b/docs/tasks/done/2026-06-22-18-07-CST-readme-showcase-screenshots.md new file mode 100644 index 00000000..966d7fcf --- /dev/null +++ b/docs/tasks/done/2026-06-22-18-07-CST-readme-showcase-screenshots.md @@ -0,0 +1,27 @@ +# README Showcase Screenshots + +## Background + +The README had a placeholder screenshot section. Two product screenshots are available and should be stored in the repository with stable filenames so they render in GitHub Markdown. + +## Goals + +- Copy the screenshots into a repo-owned asset directory. +- Render both screenshots in the README showcase section. +- Use descriptive filenames and alt text. + +## Implementation Plan + +1. Copy the chat/channel screenshot to `docs/assets/showcase/chat-knowledge-organization.png`. +2. Copy the resources/highlights screenshot to `docs/assets/showcase/resources-highlights.png`. +3. Replace the README screenshot placeholder with a showcase section. + +## Acceptance Criteria + +- README renders both screenshots through relative repository paths. +- Screenshot filenames describe the product surface they show. +- No app behavior changes are made. + +## Validation Commands + +- `sed -n '1,90p' README.md` diff --git a/docs/tasks/trash/.gitkeep b/docs/tasks/trash/.gitkeep new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/docs/tasks/trash/.gitkeep @@ -0,0 +1 @@ +