aboutsummaryrefslogtreecommitdiff
path: root/scripts/tests
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-06-06 18:27:47 -0500
committerCraig Jennings <c@cjennings.net>2026-06-06 18:27:47 -0500
commit7bd9f49ed7cb8482226d70ffaaa3d83fde194710 (patch)
treed3db8680e0e0091382fd0759ca5877c07c5fc45d /scripts/tests
parent4250bbb39ab1f01a09567ea8f202c2109a2dad30 (diff)
downloadpearl-7bd9f49ed7cb8482226d70ffaaa3d83fde194710.tar.gz
pearl-7bd9f49ed7cb8482226d70ffaaa3d83fde194710.zip
fix(scripts): make Triage the intake column and the default new-issue state
Two problems showed up dogfooding the seeded board. New issues landed in Backlog, because Linear's teamCreate sets the team's default new-issue state to its own Backlog default and the seed never changed it. And the intake column read "Triage Queue" when "Triage" is the name we want. The script now points the team's defaultIssueStateId at the intake column after the columns reconcile, so a fresh issue opens in Triage instead of Backlog. It skips the update when the default is already correct, so a re-run stays a no-op. The intake column is renamed Triage throughout: the target state, the Pearl Inbox view filter, and INTAKE_STATE. find_team reads defaultIssueState so the idempotence check has something to compare. Both mutations were schema-introspected first: TeamUpdateInput.defaultIssueStateId and CustomViewUpdateInput.filterData. The live board was fixed the same way (rename, default-state, Inbox filter) and the dry-run against it comes back all-update, no-create. 13 tests cover the rename, the default-state set on a fresh seed, and the skip when it's already Triage.
Diffstat (limited to 'scripts/tests')
-rw-r--r--scripts/tests/test_seed_pearl_workspace.py28
1 files changed, 19 insertions, 9 deletions
diff --git a/scripts/tests/test_seed_pearl_workspace.py b/scripts/tests/test_seed_pearl_workspace.py
index ea22230..fdf967b 100644
--- a/scripts/tests/test_seed_pearl_workspace.py
+++ b/scripts/tests/test_seed_pearl_workspace.py
@@ -9,7 +9,7 @@ The state reconcile is shaped by what Linear actually allows, verified live:
teamCreate seeds its own default columns, a team must keep an unstarted state,
and the Duplicate state is reserved. So the script never archives -- it renames
Linear's defaults into the six Agile columns (repurposing the unstarted Todo as
-Triage Queue), creates only the genuinely-new Icebox, and leaves Duplicate
+Triage), creates only the genuinely-new Icebox, and leaves Duplicate
alone. Positions are set with distinct nonzero values, since Linear ignores a
0.0 position and appends new states at a high one.
"""
@@ -28,13 +28,13 @@ import seed_pearl_workspace as seed
def test_target_states_are_the_six_agile_columns_in_order():
assert [s["name"] for s in seed.TARGET_STATES] == [
- "Icebox", "Triage Queue", "Backlog", "In Progress", "Done", "Canceled"]
+ "Icebox", "Triage", "Backlog", "In Progress", "Done", "Canceled"]
def test_target_state_types_and_distinct_nonzero_positions():
by_name = {s["name"]: s for s in seed.TARGET_STATES}
assert by_name["Icebox"]["type"] == "backlog"
- assert by_name["Triage Queue"]["type"] == "unstarted" # repurposed from Todo
+ assert by_name["Triage"]["type"] == "unstarted" # repurposed from Todo
assert by_name["Backlog"]["type"] == "backlog"
assert by_name["In Progress"]["type"] == "started"
assert by_name["Done"]["type"] == "completed"
@@ -46,7 +46,7 @@ def test_target_state_types_and_distinct_nonzero_positions():
def test_triage_queue_sources_the_default_todo():
- tq = next(s for s in seed.TARGET_STATES if s["name"] == "Triage Queue")
+ tq = next(s for s in seed.TARGET_STATES if s["name"] == "Triage")
assert "Todo" in tq["sources"]
@@ -67,7 +67,7 @@ def test_open_view_excludes_completed_and_cancelled():
def test_icebox_and_inbox_views_filter_to_their_column():
views = {v["name"]: v for v in seed.desired_views("p")}
assert views["Pearl Icebox"]["filterData"]["state"]["name"]["eq"] == "Icebox"
- assert views["Pearl Inbox"]["filterData"]["state"]["name"]["eq"] == "Triage Queue"
+ assert views["Pearl Inbox"]["filterData"]["state"]["name"]["eq"] == "Triage"
# --- pure: the state reconcile planner ----------------------------------------
@@ -89,9 +89,9 @@ def _plan_by_target(existing):
def test_reconcile_against_linear_defaults_renames_and_creates():
plan = _plan_by_target(LINEAR_DEFAULTS)
- # Icebox is genuinely new; the rest map onto a default (Triage Queue <- Todo).
+ # Icebox is genuinely new; the rest map onto a default (Triage <- Todo).
assert plan["Icebox"] is None
- assert plan["Triage Queue"] == "d2" # the unstarted Todo
+ assert plan["Triage"] == "d2" # the unstarted Todo
assert plan["Backlog"] == "d1"
assert plan["In Progress"] == "d3"
assert plan["Done"] == "d4"
@@ -100,7 +100,7 @@ def test_reconcile_against_linear_defaults_renames_and_creates():
def test_reconcile_is_idempotent_against_already_named_columns():
existing = [{"id": f"s{i}", "name": n, "type": "x"} for i, n in enumerate(
- ["Icebox", "Triage Queue", "Backlog", "In Progress", "Done", "Canceled"])]
+ ["Icebox", "Triage", "Backlog", "In Progress", "Done", "Canceled"])]
plan = _plan_by_target(existing)
assert all(mid is not None for mid in plan.values()) # everything matches, nothing created
@@ -148,6 +148,7 @@ def test_seed_on_empty_workspace_reconciles_then_builds():
"TeamStates": {"data": {"team": {"states": {"nodes": LINEAR_DEFAULTS}}}},
"StateCreate": _ok("workflowStateCreate", {"workflowState": {"id": "new1"}}),
"StateUpdate": _ok("workflowStateUpdate", {"workflowState": {"id": "u"}}),
+ "TeamUpdate": _ok("teamUpdate", {}),
"Projects": {"data": {"projects": {"nodes": []}}},
"ProjectCreate": _ok("projectCreate", {"project": {"id": "p1", "name": "Pearl"}}),
"CustomViews": {"data": {"customViews": {"nodes": []}}},
@@ -158,23 +159,31 @@ def test_seed_on_empty_workspace_reconciles_then_builds():
assert ops.count("TeamCreate") == 1
assert ops.count("StateCreate") == 1 # only Icebox is new
assert ops.count("StateUpdate") == 6 # six columns positioned/renamed
+ assert ops.count("TeamUpdate") == 1 # default new-issue state -> Triage
assert ops.count("ProjectCreate") == 1
assert ops.count("CustomViewCreate") == 3
assert summary["team"] == "t1"
assert summary["project"] == "p1"
assert summary["leftover_states"] == ["Duplicate"]
+ assert summary["default_issue_state"] == "Triage"
+ # the default points at Triage (which reconciled from the unstarted Todo, d2)
+ team_update = next(v for op, v in fake.calls if op == "TeamUpdate")
+ assert team_update["input"]["defaultIssueStateId"] == "d2"
assert "workflowStateArchive" not in ops # never archives
def test_seed_is_idempotent_against_a_seeded_workspace():
seeded_states = [{"id": f"s{i}", "name": n, "type": "x"} for i, n in enumerate(
- ["Icebox", "Triage Queue", "Backlog", "In Progress", "Done", "Canceled", "Duplicate"])]
+ ["Icebox", "Triage", "Backlog", "In Progress", "Done", "Canceled", "Duplicate"])]
fake = FakeTransport({
+ # default already points at Triage (s1), so no team update fires either
"Teams": {"data": {"teams": {"nodes": [
{"id": "t1", "key": "PEARL", "name": "Pearl",
+ "defaultIssueState": {"id": "s1"},
"states": {"nodes": seeded_states}}]}}},
"TeamStates": {"data": {"team": {"states": {"nodes": seeded_states}}}},
"StateUpdate": _ok("workflowStateUpdate", {"workflowState": {"id": "u"}}),
+ "TeamUpdate": _ok("teamUpdate", {}),
"Projects": {"data": {"projects": {"nodes": [{"id": "p1", "name": "Pearl"}]}}},
"CustomViews": {"data": {"customViews": {"nodes": [
{"id": "v1", "name": "Pearl Open Issues"},
@@ -185,6 +194,7 @@ def test_seed_is_idempotent_against_a_seeded_workspace():
ops = fake.ops()
for create_op in ("TeamCreate", "StateCreate", "ProjectCreate", "CustomViewCreate"):
assert create_op not in ops # nothing re-created
+ assert "TeamUpdate" not in ops # default already correct
def test_client_raises_on_graphql_errors():