Skip to content

[local-test-e2e-cover-the-linear-issue-lifecycl] test(e2e): cover the Linear issue lifecycle against an offline fake GraphQL server - #374

Open
danii1 wants to merge 1 commit into
mainfrom
feature/local-test-e2e-cover-the-linear-issue-lifecycl
Open

danii1 wants to merge 1 commit into
mainfrom
feature/local-test-e2e-cover-the-linear-issue-lifecycl

Conversation

@danii1

@danii1 danii1 commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

Why

Task local-test-e2e-cover-the-linear-issue-lifecycl: test(e2e): cover the Linear issue lifecycle against an offline fake GraphQL server

Linear was the only supported task tracker with zero lifecycle coverage: only helpers/tracker-smoke.ts detected LINEAR_API_KEY, with no fake server or end-to-end run. A drift in Linear GraphQL operation names, variables, issue lookup, comments, workflow-state transitions, or JSON IssueFilter selection would have shipped silently. The new offline fake closes that gap without a live account, token, or network access.

What changed

  • Added packages/e2e/providers/fake-linear.ts: an in-process Bun.serve fake bound to 127.0.0.1:0 with one POST /graphql route. It owns one ENG team, Todo/In Progress/In Review/Done workflow states, issues, labels, markdown comments, and a fixed advancing clock. It dispatches on IssueById, IssueComments, TeamStates, CommentCreate, IssueUpdate, and SearchIssues; validates variables; and records unrecognized operations or malformed payloads in unexpectedOperations with a diagnostic GraphQL error response. It accepts but never reads/logs Authorization.
  • Added packages/e2e/tests/local/tracker-linear-fake.test.ts: explicit-key ENG-1 and JSON IssueFilter --query scenarios driven through the built CLI and FixtureWorkspace. Both seed an expected issue plus a decoy, write linear.projects.ENG status settings with prStatus equal to inProgressStatus, and assert exit 0, exact remote branch SHA, only src/greeting.ts changed, one succeeded tracker = "linear" run record, final workflow state, preserved unrelated label, one implementation comment, surviving seeded comment, marker delivery to the fake agent, untouched decoy, and empty unexpectedOperations.
  • Updated packages/e2e/README.md, packages/e2e/docs/tracker-e2e-plan.md, and packages/e2e/docs/coverage-matrix.md: documented the Linear fake and its env vars/operations, marked Linear Layer 3 implemented, changed the Linear matrix row to default, and updated backlog item 17.

Scope

Included exactly the offline Linear fake and lifecycle coverage described in the task, reusing TrackerFixture, lifecycle assertions, FixtureWorkspace, and buildTrackerSettings. No live or skipped Linear suite was added, no production Linear client code was changed, and FixtureWorkspace was not modified because no extra CLI args were needed. Trello/Asana/Azure DevOps and live Linear team/project lifecycle, estimation, and attachment uploads remain out of scope.

Tradeoffs

The unique description marker is also embedded in the issue title because the required --query scenario uses Linear’s title.containsIgnoreCase filter while lifecycle assertions read the marker back from the issue body. prStatus is intentionally set equal to inProgressStatus because the local bare Git remote cannot create the PR that would trigger a separate post-PR transition; the same approach is already used by the offline Jira test. The fake supports a small superset of IssueUpdate fields (estimate, labelIds, title, description) beyond the currently exercised stateId to avoid freezing an unnecessarily narrow contract.

Blast Radius

Only the @devintern/e2e workspace is affected. The new provider implements the existing TrackerFixture contract and is consumed solely by the new test; existing Jira/GitHub/GitLab fixtures, helpers, and all production tracker code are untouched. The docs changes are additive or status updates. No product bug was found in LinearClient or LinearTaskTrackerClient, so no product fix was bundled.

Verification

All commands were run from the monorepo root unless noted:

  • bun run --filter @getdevintern/code build → succeeded (Built devintern v2.22.0).
  • bun run --filter @devintern/e2e test → 137 pass, 16 skip, 4 todo, 0 fail across 157 tests in 25 files.
  • bun run --filter @devintern/e2e typecheck → exit 0.
  • bun run --filter @devintern/e2e lint → exit 0.
  • bun run --filter @devintern/e2e format:check → all 89 matched files correctly formatted.
  • Targeted bun test tests/local/tracker-linear-fake.test.ts → 2 pass, 0 fail, 61 assertions, run repeatedly after the final fake changes.

This PR was automatically created by @devintern/code

…(e2e): cover the Linear issue lifecycle against an offline fake GraphQL server
@danii1 danii1 added the devintern-authored Pull request written by DevIntern working its own backlog label Nov 4, 2026
@danii1

danii1 commented Nov 4, 2026

Copy link
Copy Markdown
Contributor Author

Artifacts & verification

Run: 48 · Commit: a51e3a9afc8fe95b55b2060cd7fcd4edf5f6b1f3

Change explanation

Produced by pr-explainer-narrative, execution 1 at a51e3a9afc8fe95b55b2060cd7fcd4edf5f6b1f3.

Offline Linear issue-lifecycle e2e coverage

Why this change is needed

The task states that Linear is the only supported task tracker with zero lifecycle e2e coverage. Its GraphQL API and schema are external, so a drift in request shape or field names for issue lookup, comments, workflow-state transitions, or --query selection would ship silently. Previously the only Linear reference in packages/e2e was credential detection in helpers/tracker-smoke.ts.

What changed

  • Added an in-process Linear GraphQL fake (packages/e2e/providers/fake-linear.ts). It binds 127.0.0.1:0, exposes a single POST /graphql, accepts any Authorization header without logging it, and keeps one team, workflow states, issues, labels, and markdown comments in memory with a fixed advancing clock. It implements only IssueById, IssueComments, TeamStates, CommentCreate, IssueUpdate, and SearchIssues, validates the variables, and records any unrecognized operation or malformed payload in unexpectedOperations while returning a diagnostic GraphQL error. It implements the TrackerFixture surface (type = "linear", statusKind = "state") and parses raw HTTP/GraphQL rather than importing the product client.
  • Added two default-suite scenarios (packages/e2e/tests/local/tracker-linear-fake.test.ts). Both seed one expected issue plus a decoy outside the selector, write per-team statuses to .devintern-code/settings.json, and run the built CLI through FixtureWorkspace. Scenario 1 selects by explicit key; scenario 2 selects through a JSON IssueFilter --query with containsIgnoreCase. Assertions cover exit code, pushed feature/<key> branch and remote SHA, the single-file diff and greeting content, exactly one succeeded run record with tracker = "linear", final workflow state read back through the fake, preserved seeded label, one implementation comment, the seeded comment, marker delivery to the fake agent, and an untouched decoy. Both scenarios assert unexpectedOperations is empty and clean up the server and fixture root in finally.
  • Updated documentation and the backlog: a "Offline fake Linear server" section in packages/e2e/README.md, Linear marked implemented in Layer 3 of docs/tracker-e2e-plan.md, the Linear row changed to default in docs/coverage-matrix.md with remaining gaps noted, and backlog item 17 amended.

No production code was modified; the diff is the fake provider, one test file, and three documentation files.

Validation actually performed

Recorded in the pipeline evidence (not re-run while writing this):

  • bun run --filter @getdevintern/code build exited 0.
  • The two new Linear scenarios passed: 2 pass, 0 fail, 61 expect() calls.
  • The full e2e suite passed: 137 pass, 16 skip, 4 todo, 0 fail across 25 files. The auto-review step independently reports the same result for bun test tests/local.
  • typecheck, lint, and format:check for @devintern/e2e all exited 0.

Not recorded: any live Linear run. Per the task, none was added, and both scenarios skip when the built CLI is absent, matching the Jira fake test. The 16 skips in the suite total are not broken down in the evidence, so this does not independently confirm the skip guard fired.

Material risks

  • Fixture fidelity gap (found in auto-review, low priority). findTickets returns every in-memory issue, including ones moved to Done by closeTicket, while the TrackerFixture contract describes a search over open tickets. The current scenarios do not hit this, but a future scenario using the shared selection helper could select an already-closed ticket and mask a regression.
  • Partial transition coverage. The test deliberately sets prStatus equal to inProgressStatus because the local bare remote cannot create a PR. So the run proves the Todo to In Progress transition and the state lookup, but not a distinct post-PR transition; auto-review flags the same limitation.
  • Hand-maintained request contract. Because the fake intentionally does not import LinearClient, a deliberate client-side query change will fail these tests until the fake is updated. That is the intended signal, but it is maintenance cost.
  • Test-only blast radius. The new fake is imported only by its own test, so regression risk to product behavior is low; the risk is false confidence if the fake diverges from real Linear semantics.

Where reviewers should focus

  1. packages/e2e/providers/fake-linear.ts — operation dispatch, variable validation, the IssueFilter compiler, state/id handling, and the unexpectedOperations path.
  2. packages/e2e/tests/local/tracker-linear-fake.test.ts — settings written, the prStatus comment and its consequence, decoy isolation assertions, and cleanup.
  3. The Linear rows in docs/coverage-matrix.md and docs/tracker-e2e-plan.md — confirm they claim only what the tests actually cover.

Suggested checks (inference, not observed here): add a small case asserting a closed ticket is excluded from findTickets if you want the fixture aligned with the documented contract, and consider a separate state to exercise the post-PR transition once a code host that can create a PR is available.

Visual explanation

Produced by pr-explainer-visual, execution 1 at a51e3a9afc8fe95b55b2060cd7fcd4edf5f6b1f3.

Show artifact

Summary

Adds an in-process Linear GraphQL fake (packages/e2e/providers/fake-linear.ts) and two default-suite scenarios in packages/e2e/tests/local/tracker-linear-fake.test.ts that drive the built CLI through a real Linear issue lifecycle. Linear was the only supported tracker with zero lifecycle coverage, so request-shape or field-name drift in lookup, comments, workflow states, or --query selection could ship silently; this closes that gap offline, with no credentials or network. The change also updates the e2e README, tracker plan, coverage matrix, and backlog entry. It is test-and-docs only: no production or shared helper code is modified.

Evidence

packages/e2e/
├── providers/
│   ├── tracker.ts                     unchanged fixture contract
│   ├── fake-jira.ts                   existing offline pattern
│   └── fake-linear.ts                 NEW  Bun.serve on 127.0.0.1:0, in-memory state
└── tests/local/
    ├── tracker-jira-fake.test.ts
    └── tracker-linear-fake.test.ts    NEW  scenario 1 (key) + scenario 2 (--query)

Operation dispatch in the fake, matching what the production client sends:

POST /graphql  (any Authorization, never logged)
  ├─ IssueById       identifier/UUID lookup
  ├─ IssueComments   markdown comment bodies
  ├─ TeamStates      workflow state ids/names
  ├─ CommentCreate   implementation summary
  ├─ IssueUpdate     stateId, estimate, labelIds, title, description
  └─ SearchIssues    JSON IssueFilter title/description + plain-text contains
  └─ anything else → diagnostic GraphQL error + entry in unexpectedOperations

Validation reported in the pipeline evidence (observed, post-change only):

  • bun run --filter @getdevintern/code build exited 0.
  • Targeted run: bun test tests/local/tracker-linear-fake.test.ts gave 2 pass, 0 fail, 61 expect() calls.
  • Full suite: bun run --filter @devintern/e2e test gave 137 pass, 16 skip, 4 todo, 0 fail across 25 files.
  • typecheck, lint, and format:check each exited 0.

Before/after execution results were not captured, so the numbers above are a single post-change run with no baseline to compare against. Scoping note: because the local bare remote cannot create a PR, the test sets prStatus equal to inProgressStatus, so the run proves the pre-PR transition and state lookup but does not separately observe a post-PR transition.

Merge Danger

  • Reverting removes only new files and documentation edits: Linear returns to gap in the coverage matrix and the default suite loses two scenarios. No user-facing runtime behavior changes, because no production file is touched.
  • Blast radius is confined to the offline e2e suite: FakeLinearTracker is imported only by the new test, binds an ephemeral loopback port, and is closed in finally. A fake defect surfaces as a red test, not a product regression.
  • Safe to ship without staged rollout since nothing is deployed. The residual risk is coverage confidence, not behavior: the fake's findTickets currently returns closed issues as well, which diverges from the contract's "search open tickets" wording and could mask a future query selection bug.

Verification report

Produced by __verify-1, execution 1 at a51e3a9afc8fe95b55b2060cd7fcd4edf5f6b1f3.

Show artifact
{
  "summary": "The implementation satisfies the task. A new offline Linear GraphQL fake implements the full TrackerFixture surface and the exact operations/variables the production client sends (IssueById, IssueComments, TeamStates, CommentCreate, IssueUpdate, SearchIssues), with strict unexpected-operation recording. Two default-suite scenarios drive the built CLI through explicit-key and JSON IssueFilter `--query` selection against a seeded issue and decoy, reusing the existing lifecycle assertions. Documentation and backlog rows were updated. I ran the full acceptance sequence: code build, e2e test (137 pass, 16 skip, 4 todo, 0 fail), typecheck, lint, and format:check all passed. The change is test/docs-only: no production file was modified, so blast radius is confined to the new fixture and test.",
  "items": [
    {
      "priority": "info",
      "confidence": "high",
      "category": "testing",
      "file": "packages/e2e/tests/local/tracker-linear-fake.test.ts",
      "line": "158",
      "issue": "All task acceptance checks pass, including both new Linear scenarios.",
      "evidence": "Ran `bun run --filter @getdevintern/code build` (exit 0); `bun test tests/local/tracker-linear-fake.test.ts` => 2 pass, 0 fail, 61 expect() calls; `bun run --filter @devintern/e2e test` => 137 pass, 16 skip, 4 todo, 0 fail (0 failures across 25 files); typecheck, lint, and format:check all exited 0.",
      "suggestion": "No action needed."
    },
    {
      "priority": "info",
      "confidence": "high",
      "category": "code-quality",
      "file": "packages/e2e/providers/fake-linear.ts",
      "line": "87",
      "issue": "Blast radius is contained: the commit adds only the fake provider, one default-suite test, and three docs updates; no production or shared helper code changed, and the new fixture is imported only by its own test.",
      "evidence": "Ran `git show --name-status --format=\"\" HEAD` => M README.md, M coverage-matrix.md, M tracker-e2e-plan.md, A fake-linear.ts, A tracker-linear-fake.test.ts. `grep -rn \"FakeLinearTracker\" packages/e2e` shows only tests/local/tracker-linear-fake.test.ts references outside the fixture itself. The change is safe because the fixture is dead code to every other consumer and all writes happen against an ephemeral 127.0.0.1 Bun.serve instance created inside the test.",
      "suggestion": "Keep the fixture scoped to this single test unless another Linear scenario genuinely needs it."
    },
    {
      "priority": "low",
      "confidence": "medium",
      "category": "code-quality",
      "file": "packages/e2e/providers/fake-linear.ts",
      "line": "581",
      "issue": "An empty IssueFilter object compiles to an empty matcher list, and `matchers.every(...)` vacuously returns true, so `{}` is treated as match-all rather than an unsupported/malformed filter. This does not affect the required scenarios, but it weakens the fake's stated strictness.",
      "evidence": "Pointed at packages/e2e/providers/fake-linear.ts:558-581: `const matchers: IssueMatcher[] = []; ... return (issue) => matchers.every((matcher) => matcher(issue));` — with no entries this always evaluated true. Unproven as a practical failure because neither the test nor the production client sends an empty filter in this lifecycle.",
      "suggestion": "Return `undefined` (and record an unexpected operation) when no supported filter condition was compiled, e.g. `if (matchers.length === 0) return undefined;`."
    },
    {
      "priority": "low",
      "confidence": "medium",
      "category": "documentation",
      "file": "packages/e2e/docs/coverage-matrix.md",
      "line": "59",
      "issue": "The matrix now has two Linear tracker rows (`Linear: offline fake ...` at line 55 and `Offline fake Linear server (default lifecycle)` at line 59) with identical status, evidence, and missing columns. This is redundant but not incorrect.",
      "evidence": "Pointed at packages/e2e/docs/coverage-matrix.md:55 and :59 — both list `default`, the same test file and command, and the same 'Live team/project lifecycle, estimation, attachment uploads' gap.",
      "suggestion": "Consider keeping the task-tracker row and dropping the duplicate 'Offline fake Linear server' row (as the Jira section does), or differentiating what each row covers."
    },
    {
      "priority": "low",
      "confidence": "medium",
      "category": "code-quality",
      "file": "packages/e2e/providers/fake-linear.ts",
      "line": "261",
      "issue": "`FakeLinearTracker.stop()` duplicates `cleanup()` and is unused by the new test, adding a second public shutdown path that must be kept consistent. The Jira fake has the same shape, so this is consistency rather than a defect.",
      "evidence": "Pointed at packages/e2e/providers/fake-linear.ts:255-263; `grep -rn \"\\.stop()\" packages/e2e/tests/local/tracker-linear-fake.test.ts` finds no call, while the test uses `await tracker.cleanup()` in `finally`.",
      "suggestion": "Either drop `stop()` or document that it is the non-async escape hatch; mirroring fake-jira.ts is acceptable."
    }
  ],
  "approved": true
}

Auto-review summary

Produced by auto-review, execution 1 at a51e3a9afc8fe95b55b2060cd7fcd4edf5f6b1f3.

Show artifact
{
  "iterations": 1,
  "success": true,
  "finalFeedback": {
    "summary": "The change is an add-only e2e fixture: `FakeLinearTracker` is imported only by `packages/e2e/tests/local/tracker-linear-fake.test.ts:14`, no production or shared helper code changed, and the full local suite passes (`bun test tests/local`: 137 pass, 16 skip, 4 todo, 0 fail). The one fact that clears most of the blast radius is that the new server and test are test-only; the remaining findings are confined to fixture fidelity, documentation, and coverage gaps.",
    "items": [
      {
        "priority": "low",
        "confidence": "high",
        "category": "bug",
        "file": "packages/e2e/providers/fake-linear.ts",
        "line": "233-252",
        "issue": "`findTickets` returns every in-memory issue, including ones moved to Done by `closeTicket`, while the `TrackerFixture` contract says the query searches open tickets (`packages/e2e/providers/tracker.ts:94-99`). A future Linear scenario using the shared `TrackerScenario.waitForSelectedTickets` path (`packages/e2e/helpers/tracker-scenario.ts:331`) could select an already-closed ticket and mask a real regression.",
        "evidence": "Ran a Bun script against `FakeLinearTracker`: `createTicket` -> `closeTicket` -> `findTickets` with a title filter printed status `Done` and returned the closed `ENG-1` ticket.",
        "suggestion": "Filter out completed/done issues in `findTickets` (or add the Linear state predicate to the compiled matcher) so the fixture matches the contract, and add a small test that closes a ticket and asserts it is no longer returned."
      },
      {
        "priority": "low",
        "confidence": "high",
        "category": "testing",
        "file": "packages/e2e/tests/local/tracker-linear-fake.test.ts",
        "line": "57-63, 128-150",
        "issue": "`statusesFor` sets `prStatus` to `tracker.statuses.inProgress`, the same state as `inProgressStatus`. The run only proves the Todo -> In Progress transition; it cannot observe the post-PR actioned transition in `packages/code/src/lib/task/actioned-transition.ts:80-84`, so a Linear-only regression there would go unnoticed.",
        "evidence": "I copied the test to `/tmp/opencode` and changed `prStatus` to `tracker.statuses.pr` (`In Review`); both scenarios failed with `Expected: In Review`, `Received: In Progress`, proving `applyActionedTransition` is not exercised. The unmodified suite passes.",
        "suggestion": "Add a focused test that drives a distinct `prStatus` (for example a direct actioned-transition test with a Linear fake, or a fake code host that returns a PR), or explicitly document this gap in `coverage-matrix.md` and `tracker-e2e-plan.md`."
      },
      {
        "priority": "low",
        "confidence": "high",
        "category": "bug",
        "file": "packages/e2e/providers/fake-linear.ts",
        "line": "418-429",
        "issue": "The `labelIds` branch accepts unknown IDs, fabricates a label whose name is the raw ID, and returns success. Real Linear would reject an unknown label ID (or a malformed element), so a future label-update test could pass against an invalid client payload; a non-string element becomes an empty-id label instead of an error.",
        "evidence": "Ran a Bun script: `IssueUpdate` with `labelIds: [label-keep, does-not-exist]` returned success and the issue labels became `[keep, does-not-exist]` with `unexpectedOperations` empty.",
        "suggestion": "Maintain a fake label catalog (and implement `TeamLabels`/`getLabels`), or reject IDs not present in the catalog with an unexpected operation; also reject non-string array elements."
      },
      {
        "priority": "low",
        "confidence": "high",
        "category": "documentation",
        "file": "packages/e2e/providers/fake-linear.ts",
        "line": "3-9, 265-301",
        "issue": "The header claims a client regression in the wrong field fails, but `handle` only inspects the operation name and variables and never the selection set; `issuePayload` always returns a fixed superset. A query selecting a bogus field therefore succeeds, so the fake does not catch that class of regression.",
        "evidence": "Sent a query selecting `bogusField` on `IssueById`; the fake returned HTTP 200 with a full `data` payload and no errors, and `unexpectedOperations` stayed empty.",
        "suggestion": "Either narrow the comment to operation/variable mismatches or validate the requested selection set (at least required field names) before returning the payload; add a test for an unknown field."
      },
      {
        "priority": "low",
        "confidence": "medium",
        "category": "documentation",
        "file": "packages/e2e/helpers/opt-in-suites.ts",
        "line": "97-101",
        "issue": "The planned Trello fake reason still says only `the offline Jira fake runs in the default suite`; this PR adds the offline Linear fake to the same default suite, so the test output is stale and understates coverage.",
        "evidence": "`packages/e2e/helpers/opt-in-suites.ts:100` still names only Jira; `README.md:342-354` and `tracker-e2e-plan.md:228+` now document both fakes.",
        "suggestion": "Update the reason to name both the Jira and Linear default fakes, or make it generic."
      },
      {
        "priority": "info",
        "confidence": "medium",
        "category": "testing",
        "file": "packages/e2e/tests/local/tracker-linear-fake.test.ts",
        "line": "129-137",
        "issue": "`assertTicketState` checks seed labels, but Linear status transitions use `stateId` only (`packages/code/src/lib/trackers/linear/linear-task-tracker-client.ts:105-121`), so no production code path mutates labels. The `surviving unrelated labels` claim in `README.md:362` therefore only proves the fake retains its own initial labels, not that the client preserves them.",
        "evidence": "Pointed: `transitionStatus` calls `getWorkflowStates`/`updateIssueState` only; `assertTicketState` at `packages/e2e/helpers/tracker-lifecycle.ts:137` asserts labels from the same fake object.",
        "suggestion": "Either drop the label-preservation wording for Linear or add a deliberate label-changing assertion (for example a test that a state transition leaves labels untouched through the fake's `IssueUpdate` path)."
      }
    ],
    "approved": true
  },
  "history": [
    {
      "iteration": 1,
      "feedback": {
        "summary": "The change is an add-only e2e fixture: `FakeLinearTracker` is imported only by `packages/e2e/tests/local/tracker-linear-fake.test.ts:14`, no production or shared helper code changed, and the full local suite passes (`bun test tests/local`: 137 pass, 16 skip, 4 todo, 0 fail). The one fact that clears most of the blast radius is that the new server and test are test-only; the remaining findings are confined to fixture fidelity, documentation, and coverage gaps.",
        "items": [
          {
            "priority": "low",
            "confidence": "high",
            "category": "bug",
            "file": "packages/e2e/providers/fake-linear.ts",
            "line": "233-252",
            "issue": "`findTickets` returns every in-memory issue, including ones moved to Done by `closeTicket`, while the `TrackerFixture` contract says the query searches open tickets (`packages/e2e/providers/tracker.ts:94-99`). A future Linear scenario using the shared `TrackerScenario.waitForSelectedTickets` path (`packages/e2e/helpers/tracker-scenario.ts:331`) could select an already-closed ticket and mask a real regression.",
            "evidence": "Ran a Bun script against `FakeLinearTracker`: `createTicket` -> `closeTicket` -> `findTickets` with a title filter printed status `Done` and returned the closed `ENG-1` ticket.",
            "suggestion": "Filter out completed/done issues in `findTickets` (or add the Linear state predicate to the compiled matcher) so the fixture matches the contract, and add a small test that closes a ticket and asserts it is no longer returned."
          },
          {
            "priority": "low",
            "confidence": "high",
            "category": "testing",
            "file": "packages/e2e/tests/local/tracker-linear-fake.test.ts",
            "line": "57-63, 128-150",
            "issue": "`statusesFor` sets `prStatus` to `tracker.statuses.inProgress`, the same state as `inProgressStatus`. The run only proves the Todo -> In Progress transition; it cannot observe the post-PR actioned transition in `packages/code/src/lib/task/actioned-transition.ts:80-84`, so a Linear-only regression there would go unnoticed.",
            "evidence": "I copied the test to `/tmp/opencode` and changed `prStatus` to `tracker.statuses.pr` (`In Review`); both scenarios failed with `Expected: In Review`, `Received: In Progress`, proving `applyActionedTransition` is not exercised. The unmodified suite passes.",
            "suggestion": "Add a focused test that drives a distinct `prStatus` (for example a direct actioned-transition test with a Linear fake, or a fake code host that returns a PR), or explicitly document this gap in `coverage-matrix.md` and `tracker-e2e-plan.md`."
          },
          {
            "priority": "low",
            "confidence": "high",
            "category": "bug",
            "file": "packages/e2e/providers/fake-linear.ts",
            "line": "418-429",
            "issue": "The `labelIds` branch accepts unknown IDs, fabricates a label whose name is the raw ID, and returns success. Real Linear would reject an unknown label ID (or a malformed element), so a future label-update test could pass against an invalid client payload; a non-string element becomes an empty-id label instead of an error.",
            "evidence": "Ran a Bun script: `IssueUpdate` with `labelIds: [label-keep, does-not-exist]` returned success and the issue labels became `[keep, does-not-exist]` with `unexpectedOperations` empty.",
            "suggestion": "Maintain a fake label catalog (and implement `TeamLabels`/`getLabels`), or reject IDs not present in the catalog with an unexpected operation; also reject non-string array elements."
          },
          {
            "priority": "low",
            "confidence": "high",
            "category": "documentation",
            "file": "packages/e2e/providers/fake-linear.ts",
            "line": "3-9, 265-301",
            "issue": "The header claims a client regression in the wrong field fails, but `handle` only inspects the operation name and variables and never the selection set; `issuePayload` always returns a fixed superset. A query selecting a bogus field therefore succeeds, so the fake does not catch that class of regression.",
            "evidence": "Sent a query selecting `bogusField` on `IssueById`; the fake returned HTTP 200 with a full `data` payload and no errors, and `unexpectedOperations` stayed empty.",
            "suggestion": "Either narrow the comment to operation/variable mismatches or validate the requested selection set (at least required field names) before returning the payload; add a test for an unknown field."
          },
          {
            "priority": "low",
            "confidence": "medium",
            "category": "documentation",
            "file": "packages/e2e/helpers/opt-in-suites.ts",
            "line": "97-101",
            "issue": "The planned Trello fake reason still says only `the offline Jira fake runs in the default suite`; this PR adds the offline Linear fake to the same default suite, so the test output is stale and understates coverage.",
            "evidence": "`packages/e2e/helpers/opt-in-suites.ts:100` still names only Jira; `README.md:342-354` and `tracker-e2e-plan.md:228+` now document both fakes.",
            "suggestion": "Update the reason to name both the Jira and Linear default fakes, or make it generic."
          },
          {
            "priority": "info",
            "confidence": "medium",
            "category": "testing",
            "file": "packages/e2e/tests/local/tracker-linear-fake.test.ts",
            "line": "129-137",
            "issue": "`assertTicketState` checks seed labels, but Linear status transitions use `stateId` only (`packages/code/src/lib/trackers/linear/linear-task-tracker-client.ts:105-121`), so no production code path mutates labels. The `surviving unrelated labels` claim in `README.md:362` therefore only proves the fake retains its own initial labels, not that the client preserves them.",
            "evidence": "Pointed: `transitionStatus` calls `getWorkflowStates`/`updateIssueState` only; `assertTicketState` at `packages/e2e/helpers/tracker-lifecycle.ts:137` asserts labels from the same fake object.",
            "suggestion": "Either drop the label-preservation wording for Linear or add a deliberate label-changing assertion (for example a test that a state transition leaves labels untouched through the fake's `IssueUpdate` path)."
          }
        ],
        "approved": true
      },
      "addressed": []
    }
  ]
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

devintern-authored Pull request written by DevIntern working its own backlog

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant