feat(bug-fast-track): cheaper/shorter pipeline route for bug-fix tasks (ORCH-019)
A task carrying the Plane `Bug` label takes a shortened route that skips the `architecture` stage (one opus architect run + ADR + check_architecture_done), replacing heavy analysis with a lite package (bug-report + mandatory regression test plan). EVERY Quality Gate / sub-gate runs UNCHANGED — the route is a scheduler property, not a gate (root invariant NFR-1): STAGE_TRANSITIONS / QG_CHECKS / check_* / machine-verdict keys are byte-for-byte preserved. - src/bug_fast_track.py: new leaf (never-raise) — bug_fast_track_applies (local, network-free, checked first), is_bug_task (labels.has_label, Plane API source), skips_architecture (pure DB-backed routing predicate), snapshot. - src/db.py: additive idempotent tasks.track column (TEXT DEFAULT 'full') + set_task_track / get_task_track helpers (missing/NULL -> 'full', fail-safe). - src/stage_engine.py: routing-override on the analysis-exit edge (track='bug' -> development/developer, skipping architect); brd-review-clock stamp extended to analysis->development. get_next_stage/get_agent_for_stage stay pure. - src/webhooks/plane.py: classify task as bug in start_pipeline (applies-first short-circuit; never-raise -> full cycle on any error). - src/main.py: additive bug_fast_track block in GET /queue + POST /bug-fast-track/escalate (reset 'bug'->'full' to return to the full cycle). - src/config.py: bug_fast_track_enabled / _label / _repos flags (empty CSV -> self-hosting only). - src/notifications.py: optional 🐞 marker on the bug-track card (never-raise). - Prompts: analyst.md (lite bug package + escalation), reviewer.md (regression- test axis) — 52d canon preserved. - Docs: CLAUDE.md, README.md (env + API + section), docs/architecture/README.md, CHANGELOG.md, .env.example. - Tests: tests/test_bug_fast_track*.py + test_db_migrations.py + queue block (TC-01..TC-15). Full regression green (1551 passed). Kill-switch ORCH_BUG_FAST_TRACK_ENABLED=false -> 1:1 pre-ORCH-019 (zero regression; residual track column harmless). Refs: ORCH-019 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
79
tests/test_db_migrations.py
Normal file
79
tests/test_db_migrations.py
Normal file
@@ -0,0 +1,79 @@
|
||||
"""ORCH-019 (TC-15) — additive, idempotent tasks.track migration.
|
||||
|
||||
The bug-fast-track stores the task type in an additive ``tasks.track`` column
|
||||
(``TEXT DEFAULT 'full'``) created via ``_ensure_column`` (idempotent). A repeated
|
||||
``init_db`` must not crash, existing rows must default to ``'full'``, and the
|
||||
helpers must round-trip.
|
||||
"""
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
import pytest
|
||||
|
||||
os.environ.setdefault(
|
||||
"ORCH_DB_PATH", os.path.join(tempfile.gettempdir(), "test_db_migrations.db")
|
||||
)
|
||||
os.environ.setdefault("ORCH_GITEA_TOKEN", "test-token")
|
||||
os.environ.setdefault("ORCH_PLANE_API_TOKEN", "test-token")
|
||||
|
||||
import src.db as db # noqa: E402
|
||||
from src.db import init_db, get_db, set_task_track, get_task_track # noqa: E402
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def fresh_db(tmp_path, monkeypatch):
|
||||
dbfile = tmp_path / "m.db"
|
||||
monkeypatch.setattr(db.settings, "db_path", str(dbfile))
|
||||
init_db()
|
||||
yield
|
||||
|
||||
|
||||
def _columns(table):
|
||||
conn = get_db()
|
||||
try:
|
||||
return [r[1] for r in conn.execute(f"PRAGMA table_info({table})").fetchall()]
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def test_tc15_track_column_present_with_default():
|
||||
assert "track" in _columns("tasks")
|
||||
# A row inserted WITHOUT track gets the DEFAULT 'full'.
|
||||
conn = get_db()
|
||||
conn.execute(
|
||||
"INSERT INTO tasks (plane_id, work_item_id, repo, branch, stage, title) "
|
||||
"VALUES ('p','ORCH-1','orchestrator','feature/x','analysis','t')"
|
||||
)
|
||||
conn.commit()
|
||||
row = conn.execute("SELECT track FROM tasks WHERE work_item_id='ORCH-1'").fetchone()
|
||||
conn.close()
|
||||
assert row["track"] == "full"
|
||||
|
||||
|
||||
def test_tc15_init_db_idempotent():
|
||||
# Running init_db again is a no-op on the existing column (no crash).
|
||||
init_db()
|
||||
init_db()
|
||||
assert "track" in _columns("tasks")
|
||||
|
||||
|
||||
def test_tc15_helpers_round_trip():
|
||||
conn = get_db()
|
||||
cur = conn.execute(
|
||||
"INSERT INTO tasks (plane_id, work_item_id, repo, branch, stage, title) "
|
||||
"VALUES ('p2','ORCH-2','orchestrator','feature/y','analysis','t')"
|
||||
)
|
||||
tid = cur.lastrowid
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
assert get_task_track(tid) == "full" # default
|
||||
set_task_track(tid, "bug")
|
||||
assert get_task_track(tid) == "bug"
|
||||
set_task_track(tid, "full")
|
||||
assert get_task_track(tid) == "full"
|
||||
|
||||
|
||||
def test_tc15_get_task_track_missing_row_failsafe():
|
||||
# Unknown task id -> 'full' (fail-safe -> full cycle), never raises.
|
||||
assert get_task_track(999999) == "full"
|
||||
Reference in New Issue
Block a user