diff --git a/apps/decodex/src/cli.rs b/apps/decodex/src/cli.rs index 0e090739..b50deb94 100644 --- a/apps/decodex/src/cli.rs +++ b/apps/decodex/src/cli.rs @@ -1549,30 +1549,59 @@ mod tests { } #[test] - fn parses_commit_with_manual_authority() { - let cli = Cli::parse_from(["decodex", "commit", "ship hotfix", "--manual-authority"]); - - assert!(matches!( - cli.command, - Command::Commit(CommitCommand { authority: None, manual_authority: true, .. }) - )); - } - - #[test] - fn parses_land_with_manual_authority() { - let cli = Cli::parse_from([ - "decodex", - "land", - "ship hotfix", - "--manual-authority", - "--pr", - "https://github.com/hack-ink/decodex/pull/64", - ]); + fn parses_manual_authority_commands() { + enum ExpectedCommand { + Commit, + Land, + } - assert!(matches!( - cli.command, - Command::Land(LandCommand { authority: None, manual_authority: true, pr: Some(_), .. }) - )); + for (case_name, args, expected) in [ + ( + "commit manual authority", + &["decodex", "commit", "ship hotfix", "--manual-authority"][..], + ExpectedCommand::Commit, + ), + ( + "land manual authority", + &[ + "decodex", + "land", + "ship hotfix", + "--manual-authority", + "--pr", + "https://github.com/hack-ink/decodex/pull/64", + ][..], + ExpectedCommand::Land, + ), + ] { + let cli = Cli::parse_from(args.iter().copied()); + + match expected { + ExpectedCommand::Commit => assert!( + matches!( + cli.command, + Command::Commit(CommitCommand { + authority: None, + manual_authority: true, + .. + }) + ), + "unexpected parsed command for `{case_name}`" + ), + ExpectedCommand::Land => assert!( + matches!( + cli.command, + Command::Land(LandCommand { + authority: None, + manual_authority: true, + pr: Some(_), + .. + }) + ), + "unexpected parsed command for `{case_name}`" + ), + } + } } #[test] @@ -1601,33 +1630,37 @@ mod tests { } #[test] - fn parses_run_with_positional_issue_and_dry_run() { - let cli = Cli::parse_from(["decodex", "run", "issue-1", "--dry-run"]); - - assert!(matches!( - cli.command, - Command::Run(RunCommand { issue: Some(_), dry_run: true, explain: false, .. }) - )); - } - - #[test] - fn parses_run_without_issue() { - let cli = Cli::parse_from(["decodex", "run"]); - - assert!(matches!( - cli.command, - Command::Run(RunCommand { issue: None, dry_run: false, explain: false, .. }) - )); - } - - #[test] - fn parses_run_dry_run_explain() { - let cli = Cli::parse_from(["decodex", "run", "--dry-run", "--explain"]); - - assert!(matches!( - cli.command, - Command::Run(RunCommand { issue: None, dry_run: true, explain: true, .. }) - )); + fn parses_run_modes() { + for (case_name, args, expected_issue, expected_dry_run, expected_explain) in [ + ( + "positional issue dry run", + &["decodex", "run", "issue-1", "--dry-run"][..], + Some("issue-1"), + true, + false, + ), + ("default run", &["decodex", "run"][..], None, false, false), + ( + "explain dry run", + &["decodex", "run", "--dry-run", "--explain"][..], + None, + true, + true, + ), + ] { + let cli = Cli::parse_from(args.iter().copied()); + + assert!( + matches!( + cli.command, + Command::Run(RunCommand { issue, dry_run, explain, .. }) + if issue.as_deref() == expected_issue + && dry_run == expected_dry_run + && explain == expected_explain + ), + "unexpected parsed run command for `{case_name}`" + ); + } let error = Cli::try_parse_from(["decodex", "run", "--explain"]) .expect_err("explain should require dry-run"); @@ -1642,48 +1675,41 @@ mod tests { } #[test] - fn parses_serve_default_listen_address() { - let cli = Cli::parse_from(["decodex", "serve"]); - - assert!(matches!( - cli.command, - Command::Serve(ServeCommand { - project_config: ProjectConfigArgs { config: None }, - listen_address, - dev: false, - }) if listen_address == "127.0.0.1:8192" - )); - } - - #[test] - fn parses_serve_with_listen_address_and_project_config() { - let cli = Cli::parse_from([ - "decodex", - "serve", - "--config", - "./project.toml", - "--listen-address", - "127.0.0.1:9000", - ]); - - assert!(matches!( - cli.command, - Command::Serve(ServeCommand { - project_config: ProjectConfigArgs { config: Some(config) }, - listen_address, - dev, - }) - if listen_address == "127.0.0.1:9000" - && !dev - && config == Path::new("./project.toml") - )); - } - - #[test] - fn parses_serve_dev() { - let cli = Cli::parse_from(["decodex", "serve", "--dev"]); - - assert!(matches!(cli.command, Command::Serve(ServeCommand { dev: true, .. }))); + fn parses_serve_modes() { + for (case_name, args, expected_listen_address, expected_config, expected_dev) in [ + ("default listen address", &["decodex", "serve"][..], "127.0.0.1:8192", None, false), + ( + "custom listen address and project config", + &[ + "decodex", + "serve", + "--config", + "./project.toml", + "--listen-address", + "127.0.0.1:9000", + ][..], + "127.0.0.1:9000", + Some("./project.toml"), + false, + ), + ("dev mode", &["decodex", "serve", "--dev"][..], "127.0.0.1:8192", None, true), + ] { + let cli = Cli::parse_from(args.iter().copied()); + + assert!( + matches!( + cli.command, + Command::Serve(ServeCommand { + project_config: ProjectConfigArgs { config }, + listen_address, + dev, + }) if listen_address == expected_listen_address + && config.as_deref() == expected_config.map(Path::new) + && dev == expected_dev + ), + "unexpected parsed serve command for `{case_name}`" + ); + } } #[test] @@ -1957,33 +1983,56 @@ mod tests { } #[test] - fn parses_project_add() { - let cli = Cli::parse_from(["decodex", "project", "add", "./project.toml"]); - - assert!(matches!( - cli.command, - Command::Project(ProjectCommand { command: ProjectSubcommand::Add(_) }) - )); - } - - #[test] - fn parses_project_enable() { - let cli = Cli::parse_from(["decodex", "project", "enable", "pubfi"]); - - assert!(matches!( - cli.command, - Command::Project(ProjectCommand { command: ProjectSubcommand::Enable(_) }) - )); - } - - #[test] - fn parses_project_remove() { - let cli = Cli::parse_from(["decodex", "project", "remove", "vibe-mono"]); + fn parses_project_subcommands() { + enum ExpectedProjectSubcommand { + Add, + Enable, + Remove, + } - assert!(matches!( - cli.command, - Command::Project(ProjectCommand { command: ProjectSubcommand::Remove(_) }) - )); + for (case_name, args, expected) in [ + ( + "add", + &["decodex", "project", "add", "./project.toml"][..], + ExpectedProjectSubcommand::Add, + ), + ( + "enable", + &["decodex", "project", "enable", "pubfi"][..], + ExpectedProjectSubcommand::Enable, + ), + ( + "remove", + &["decodex", "project", "remove", "vibe-mono"][..], + ExpectedProjectSubcommand::Remove, + ), + ] { + let cli = Cli::parse_from(args.iter().copied()); + + match expected { + ExpectedProjectSubcommand::Add => assert!( + matches!( + cli.command, + Command::Project(ProjectCommand { command: ProjectSubcommand::Add(_) }) + ), + "unexpected parsed project subcommand for `{case_name}`" + ), + ExpectedProjectSubcommand::Enable => assert!( + matches!( + cli.command, + Command::Project(ProjectCommand { command: ProjectSubcommand::Enable(_) }) + ), + "unexpected parsed project subcommand for `{case_name}`" + ), + ExpectedProjectSubcommand::Remove => assert!( + matches!( + cli.command, + Command::Project(ProjectCommand { command: ProjectSubcommand::Remove(_) }) + ), + "unexpected parsed project subcommand for `{case_name}`" + ), + } + } } #[test] diff --git a/apps/decodex/src/config.rs b/apps/decodex/src/config.rs index 8f2341db..c08135bd 100644 --- a/apps/decodex/src/config.rs +++ b/apps/decodex/src/config.rs @@ -1318,35 +1318,16 @@ mod tests { } #[test] - fn rejects_project_scoped_codex_fixed_account() { - let temp_dir = TempDir::new().expect("temp dir should exist"); - let config_path = write_config_file( - temp_dir.path(), - r#" - service_id = "pubfi" - - [tracker] - api_key_env_var = "HOME" - - [github] - token_env_var = "HOME" - - [codex.accounts] - fixed_account = "primary@example.com" - "#, - ); - let error = ServiceConfig::from_path(&config_path) - .expect_err("project-scoped account selection should fail"); - - assert!(error.to_string().contains("fixed_account")); - } - - #[test] - fn rejects_legacy_codex_accounts_path_override() { - let temp_dir = TempDir::new().expect("temp dir should exist"); - let config_path = write_config_file( - temp_dir.path(), - r#" + fn rejects_removed_project_scoped_codex_account_fields() { + for (case_name, removed_field) in [ + ("project-scoped account selection", r#"fixed_account = "primary@example.com""#), + ("legacy account path override", r#"path = "accounts/codex-auth.jsonl""#), + ] { + let temp_dir = TempDir::new().expect("temp dir should exist"); + let config_path = write_config_file( + temp_dir.path(), + &format!( + r#" service_id = "pubfi" [tracker] @@ -1356,13 +1337,22 @@ mod tests { token_env_var = "HOME" [codex.accounts] - path = "accounts/codex-auth.jsonl" - "#, - ); - let error = ServiceConfig::from_path(&config_path) - .expect_err("legacy account path override should fail"); + {removed_field} + "# + ), + ); + let error = ServiceConfig::from_path(&config_path).expect_err(case_name); - assert!(error.to_string().contains("path")); + assert!( + error.to_string().contains( + removed_field + .split_once(" = ") + .expect("removed field assignment should include a separator") + .0 + ), + "unexpected error for `{case_name}`: {error:?}" + ); + } } #[test] diff --git a/apps/decodex/src/workflow.rs b/apps/decodex/src/workflow.rs index bd10deaf..570c4175 100644 --- a/apps/decodex/src/workflow.rs +++ b/apps/decodex/src/workflow.rs @@ -1763,65 +1763,29 @@ Then validate the lane. } #[test] - fn rejects_string_global_concurrency_limit() { - let result = parse_valid_workflow_with(|markdown| { - *markdown = markdown - .replace("max_concurrent_agents = 1", "max_concurrent_agents = \"unlimited\""); - }); - let error = result.expect_err("string global concurrency should be invalid"); - - assert!( - error.to_string().contains("must be an integer greater than or equal to zero"), - "unexpected error: {error:?}" - ); - } - - #[test] - fn rejects_negative_global_concurrency_limit() { - let result = WorkflowDocument::parse_markdown( - r#" -+++ -version = 1 - -[tracker] -provider = "linear" -startable_states = ["Todo"] -terminal_states = ["Done", "Canceled", "Duplicate"] -in_progress_state = "In Progress" -success_state = "In Review" -completed_state = "Done" -failure_state = "Todo" -opt_out_label = "decodex:manual-only" -needs_attention_label = "decodex:needs-attention" - -[agent] -transport = "stdio://" - -[execution] -max_attempts = 3 -max_turns = 1 -max_retry_backoff_ms = 300000 -max_concurrent_agents = -1 -gate_profiles = {} -canonicalize_commands = [] -verify_commands = [] - -[execution.workspace_hooks] -after_create_commands = [] -before_remove_commands = [] -timeout_seconds = 60 - -[context] -read_first = [] -+++ - "#, - ); - let error = result.expect_err("negative global concurrency should be invalid"); + fn rejects_invalid_global_concurrency_limits() { + for (case_name, replacement, expected) in [ + ( + "string global concurrency", + "max_concurrent_agents = \"unlimited\"", + "must be an integer greater than or equal to zero", + ), + ( + "negative global concurrency", + "max_concurrent_agents = -1", + "must be greater than or equal to zero", + ), + ] { + let result = parse_valid_workflow_with(|markdown| { + *markdown = markdown.replace("max_concurrent_agents = 1", replacement); + }); + let error = result.expect_err(case_name); - assert!( - error.to_string().contains("must be greater than or equal to zero"), - "unexpected error: {error:?}" - ); + assert!( + error.to_string().contains(expected), + "unexpected error for `{case_name}`: {error:?}" + ); + } } fn parse_valid_workflow_with(rewrite: impl FnOnce(&mut String)) -> Result { diff --git a/docs/reference/test-suite.md b/docs/reference/test-suite.md index 108b99f3..f1f5a765 100644 --- a/docs/reference/test-suite.md +++ b/docs/reference/test-suite.md @@ -14,13 +14,22 @@ standards for keeping, merging, or deleting tests. ## Current Snapshot -This snapshot lists 805 `nextest` tests. The repo gate run for this inventory reported -805 passed tests and 1 skipped test. Regenerate the runnable inventory with: +This snapshot lists 971 default-runnable `nextest` tests. One additional ignored +live app-server test is listed only with verbose or JSON inventory output. The repo +gate run for this inventory reported 971 passed tests and 1 skipped test. Regenerate +the runnable inventory with: ```sh cargo nextest list --workspace --all-targets --all-features ``` +Regenerate the ignored/live-test inventory with: + +```sh +cargo nextest list --workspace --all-targets --all-features --verbose \ + | rg "\\(skipped\\)|ignored" +``` + Regenerate the top-level grouping with: ```sh @@ -36,13 +45,14 @@ cargo nextest list --workspace --all-targets --all-features 2>/dev/null \ | Group | Count | Primary surfaces | Owns | | --- | ---: | --- | --- | -| Orchestrator | 405 | `apps/decodex/src/orchestrator/tests.rs`, `apps/decodex/src/orchestrator/tests/**/*.rs` | Intake, retry, review/landing, runtime cleanup, operator status, repo gates | -| Tracker tool bridge | 85 | `apps/decodex/src/agent/tracker_tool_bridge/tests.rs`, `apps/decodex/src/agent/tracker_tool_bridge/tests/**/*.rs` | Dynamic tracker tools, continuation guards, review handoff writes, closeout writes | -| App-server protocol/runtime | 59 | `apps/decodex/src/agent/app_server/tests.rs`, `apps/decodex/src/agent/json_rpc.rs`, app-server protocol tests | JSON-RPC parsing, turn execution, dynamic tools, thread config, transport failures | -| Runtime state, locks, and maintenance | 45 | `state::tests`, `runtime::tests`, `maintenance::tests` | Persistent local state, lock ownership, runtime database contracts, local retention | -| Workflow and config parsing | 44 | `workflow::tests`, `config::tests`, `codex_config::tests` | `WORKFLOW.md`, project config, Codex config edits, removed-field rejection, default policy | -| Git, worktree, landing, and recovery helpers | 108 | `worktree::tests`, `manual::tests`, `commit_message::tests`, `github::tests`, `default_branch_sync::tests`, `pull_request::tests`, `recovery::tests`, `git_credentials::tests` | Git/worktree behavior, manual landing, GitHub/PR helpers, recovery commands, commit-message policy | -| Account, CLI, archive, and tracker integration | 59 | `accounts::tests`, `agent::codex_accounts::tests`, `agent::decodex_tool_bridge::tests`, `app_bridge::tests`, `cli::tests`, `archive_hygiene::tests`, `tracker::*::tests` | User-facing commands, account pools, app bridge parsing, archive hygiene, direct tracker adapter and public-text behavior | +| Orchestrator | 460 | `apps/decodex/src/orchestrator/tests.rs`, `apps/decodex/src/orchestrator/tests/**/*.rs` | Intake, retry, review/landing, runtime cleanup, operator status, repo gates | +| Tracker tool bridge | 91 | `apps/decodex/src/agent/tracker_tool_bridge/tests.rs`, `apps/decodex/src/agent/tracker_tool_bridge/tests/**/*.rs` | Dynamic tracker tools, continuation guards, review handoff writes, closeout writes | +| App-server protocol/runtime | 80 | `apps/decodex/src/agent/app_server/tests.rs`, `apps/decodex/src/agent/json_rpc.rs`, app-server protocol tests | JSON-RPC parsing, turn execution, dynamic tools, thread config, transport failures | +| Runtime state, locks, and maintenance | 60 | `state::tests`, `runtime::tests`, `maintenance::tests` | Persistent local state, lock ownership, runtime database contracts, local retention | +| Workflow and config parsing | 45 | `workflow::tests`, `config::tests`, `codex_config::tests` | `WORKFLOW.md`, project config, Codex config edits, removed-field rejection, default policy | +| Git, worktree, landing, and recovery helpers | 120 | `worktree::tests`, `manual::tests`, `commit_message::tests`, `github::tests`, `default_branch_sync::tests`, `pull_request::tests`, `recovery::tests`, `git_credentials::tests` | Git/worktree behavior, manual landing, GitHub/PR helpers, recovery commands, commit-message policy | +| Radar content validation | 33 | `radar::tests` | Upstream Radar schemas, bundles, signal rendering, social publication ledgers | +| Account, CLI, archive, and tracker integration | 82 | `accounts::tests`, `agent::codex_accounts::tests`, `agent::decodex_tool_bridge::tests`, `app_bridge::tests`, `cli::tests`, `archive_hygiene::tests`, `tracker::*::tests` | User-facing commands, account pools, app bridge parsing, archive hygiene, direct tracker adapter and public-text behavior | ## Orchestrator Inventory @@ -59,30 +69,31 @@ large catch-all test file unless the behavior crosses several of these stages. | `apps/decodex/src/orchestrator/tests/retry/scheduling.rs` | 24 | Retry timing, dry-run behavior, retry marker semantics | | `apps/decodex/src/orchestrator/tests/retry/selection.rs` | 14 | Retry queue selection and blocked retry candidates | | `apps/decodex/src/orchestrator/tests/runtime/repo_gate.rs` | 8 | Repo gate command selection, cleanliness, shell fallback, and failure classification | -| `apps/decodex/src/orchestrator/tests/runtime/failure.rs` | 30 | Failure comments, runtime credentials, cleanup, lease release | -| `apps/decodex/src/orchestrator/tests/recovery/reconciliation.rs` | 17 | Stale lease, recovery worktree, and reconciliation behavior | +| `apps/decodex/src/orchestrator/tests/runtime/failure.rs` | 33 | Failure comments, runtime credentials, cleanup, lease release | +| `apps/decodex/src/orchestrator/tests/runtime/thread_archive.rs` | 1 | Completed-thread archive candidate filtering | +| `apps/decodex/src/orchestrator/tests/recovery/reconciliation.rs` | 22 | Stale lease, recovery worktree, and reconciliation behavior | | `apps/decodex/src/orchestrator/tests/recovery/terminal_support.rs` | 0 | Shared retained recovery and closeout fixtures | | `apps/decodex/src/orchestrator/tests/recovery/closeout/dispatch.rs` | 5 | Direct closeout dispatch and PR validation | | `apps/decodex/src/orchestrator/tests/recovery/closeout/identity.rs` | 4 | Closeout identity reuse after retained runs | | `apps/decodex/src/orchestrator/tests/recovery/closeout/cleanup.rs` | 6 | Retained closeout cleanup and cleanup blockers | -| `apps/decodex/src/orchestrator/tests/recovery/terminal_failures.rs` | 11 | Terminal failure labeling, nonretryable attention, and local/remote duplicate writeback idempotency | -| `apps/decodex/src/orchestrator/tests/recovery/runtime_reentry.rs` | 27 | Runtime reentry, recovered worktrees, liveness, and live-run recovery | +| `apps/decodex/src/orchestrator/tests/recovery/terminal_failures.rs` | 14 | Terminal failure labeling, nonretryable attention, and local/remote duplicate writeback idempotency | +| `apps/decodex/src/orchestrator/tests/recovery/runtime_reentry.rs` | 30 | Runtime reentry, recovered worktrees, liveness, and live-run recovery | | `apps/decodex/src/orchestrator/tests/operator/status_support.rs` | 0 | Shared operator status fixtures | -| `apps/decodex/src/orchestrator/tests/operator/status/control_plane.rs` | 5 | Registered project control-plane rows | -| `apps/decodex/src/orchestrator/tests/operator/status/running_lanes.rs` | 29 | Running lanes, stalled lanes, active-run hydration, and local worktrees | -| `apps/decodex/src/orchestrator/tests/operator/status/history.rs` | 6 | Run ledger and Linear history hydration | -| `apps/decodex/src/orchestrator/tests/operator/status/text.rs` | 8 | Human-readable operator status text | -| `apps/decodex/src/orchestrator/tests/operator/status/publishing.rs` | 7 | Snapshot publishing, degraded observers, and tracker backoff | -| `apps/decodex/src/orchestrator/tests/operator/status/queue.rs` | 10 | Intake queue classifications and shared-claim visibility | -| `apps/decodex/src/orchestrator/tests/operator/status/http.rs` | 21 | Operator dashboard HTTP pages/assets, `/livez`, WebSocket control, and removed snapshot-route responses | +| `apps/decodex/src/orchestrator/tests/operator/status/control_plane.rs` | 10 | Registered project control-plane rows | +| `apps/decodex/src/orchestrator/tests/operator/status/running_lanes.rs` | 34 | Running lanes, stalled lanes, active-run hydration, and local worktrees | +| `apps/decodex/src/orchestrator/tests/operator/status/history.rs` | 7 | Run ledger and Linear history hydration | +| `apps/decodex/src/orchestrator/tests/operator/status/text.rs` | 9 | Human-readable operator status text | +| `apps/decodex/src/orchestrator/tests/operator/status/publishing.rs` | 11 | Snapshot publishing, degraded observers, and tracker backoff | +| `apps/decodex/src/orchestrator/tests/operator/status/queue.rs` | 17 | Intake queue classifications and shared-claim visibility | +| `apps/decodex/src/orchestrator/tests/operator/status/http.rs` | 31 | Operator dashboard HTTP pages/assets, `/livez`, WebSocket control, and removed snapshot-route responses | | `apps/decodex/src/orchestrator/tests/operator/status/dashboard.rs` | 34 | Dashboard client rendering contracts | | `apps/decodex/src/orchestrator/tests/operator/status/agent_evidence.rs` | 4 | Agent evidence snapshots and private evidence readback | | `apps/decodex/src/orchestrator/tests/review_landing/status_support.rs` | 0 | Shared Review & Landing status fixtures | -| `apps/decodex/src/orchestrator/tests/review_landing/status_rows.rs` | 17 | Review & Landing status rows and handoff lineage | -| `apps/decodex/src/orchestrator/tests/review_landing/orchestration.rs` | 16 | Review orchestration, admin merge, and repair routing | +| `apps/decodex/src/orchestrator/tests/review_landing/status_rows.rs` | 18 | Review & Landing status rows and handoff lineage | +| `apps/decodex/src/orchestrator/tests/review_landing/orchestration.rs` | 17 | Review orchestration, admin merge, and repair routing | | `apps/decodex/src/orchestrator/tests/review_landing/status_markers.rs` | 1 | Review orchestration marker handling and recovered targeted visibility | | `apps/decodex/src/orchestrator/tests/review_landing/classification_review.rs` | 12 | Review repair, request-pending, stale handoff, merged PR classification | -| `apps/decodex/src/orchestrator/tests/review_landing/classification_checks.rs` | 14 | Required checks, GitHub token gates, GraphQL pagination/query shape | +| `apps/decodex/src/orchestrator/tests/review_landing/classification_checks.rs` | 16 | Required checks, GitHub token gates, GraphQL pagination/query shape | | `apps/decodex/src/orchestrator/tests/review_landing/review_state.rs` | 2 | Pull-request review-state conversion from GitHub GraphQL nodes | ## Tracker Bridge Inventory @@ -91,9 +102,9 @@ large catch-all test file unless the behavior crosses several of these stages. | --- | ---: | --- | | `apps/decodex/src/agent/tracker_tool_bridge/tests/mutation/dispatch.rs` | 24 | Tool argument validation, state transitions, label mutations, closeout dispatch | | `apps/decodex/src/agent/tracker_tool_bridge/tests/mutation/continuation.rs` | 13 | Continuation-blocking writes and reactivation safety | -| `apps/decodex/src/agent/tracker_tool_bridge/tests/mutation/progress.rs` | 7 | Progress checkpoint comments and worktree path handling | -| `apps/decodex/src/agent/tracker_tool_bridge/tests/review/policy.rs` | 22 | Internal-review stop policy, repair/writeback behavior, checkpoint handling | -| `apps/decodex/src/agent/tracker_tool_bridge/tests/review/handoff.rs` | 19 | Review handoff, repair complete, terminal finalize, closeout complete | +| `apps/decodex/src/agent/tracker_tool_bridge/tests/mutation/progress.rs` | 10 | Progress checkpoint comments and worktree path handling | +| `apps/decodex/src/agent/tracker_tool_bridge/tests/review/policy.rs` | 23 | Internal-review stop policy, repair/writeback behavior, checkpoint handling | +| `apps/decodex/src/agent/tracker_tool_bridge/tests/review/handoff.rs` | 21 | Review handoff, repair complete, terminal finalize, closeout complete | ## Keep Standards @@ -123,6 +134,25 @@ observable contract: The merged test name should describe the behavior contract, not the fixture shape. +## Current Cleanup Targets + +The first low-risk cleanup already collapsed equivalent CLI parser sibling tests plus +same-field config/workflow negative cases into table-driven coverage. Continue pruning +in this order: + +- Remaining `cli::tests`, `config::tests`, and `workflow::tests` when cases only vary + argument spelling, missing-field name, or invalid value text. +- `operator/status/dashboard.rs` when assertions inspect raw CSS or JavaScript text + rather than a stable operator-facing contract. +- Large orchestrator lifecycle files only after proving the candidate cases share the + same entrypoint, branch, state setup, persisted marker semantics, and externally + visible assertion. + +Keep the ignored +`agent::app_server::tests::live_app_server_resume_round_trip_updates_marker_and_state` +test out of the default gate unless the local app-server binary becomes deterministic +enough for normal CI. + ## Delete Standards Delete a test only when another remaining test is a strict behavioral superset: