diff --git a/.pql/changelog/ticket_history/2026-05.sql b/.pql/changelog/ticket_history/2026-05.sql index 8daa3e45..97dd9237 100644 --- a/.pql/changelog/ticket_history/2026-05.sql +++ b/.pql/changelog/ticket_history/2026-05.sql @@ -2308,3 +2308,13 @@ INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-192', 'description', 'The pre-push gate (make push-check) and the inner-loop make test are slow; cost is dominated by ''flutter test --coverage --exclude-tags pty'' running the whole flutter suite WITH coverage instrumentation (ci/test.sh). Two concrete levers found by reading the pipeline: (1) FREE WIN — a11y tests run TWICE in push-check: the coverage run in ci/test.sh has no path filter so it already includes test/a11y/, then ci/test_a11y.sh (''flutter test test/a11y/'') runs them again in a separate flutter invocation, chained AFTER test (so not even fast-fail). Drop test-a11y from the push-check chain (keep ''make test-a11y'' as a standalone dev target) to remove a whole redundant flutter compile+boot+run; same tests still execute under test. (2) Coverage is on the dev inner loop — make test always runs --coverage, which is only needed where coverage-gate reads lcov (push-check/CI). Split a fast no-coverage ''make test'' for the edit loop from a coverage run used by push-check/CI; the script even claims <60s warm but coverage blows past that. MEASURE FIRST (cheap): time each ci/*.sh step, check ''flutter test --concurrency'' vs nproc, and look at per-file timing for golden/alchemist tests before further changes. Acceptance: dev ''make test'' runs without coverage and is meaningfully faster; push-check no longer double-runs the a11y suite; coverage is still gated (coverage-gate unchanged, floor 95); all gates stay green; the fast-vs-coverage split is documented in the Makefile/CONTRIBUTING. Domain: tooling/CI. Raised 2026-05-31 while the push gate kept interrupting flow.', 'The pre-push gate (make push-check) and the inner-loop make test are slow; cost is dominated by ''flutter test --coverage --exclude-tags pty'' running the whole flutter suite WITH coverage instrumentation (ci/test.sh). Two concrete levers found by reading the pipeline: (1) FREE WIN — a11y tests run TWICE in push-check: the coverage run in ci/test.sh has no path filter so it already includes test/a11y/, then ci/test_a11y.sh (''flutter test test/a11y/'') runs them again in a separate flutter invocation, chained AFTER test (so not even fast-fail). Drop test-a11y from the push-check chain (keep ''make test-a11y'' as a standalone dev target) to remove a whole redundant flutter compile+boot+run; same tests still execute under test. (2) Coverage is on the dev inner loop — make test always runs --coverage, which is only needed where coverage-gate reads lcov (push-check/CI). Split a fast no-coverage ''make test'' for the edit loop from a coverage run used by push-check/CI; the script even claims <60s warm but coverage blows past that. MEASURE FIRST (cheap): time each ci/*.sh step, check ''flutter test --concurrency'' vs nproc, and look at per-file timing for golden/alchemist tests before further changes. Acceptance: dev ''make test'' runs without coverage and is meaningfully faster; push-check no longer double-runs the a11y suite; coverage is still gated (coverage-gate unchanged, floor 95); all gates stay green; the fast-vs-coverage split is documented in the Makefile/CONTRIBUTING. Domain: tooling/CI. Raised 2026-05-31 while the push gate kept interrupting flow. Add a per-test timeout to the gate''s flutter test (e.g. ci/test.sh: ''flutter test --coverage --timeout 60s''). Motivation discovered 2026-05-31: a hung pumpAndSettle has a 10-MINUTE default timeout, so a few hanging widget tests wedge make push-check for 30+ minutes — which is almost certainly why a MacBook session resorted to ''git push --no-verify'' (the gate never returned; it was not disabled, it was hung). A tight per-test timeout makes any future hang fail fast (60s) instead of wedging a pre-push, so the gate stays usable and nobody is tempted to bypass it. Pair with: keep writing pump()-bounded tests instead of pumpAndSettle() where a view has overlapping async loads (see the T-188 decision_reader_test hang).', NULL, '2026-05-31 12:31:58', '2026-05-31 12:31:58', '2026-05-31 12:31:58', NULL, 'e42f8392c580eee1269d266cb0fb9749', 1) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-187', 'status', 'in_progress', 'done', NULL, '2026-05-31 14:17:22', '2026-05-31 14:17:22', '2026-05-31 14:17:22', NULL, '4bef7314b0b25fbed23934e915a2ce59', 1) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-162', 'status', 'in_progress', 'done', NULL, '2026-05-31 14:17:22', '2026-05-31 14:17:22', '2026-05-31 14:17:22', NULL, '677a9a319e57dffeb7158b17a9a61392', 1) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-188', 'status', 'in_progress', 'done', NULL, '2026-05-31 14:17:22', '2026-05-31 14:17:22', '2026-05-31 14:17:22', NULL, '9e04aeba4367316d03afb0855a306520', 1) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-192', 'description', 'The pre-push gate (make push-check) and the inner-loop make test are slow; cost is dominated by ''flutter test --coverage --exclude-tags pty'' running the whole flutter suite WITH coverage instrumentation (ci/test.sh). Two concrete levers found by reading the pipeline: (1) FREE WIN — a11y tests run TWICE in push-check: the coverage run in ci/test.sh has no path filter so it already includes test/a11y/, then ci/test_a11y.sh (''flutter test test/a11y/'') runs them again in a separate flutter invocation, chained AFTER test (so not even fast-fail). Drop test-a11y from the push-check chain (keep ''make test-a11y'' as a standalone dev target) to remove a whole redundant flutter compile+boot+run; same tests still execute under test. (2) Coverage is on the dev inner loop — make test always runs --coverage, which is only needed where coverage-gate reads lcov (push-check/CI). Split a fast no-coverage ''make test'' for the edit loop from a coverage run used by push-check/CI; the script even claims <60s warm but coverage blows past that. MEASURE FIRST (cheap): time each ci/*.sh step, check ''flutter test --concurrency'' vs nproc, and look at per-file timing for golden/alchemist tests before further changes. Acceptance: dev ''make test'' runs without coverage and is meaningfully faster; push-check no longer double-runs the a11y suite; coverage is still gated (coverage-gate unchanged, floor 95); all gates stay green; the fast-vs-coverage split is documented in the Makefile/CONTRIBUTING. Domain: tooling/CI. Raised 2026-05-31 while the push gate kept interrupting flow. + +Add a per-test timeout to the gate''s flutter test (e.g. ci/test.sh: ''flutter test --coverage --timeout 60s''). Motivation discovered 2026-05-31: a hung pumpAndSettle has a 10-MINUTE default timeout, so a few hanging widget tests wedge make push-check for 30+ minutes — which is almost certainly why a MacBook session resorted to ''git push --no-verify'' (the gate never returned; it was not disabled, it was hung). A tight per-test timeout makes any future hang fail fast (60s) instead of wedging a pre-push, so the gate stays usable and nobody is tempted to bypass it. Pair with: keep writing pump()-bounded tests instead of pumpAndSettle() where a view has overlapping async loads (see the T-188 decision_reader_test hang).', 'The pre-push gate (make push-check) and the inner-loop make test are slow; cost is dominated by ''flutter test --coverage --exclude-tags pty'' running the whole flutter suite WITH coverage instrumentation (ci/test.sh). Two concrete levers found by reading the pipeline: (1) FREE WIN — a11y tests run TWICE in push-check: the coverage run in ci/test.sh has no path filter so it already includes test/a11y/, then ci/test_a11y.sh (''flutter test test/a11y/'') runs them again in a separate flutter invocation, chained AFTER test (so not even fast-fail). Drop test-a11y from the push-check chain (keep ''make test-a11y'' as a standalone dev target) to remove a whole redundant flutter compile+boot+run; same tests still execute under test. (2) Coverage is on the dev inner loop — make test always runs --coverage, which is only needed where coverage-gate reads lcov (push-check/CI). Split a fast no-coverage ''make test'' for the edit loop from a coverage run used by push-check/CI; the script even claims <60s warm but coverage blows past that. MEASURE FIRST (cheap): time each ci/*.sh step, check ''flutter test --concurrency'' vs nproc, and look at per-file timing for golden/alchemist tests before further changes. Acceptance: dev ''make test'' runs without coverage and is meaningfully faster; push-check no longer double-runs the a11y suite; coverage is still gated (coverage-gate unchanged, floor 95); all gates stay green; the fast-vs-coverage split is documented in the Makefile/CONTRIBUTING. Domain: tooling/CI. Raised 2026-05-31 while the push gate kept interrupting flow. + +Add a per-test timeout to the gate''s flutter test (e.g. ci/test.sh: ''flutter test --coverage --timeout 60s''). Motivation discovered 2026-05-31: a hung pumpAndSettle has a 10-MINUTE default timeout, so a few hanging widget tests wedge make push-check for 30+ minutes — which is almost certainly why a MacBook session resorted to ''git push --no-verify'' (the gate never returned; it was not disabled, it was hung). A tight per-test timeout makes any future hang fail fast (60s) instead of wedging a pre-push, so the gate stays usable and nobody is tempted to bypass it. Pair with: keep writing pump()-bounded tests instead of pumpAndSettle() where a view has overlapping async loads (see the T-188 decision_reader_test hang). + +Measured 2026-05-31 (16-core box, warm cache, full flutter suite --exclude-tags pty): coverage default-concurrency = ~36s; coverage --concurrency=12 = ~37s (NO improvement — coverage runs are concurrency-insensitive, the instrumentation/collection dominates); NO-coverage --concurrency=12 = ~21s. Conclusions: (1) Concurrency only helps the NON-coverage path — do NOT bother adding --concurrency to the coverage gate, it buys nothing. (2) The dev inner-loop win is concrete: a no-coverage ''make test'' with --concurrency=12 runs ~21s vs ~36s today (~40% faster) — this is how we ''beat 40s'' for the edit loop. (3) The push-check coverage run is floored at ~36s by coverage itself; getting below that needs a different coverage approach (package:coverage via VM service, or coverage-on-changed-files) — out of scope for the quick win. So the actionable set is unchanged: no-coverage+concurrency dev target, coverage-only-in-push, drop the redundant a11y pass, add a per-test --timeout for hang-safety.', NULL, '2026-05-31 14:46:39', '2026-05-31 14:46:39', '2026-05-31 14:46:39', NULL, 'b6e7ea80f4abe35cbfc4ba9f2ff80e06', 1) ON CONFLICT(hash) DO NOTHING; diff --git a/.pql/changelog/tickets/2026-05.sql b/.pql/changelog/tickets/2026-05.sql index 22bdafe1..cf9294d5 100644 --- a/.pql/changelog/tickets/2026-05.sql +++ b/.pql/changelog/tickets/2026-05.sql @@ -2947,3 +2947,11 @@ INSERT INTO tickets (id, type, parent_id, title, description, status, priority, INSERT INTO tickets (id, type, parent_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-192', 'task', NULL, 'Speed up the test gate: dedupe the a11y run + decouple coverage from the dev loop', 'The pre-push gate (make push-check) and the inner-loop make test are slow; cost is dominated by ''flutter test --coverage --exclude-tags pty'' running the whole flutter suite WITH coverage instrumentation (ci/test.sh). Two concrete levers found by reading the pipeline: (1) FREE WIN — a11y tests run TWICE in push-check: the coverage run in ci/test.sh has no path filter so it already includes test/a11y/, then ci/test_a11y.sh (''flutter test test/a11y/'') runs them again in a separate flutter invocation, chained AFTER test (so not even fast-fail). Drop test-a11y from the push-check chain (keep ''make test-a11y'' as a standalone dev target) to remove a whole redundant flutter compile+boot+run; same tests still execute under test. (2) Coverage is on the dev inner loop — make test always runs --coverage, which is only needed where coverage-gate reads lcov (push-check/CI). Split a fast no-coverage ''make test'' for the edit loop from a coverage run used by push-check/CI; the script even claims <60s warm but coverage blows past that. MEASURE FIRST (cheap): time each ci/*.sh step, check ''flutter test --concurrency'' vs nproc, and look at per-file timing for golden/alchemist tests before further changes. Acceptance: dev ''make test'' runs without coverage and is meaningfully faster; push-check no longer double-runs the a11y suite; coverage is still gated (coverage-gate unchanged, floor 95); all gates stay green; the fast-vs-coverage split is documented in the Makefile/CONTRIBUTING. Domain: tooling/CI. Raised 2026-05-31 while the push gate kept interrupting flow. Add a per-test timeout to the gate''s flutter test (e.g. ci/test.sh: ''flutter test --coverage --timeout 60s''). Motivation discovered 2026-05-31: a hung pumpAndSettle has a 10-MINUTE default timeout, so a few hanging widget tests wedge make push-check for 30+ minutes — which is almost certainly why a MacBook session resorted to ''git push --no-verify'' (the gate never returned; it was not disabled, it was hung). A tight per-test timeout makes any future hang fail fast (60s) instead of wedging a pre-push, so the gate stays usable and nobody is tempted to bypass it. Pair with: keep writing pump()-bounded tests instead of pumpAndSettle() where a view has overlapping async loads (see the T-188 decision_reader_test hang).', 'backlog', 'medium', NULL, NULL, NULL, '2026-05-31 12:08:42', '2026-05-31 12:31:58', NULL, '015fe328db1b9ba7193301a809129dc6', 1) ON CONFLICT(id) DO UPDATE SET type=excluded.type, parent_id=excluded.parent_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); +INSERT INTO tickets (id, type, parent_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-188', 'bug', 'T-186', 'Decision reader no longer opens on click', 'Clicking a decision (in the decisions list, or a D-ref from a ticket/markdown) no longer opens it in the decision reader. The channels are CORRECT — publishers emit (''builtin.decisions'',''selection'') and DecisionDetailView subscribes to the same (decision_detail_view.dart ~line 26). Suspected cause (confirm at fix time): DecisionsExtension.activate (decisions/src/extension.dart ~23-37) tears down and re-contributes the decisions.detail tab on EVERY selection (uncontribute then contribute with initialId), and the context-panel slot may not be surfaced (setVisible/setCollapsed not always succeeding) — unlike the still-working ticket panel, which uses a STATIC tab + activateTab + load (ticket_detail_controller.dart). Fix direction: align the decision reader with the ticket panel pattern (static tab; activateTab + load on selection; no per-click re-contribution) and ensure the context panel is revealed. Acceptance: clicking a decision anywhere reliably opens it in the decision reader, including same-decision-twice and rapid-sequential clicks; widget test.', 'done', 'high', NULL, NULL, NULL, '2026-05-31 11:38:28', '2026-05-31 14:17:22', NULL, '0cffcc7074b9b3aee5b4404ad9077c96', 1) ON CONFLICT(id) DO UPDATE SET type=excluded.type, parent_id=excluded.parent_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); +INSERT INTO tickets (id, type, parent_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-187', 'bug', 'T-186', 'Markdown reader no longer opens on click (files panel, Claude Config tab, wiki links)', 'Clicking a .md no longer opens it in the right-side markdown reader; only the pql markdown tab still works (it is the one surface that correctly publishes (''builtin.markdown'',''selection'')). Broken surfaces: the files panel (file_tree_view.dart _openFile / _FilteredFileRow, ~lines 213-222 and 284-285) and the Claude Config tab (claude_meta_sidebar.dart _configFileRow, ~553-556, shipped in T-183) both call ipc.request(''editor.open'',...) which targets the EDITOR, not the reader; the in-markdown wiki-link handler (markdown_viewer.dart _navigateToRecord) does not handle .md links; and a dead fallback subscribes to DaemonEvent kind ''editor.buffer_activated'' which the EditorRegistry never emits (it emits ''editor.active-changed''). Working pattern: publish (''builtin.markdown'',''selection'') -> the markdown extension activates the context-panel tab and bridges to channel ''load'' after mount -> MarkdownViewer renders. Fix: route .md clicks from the files panel, the Claude Config tab, and .md wiki links to publish (''builtin.markdown'',''selection''); fix or remove the dead editor-event fallback. Design note: per the reporter, clicking a .md should open the READER (non-.md files stay in the editor). Regression: files panel at 681ce3b (2026-04-22); Config tab born-broken at 494a6f1 (T-183). Acceptance: clicking a .md in the files panel, the Claude Config tab, and a wiki .md link each open it in the markdown reader; the dead fallback is gone; widget tests.', 'done', 'high', NULL, NULL, NULL, '2026-05-31 11:38:20', '2026-05-31 14:17:22', NULL, '40eb07a9d378b5a067fcc9488dad3bb9', 1) ON CONFLICT(id) DO UPDATE SET type=excluded.type, parent_id=excluded.parent_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); +INSERT INTO tickets (id, type, parent_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-162', 'bug', 'T-132', 'Slash typeahead omits clide-owned commands (/resume)', '/resume works when typed but doesn''t appear in the composer slash typeahead. Cause: the typeahead sources its command list from activeClaudeConfig.slashCommands (the CLI stream-json probe), which does not include ''resume'' (it''s not an in-session slash command the CLI advertises; clide owns/intercepts it). /clear appears because ''clear'' IS in the probe list. Fix: union kClideOwnedCommands (slash_commands.dart) into the typeahead''s command source — e.g. in ClaudeComposer''s default slashCommandsResolver return {...?activeClaudeConfig?.slashCommands, ...kClideOwnedCommands}. Acceptance: typing /res suggests /resume; /clear still suggested; widget test asserting clide-owned commands appear even when absent from the probe list. Follow-up to T-152/T-156.', 'done', 'low', NULL, NULL, NULL, '2026-05-24 09:29:07', '2026-05-31 14:17:22', NULL, '7eb767d4a8271738e418f6bddd7425bb', 1) ON CONFLICT(id) DO UPDATE SET type=excluded.type, parent_id=excluded.parent_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); +INSERT INTO tickets (id, type, parent_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-192', 'task', NULL, 'Speed up the test gate: dedupe the a11y run + decouple coverage from the dev loop', 'The pre-push gate (make push-check) and the inner-loop make test are slow; cost is dominated by ''flutter test --coverage --exclude-tags pty'' running the whole flutter suite WITH coverage instrumentation (ci/test.sh). Two concrete levers found by reading the pipeline: (1) FREE WIN — a11y tests run TWICE in push-check: the coverage run in ci/test.sh has no path filter so it already includes test/a11y/, then ci/test_a11y.sh (''flutter test test/a11y/'') runs them again in a separate flutter invocation, chained AFTER test (so not even fast-fail). Drop test-a11y from the push-check chain (keep ''make test-a11y'' as a standalone dev target) to remove a whole redundant flutter compile+boot+run; same tests still execute under test. (2) Coverage is on the dev inner loop — make test always runs --coverage, which is only needed where coverage-gate reads lcov (push-check/CI). Split a fast no-coverage ''make test'' for the edit loop from a coverage run used by push-check/CI; the script even claims <60s warm but coverage blows past that. MEASURE FIRST (cheap): time each ci/*.sh step, check ''flutter test --concurrency'' vs nproc, and look at per-file timing for golden/alchemist tests before further changes. Acceptance: dev ''make test'' runs without coverage and is meaningfully faster; push-check no longer double-runs the a11y suite; coverage is still gated (coverage-gate unchanged, floor 95); all gates stay green; the fast-vs-coverage split is documented in the Makefile/CONTRIBUTING. Domain: tooling/CI. Raised 2026-05-31 while the push gate kept interrupting flow. + +Add a per-test timeout to the gate''s flutter test (e.g. ci/test.sh: ''flutter test --coverage --timeout 60s''). Motivation discovered 2026-05-31: a hung pumpAndSettle has a 10-MINUTE default timeout, so a few hanging widget tests wedge make push-check for 30+ minutes — which is almost certainly why a MacBook session resorted to ''git push --no-verify'' (the gate never returned; it was not disabled, it was hung). A tight per-test timeout makes any future hang fail fast (60s) instead of wedging a pre-push, so the gate stays usable and nobody is tempted to bypass it. Pair with: keep writing pump()-bounded tests instead of pumpAndSettle() where a view has overlapping async loads (see the T-188 decision_reader_test hang). + +Measured 2026-05-31 (16-core box, warm cache, full flutter suite --exclude-tags pty): coverage default-concurrency = ~36s; coverage --concurrency=12 = ~37s (NO improvement — coverage runs are concurrency-insensitive, the instrumentation/collection dominates); NO-coverage --concurrency=12 = ~21s. Conclusions: (1) Concurrency only helps the NON-coverage path — do NOT bother adding --concurrency to the coverage gate, it buys nothing. (2) The dev inner-loop win is concrete: a no-coverage ''make test'' with --concurrency=12 runs ~21s vs ~36s today (~40% faster) — this is how we ''beat 40s'' for the edit loop. (3) The push-check coverage run is floored at ~36s by coverage itself; getting below that needs a different coverage approach (package:coverage via VM service, or coverage-on-changed-files) — out of scope for the quick win. So the actionable set is unchanged: no-coverage+concurrency dev target, coverage-only-in-push, drop the redundant a11y pass, add a per-test --timeout for hang-safety.', 'backlog', 'medium', NULL, NULL, NULL, '2026-05-31 12:08:42', '2026-05-31 14:46:39', NULL, 'baf9357d919ab245d6e939737a44695a', 1) ON CONFLICT(id) DO UPDATE SET type=excluded.type, parent_id=excluded.parent_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ecd812b3..42f545bf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -29,9 +29,10 @@ dependency for the governance + ticket workflow described below. ``` make run # launch the desktop app make verify # no-tests sweep — analyze + format + decisions + changelog gate -make test # fast suite — analyze + format + unit + widget + golden +make test # fast dev loop — analyze + format + unit + widget + golden, NO coverage, parallel (~20s) +make test-coverage # same suite WITH coverage (writes coverage/lcov.info; used by the gate) make test-a11y # WCAG-AA contrast + keyboard traversal contracts -make push-check # the pre-push gate; what CI runs +make push-check # the pre-push gate; what CI runs (runs test-coverage, not the no-coverage test) make build-linux # release artefact for the host platform ``` diff --git a/Makefile b/Makefile index a230aa8b..4986ca56 100644 --- a/Makefile +++ b/Makefile @@ -110,9 +110,13 @@ t: gen-build-info ## Run one test path with tee'd output. Usage: make t T=test/p flutter test $(T) 2>&1 | tee test/.test-output/last.log .PHONY: test -test: gen-build-info ## Fast: analyze + format + unit + widget + golden (<60s). +test: gen-build-info ## Fast dev loop: analyze + format + unit + widget + golden, NO coverage, parallel (~20s). ci/test.sh +.PHONY: test-coverage +test-coverage: gen-build-info ## Same suite WITH coverage → coverage/lcov.info (for the gate / CI). Slower. + ci/test.sh --coverage + .PHONY: test-core test-core: gen-build-info ## Core subsystem tests (IPC, PTY, git, pane registry). ci/test_core.sh @@ -133,7 +137,7 @@ test-e2e: ## End-to-end Playwright smoke. test-all: test-core test test-a11y test-integration test-e2e ## Everything, sequentially. .PHONY: coverage-gate -coverage-gate: ## Coverage gate — fails if total line % < pubspec.yaml `coverage_floor:` (D-66). Assumes `make test` ran first. +coverage-gate: ## Coverage gate — fails if total line % < pubspec.yaml `coverage_floor:` (D-66). Assumes `make test-coverage` ran first. ci/coverage_gate.sh .PHONY: changelog-gate @@ -302,7 +306,7 @@ decisions-validate: ## Parser dry-run over governance/{decisions,questions,rejec pql decisions validate .PHONY: push-check -push-check: decisions-validate test-core test test-a11y coverage-gate changelog-gate ## Pre-push gate (fast — <2 min target). +push-check: decisions-validate test-core test-coverage coverage-gate changelog-gate ## Pre-push gate (fast — <2 min target). test-coverage already runs the a11y suite (test/a11y), so no separate test-a11y pass. .PHONY: push-check-full push-check-full: push-check test-integration smoke-bundle ## Pre-release gate (push-check + integration + smoke; slower). diff --git a/ci/test.sh b/ci/test.sh index 7890f2e3..15155001 100755 --- a/ci/test.sh +++ b/ci/test.sh @@ -1,10 +1,23 @@ #!/usr/bin/env bash # Fast test layer — analyze + format + unit + widget + golden. -# Runs in <60s on a warm cache. Called from `make test` and the -# pre-push hook. +# +# Two modes (T-192): +# ci/test.sh dev inner loop — NO coverage, parallel. ~20s warm. +# ci/test.sh --coverage gate run — instrumented; writes coverage/lcov.info +# for coverage-gate. ~36s (coverage is the floor and +# is concurrency-insensitive, measured — so no +# --concurrency here; it buys nothing). +# +# Both pass `--timeout 60s` so a hung test (a stray pumpAndSettle, or a +# real-time deadlock) fails fast instead of wedging the runner for ~10 min and +# stalling the pre-push gate. (Use the pumpAsync helper in tests; never +# pumpAndSettle / Future.delayed(Duration.zero) inside testWidgets.) set -euo pipefail cd "$(dirname "$0")/.." +coverage=0 +[[ "${1:-}" == "--coverage" ]] && coverage=1 + echo "==> flutter analyze" flutter analyze @@ -14,5 +27,10 @@ dart format --set-exit-if-changed . echo "==> dart test (pty — unreliable under the flutter test runner)" dart test --tags pty test/pty/session_test.dart test/panes/registry_test.dart -echo "==> flutter test --coverage (unit + widget + golden)" -flutter test --coverage --exclude-tags pty +if [[ "$coverage" == 1 ]]; then + echo "==> flutter test --coverage (gate; unit + widget + golden + a11y)" + flutter test --coverage --exclude-tags pty --timeout 60s +else + echo "==> flutter test (dev; no coverage, parallel)" + flutter test --exclude-tags pty --concurrency=12 --timeout 60s +fi