From 50699bee2de29accda0f7a597b55df76a8642330 Mon Sep 17 00:00:00 2001 From: Haitao Pan Date: Sat, 28 Mar 2026 19:13:26 +0800 Subject: [PATCH] docs(workflow): add refactor trigger standard and real file size guard --- AGENTS.md | 94 ++++++++++++++++++++ test/quality/wave1_file_size_guard_test.dart | 58 ++++++------ 2 files changed, 121 insertions(+), 31 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index fe67c5eb..364eb7ea 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -11,6 +11,100 @@ - Do not repeatedly ask whether worktree mode or concurrent execution should be used for this repo; treat that as the default unless the user explicitly asks for a different flow. - Keep the branch/worktree lifecycle explicit: inspect, implement, verify, merge, clean up. +## Refactor Workflow Standard + +This section defines the reusable refactor workflow for this repo. +When trigger conditions are met, the workflow is executed by default without additional confirmation prompts. + +### Workflow Composition + +The standard combines: +- Orchestrator-style execution (main lane owns critical path, parallel lanes handle bounded side work) +- TDD refactor rhythm (`RED -> GREEN -> REFACTOR -> REGRESSION`) +- Flutter assistant/composer split guidance for oversized UI closures +- `xworkmate-lean-tdd-ddd-lite` as the primary architecture and verification constraint + +### Trigger Conditions (重构触发条件) + +Hard triggers (execute immediately): +- A single business file is larger than 800 lines, or close to 1000 lines. +- A business closure spreads across multiple `helpers`/utility files without clear ownership. +- A key flow regression fails and root cause points to structural coupling. +- The same business change requires repeated cross-layer edits (`lib/app`, `lib/runtime`, `lib/features`) in at least two consecutive change rounds. +- Security-sensitive changes cause controller/orchestrator responsibilities to mix with deep business logic. + +Soft triggers (recommended execution): +- PR review flags unclear ownership, duplicated logic, or low testability. +- A bug fix requires simultaneous edits across 3+ files for one business behavior. + +### Triggered Execution Flow (触发后执行流程) + +`Phase 0 - Closure Selection` +- Pick one smallest business closure as the implementation unit. +- Define explicit in-scope/out-of-scope boundaries before writing code. + +`Phase 1 - RED` +- Add or expand tests first for current behavior and regression points. +- Confirm failing expectation matches intended behavior change. + +`Phase 2 - GREEN` +- Apply the minimum code change that makes the new/updated tests pass. +- Avoid introducing extra abstraction layers unless complexity requires it. + +`Phase 3 - REFACTOR` +- Immediately refine names, dependency directions, and closure boundaries. +- Keep domain logic pure; keep orchestration in application/controller layers. + +`Phase 4 - REGRESSION` +- Run targeted suites and required quality checks. +- Run broader regression when the closure touches shared execution paths. + +`Phase 5 - SECURITY/ACCEPTANCE` +- If change scope touches auth/secrets/network/entitlements/release-sensitive settings, apply security baseline checks. +- Before any build/package/install/release readiness claim, run `xworkmate-acceptance`. + +Baseline commands: +- `flutter analyze` +- `flutter test test/runtime/app_controller_assistant_flow_test.dart` +- `flutter test test/runtime/code_agent_node_orchestrator_test.dart` +- `flutter test test/runtime/app_controller_thread_skills_test.dart` +- `flutter test test/quality/wave1_file_size_guard_test.dart` + +### Execution Roles + +- Main lane: + - Owns closure decisions, implementation, and merge readiness. + - Resolves any blockers that affect immediate next steps. +- Parallel lanes: + - Run bounded verification, diagnostics, documentation, and non-blocking side checks. + - Must not override main-lane code inspection and test evidence. + +### Required Deliverables (触发执行后的必交付物) + +Each triggered refactor must include: +- Test list: added/updated tests and covered behavior. +- Refactor record: closure boundary, key edits, risks, rollback point. +- Regression record: executed commands and pass/fail outcomes. +- Residual risk note: uncovered risk and next recommended action. + +### Then Tasks (当前仓库优先任务包) + +- `T1 (P0)` Align file-size guard targets with real implementation-bearing files (not only thin export entry files). +- `T2 (P0)` Split oversized assistant/composer closure while preserving behavior and key-flow tests. +- `T3 (P1)` Shrink oversized desktop/runtime controller closures with explicit ownership boundaries. +- `T4 (P1)` Eliminate unowned helper sprawl; keep helper code business-closure-owned. +- `T5 (P0/P1)` Run regression and security/acceptance gates and document closure-level outcomes. + +### Done Criteria + +A refactor task is complete only when: +- Closure ownership is clear and readable end-to-end. +- Domain logic remains pure and deterministic. +- Key `task-to-agent-to-result` flows are verified. +- Immediate refactor pass is completed after behavior verification. +- No new unowned helpers/utilities are introduced. +- Security/release checks are executed when trigger scope requires them. + ## Security Rules - `.env` is only a development/test prefill source for Settings -> Integrations -> Gateway. Do not hardcode `.env` values into source code. Do not auto-persist them into settings. Do not auto-connect from them. diff --git a/test/quality/wave1_file_size_guard_test.dart b/test/quality/wave1_file_size_guard_test.dart index b5fdcc74..ce7570fd 100644 --- a/test/quality/wave1_file_size_guard_test.dart +++ b/test/quality/wave1_file_size_guard_test.dart @@ -3,39 +3,35 @@ import 'dart:io'; import 'package:flutter_test/flutter_test.dart'; void main() { - test('wave1 oversized files should stay within 800 lines', () { - const maxLines = 800; - const targets = [ - // Wave 1 - 'lib/runtime/runtime_models.dart', - 'lib/runtime/runtime_controllers.dart', - 'lib/app/app_controller_desktop.dart', - 'lib/app/app_controller_web.dart', - 'lib/runtime/gateway_runtime.dart', - 'lib/runtime/multi_agent_orchestrator.dart', - 'test/features/assistant_page_suite.dart', - // Wave 2 - 'lib/features/settings/settings_page.dart', - 'lib/features/assistant/assistant_page.dart', - 'lib/features/assistant/assistant_page_components.dart', - 'lib/web/web_workspace_pages.dart', - 'lib/web/web_assistant_page.dart', - 'lib/web/web_settings_page.dart', - 'lib/features/mobile/mobile_shell.dart', - // Wave 3 - 'lib/runtime/direct_single_agent_app_server_client.dart', - 'test/runtime/app_controller_thread_skills_suite.dart', - 'go/go_core/main.go', - 'test/runtime/app_controller_ai_gateway_chat_suite.dart', - 'test/runtime/secure_config_store_suite.dart', - 'lib/app/ui_feature_manifest.dart', - 'test/runtime/app_controller_execution_target_switch_suite.dart', - 'lib/widgets/assistant_focus_panel.dart', - 'lib/web/web_focus_panel.dart', - ]; + test('wave1 implementation-bearing files stay under closure size caps', () { + // NOTE: + // - This guard now tracks implementation-bearing files instead of thin export + // entry files. + // - For oversized legacy closures, we use baseline caps to prevent further + // growth before T2/T3 shrink work lands. + const targets = { + // Enforced closure targets (300-800 expected by workflow). + 'lib/app/app_controller_desktop_core.dart': 800, + 'lib/runtime/multi_agent_orchestrator_core.dart': 800, + 'lib/runtime/multi_agent_orchestrator_workflow.dart': 800, + // Baseline cap for legacy oversized closure; tighten after T3. + 'lib/runtime/gateway_runtime_core.dart': 950, + 'lib/runtime/gateway_runtime_helpers.dart': 800, + // Baseline cap for legacy oversized closure; tighten after T3. + 'lib/runtime/runtime_controllers_settings.dart': 960, + 'lib/features/settings/settings_page_gateway.dart': 800, + 'test/runtime/app_controller_assistant_flow_suite.dart': 800, + 'test/runtime/app_controller_thread_skills_suite.dart': 800, + // Baseline caps for legacy oversized closures; tighten after T2/T3. + 'lib/features/assistant/assistant_page_main.dart': 2800, + 'lib/app/app_controller_desktop_runtime_helpers.dart': 950, + 'lib/app/app_controller_desktop_thread_sessions.dart': 1050, + }; final violations = []; - for (final path in targets) { + for (final entry in targets.entries) { + final path = entry.key; + final maxLines = entry.value; final file = File(path); expect(file.existsSync(), isTrue, reason: 'missing file: $path'); final lines = file.readAsLinesSync().length;