Skip to content

PAN-1866#1975

Merged
eltmon merged 60 commits into
mainfrom
feature/pan-1866
Jun 20, 2026
Merged

PAN-1866#1975
eltmon merged 60 commits into
mainfrom
feature/pan-1866

Conversation

@eltmon

@eltmon eltmon commented Jun 19, 2026

Copy link
Copy Markdown
Owner

Issue: #1866

Acceptance Criteria

  • Define sequence.md JSON schema + TypeScript types
  • Implement parseSequenceMd (extract + validate fenced JSON block)
  • Implement writeSequenceMd (render table + tiered rationale + JSON, auto-commit)
  • collectOpenBacklog — manifest + batched body accessor + flag joins
  • Register the 'sequencer' role in config (Role union + defaults)
  • Author roles/sequencer.md + buildSequencerPrompt(pass, input)
  • spawnSequencerAgent(pass) — tmux role run mirroring the Flywheel
  • Surface the Sequencer role in Settings → Roles
  • backlog_sequence SQLite cache table + rebuild-from-md
  • GET /api/backlog/sequence — joined view + route registration
  • POST /api/backlog/sequence/regenerate — trigger a pass
  • POST operator pickup-gate + planning-policy — persist to sequence.md
  • Wire the new Backlog top-level tab + page shell
  • BacklogDAG + IssueNode (ReactFlow + dagre, reused stack)
  • Tiered rendering + filters for a 500+ issue backlog
  • Condition indicators (REFINE / STALE) + candidates-to-close summary
  • Operator pickup-gate + planning-policy controls on the node panel
  • Flywheel executes the sequence order under auto_pickup_backlog
  • (Follow-up) debounced delta auto-trigger + periodic review cadence

Implementation Tasks

  • Operator pickup-gate + planning-policy controls on the node panel
  • Condition indicators (REFINE / STALE) + candidates-to-close summary
  • Tiered rendering + filters for a 500+ issue backlog
  • BacklogDAG + IssueNode (ReactFlow + dagre, reused stack)
  • (Follow-up) debounced delta auto-trigger + periodic review cadence
  • POST /api/backlog/sequence/regenerate — trigger a pass
  • Wire the new Backlog top-level tab + page shell
  • spawnSequencerAgent(pass) — tmux role run mirroring the Flywheel
  • GET /api/backlog/sequence — joined view + route registration
  • Author roles/sequencer.md + buildSequencerPrompt(pass, input)
  • POST operator pickup-gate + planning-policy — persist to sequence.md
  • Flywheel executes the sequence order under auto_pickup_backlog
  • backlog_sequence SQLite cache table + rebuild-from-md
  • Surface the Sequencer role in Settings → Roles
  • collectOpenBacklog — manifest + batched body accessor + flag joins
  • Implement writeSequenceMd (render table + tiered rationale + JSON, auto-commit)
  • Implement parseSequenceMd (extract + validate fenced JSON block)
  • Register the 'sequencer' role in config (Role union + defaults)
  • Define sequence.md JSON schema + TypeScript types

Summary by CodeRabbit

  • New Features

    • Added a "Backlog" page to view and manage the full backlog as a ranked dependency graph visualization.
    • Introduced the ability to automatically rank and sequence open issues based on impact and dependencies.
    • Added controls to set issue gates (ready/blocked) and planning modes (interactive/skip/auto) directly from the backlog UI.
    • Enabled on-demand backlog regeneration with multiple pass modes (creation, incremental, review).
  • Backend

    • Added backlog sequencing API endpoints for fetching sequences, updating gates/planning states, and triggering regeneration.

@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Implements PAN-1866 "Backlog Sequencer": a new sequencer tmux-hosted role that generates .pan/backlog/sequence.md via creation/incremental/review passes. Adds typed SequenceDoc I/O, a disposable SQLite cache (backlog_sequence, schema v57), four backend HTTP routes, flywheel integration, and a ReactFlow DAG dashboard page.

Changes

Backlog Sequencer (PAN-1866)

Layer / File(s) Summary
Planning and tracking artifacts
.pan/continue.json, .pan/drafts/PAN-1866.md, .pan/records/pan-1866.json, .pan/spec.vbrief.json
Adds the full vBRIEF spec (1108 lines), continuation record with D1–D9 decisions and H1–H8 hazards, PRD Q&A section from non-interactive planning run, and a closed-out run record with pipeline status, closeOut accounting, and statusOverrides.
Sequencer role contracts and global wiring
packages/contracts/src/types.ts, roles/sequencer.md, src/lib/agents.ts, src/lib/overdeck/agents.ts, src/lib/config-yaml.ts, src/lib/router-config.ts, src/lib/settings-api.ts, src/dashboard/server/read-model.ts, src/dashboard/frontend/src/types.ts, src/dashboard/frontend/src/components/primitives/AgentCard.tsx, src/dashboard/frontend/src/components/Settings/RolesPanel.tsx, src/dashboard/frontend/src/components/Agents/FleetAgentsView.tsx, sync-sources/hooks/record-cost-event.js
Extends every role union/allowlist to include sequencer; adds default workhorse:expensive model mapping; registers the role in router config, settings API, read-model, frontend type unions, AgentCard accent, fleet view sort/phase, and Settings → Roles panel; includes test default assertion updates.
Sequence data model, markdown I/O, and backlog input
src/lib/backlog/types.ts, src/lib/backlog/sequence-io.ts, src/lib/backlog/backlog-input.ts, src/lib/backlog/__tests__/types.test.ts, src/lib/backlog/__tests__/sequence-io.test.ts, src/lib/backlog/__tests__/write-sequence.test.ts, src/lib/backlog/__tests__/backlog-input.test.ts
Defines SequenceDoc/SequenceNode/SequenceEdge with enum and range validation via parseSequenceJson; implements parseSequenceMd/writeSequenceMd with machine-readable fenced JSON, operator-field merge preservation, and in-pipeline pinning; implements collectOpenBacklog with manifest derivation, batched body accessor, and prior-sequence loading; comprehensive unit test coverage.
Issue parked label application
src/lib/backlog/label-ops.ts, src/lib/backlog/__tests__/label-ops.test.ts
Exports applyIssueParkedLabel which runs gh issue edit --add-label needs-design, returning silently for non-GitHub targets and suppressing stderr/failures; tests cover command composition and no-op path.
Sequencer agent spawn and prompt builder
src/lib/backlog/sequencer-agent.ts, src/lib/backlog/__tests__/sequencer-agent.test.ts, src/lib/backlog/__tests__/spawn-sequencer.test.ts
Exports spawnSequencerAgent (resolves pass from sequence.md presence, collects backlog, selects model, calls spawnRun) and buildSequencerPrompt (per-pass instructions covering batching, operator-field preservation, and in-pipeline pinning); tests validate pass selection, prompt content requirements, and spawn parameter wiring.
Auto-trigger, sequence pick, and flywheel integration
src/lib/backlog/backlog-auto-trigger.ts, src/lib/flywheel-merge-order.ts, src/lib/cloister/flywheel.ts, src/lib/backlog/__tests__/backlog-auto-trigger.test.ts, src/lib/__tests__/flywheel-sequence-auth.test.ts, src/dashboard/server/read-model.ts
Adds debounced incremental and periodic review triggers; pickFromSequence applies blocked/pipeline/parked-label/auth/readiness eligibility filters; flywheel prompt enrichment emits MUST-start-next or interactive-gate directives from parsed sequence; read-model fires debounced trigger on issues-changed.
Database schema and backlog sequence persistence
src/lib/database/schema.ts, src/lib/database/backlog-sequence-db.ts, src/lib/overdeck/backlog.ts, src/lib/database/__tests__/backlog-sequence-schema.test.ts, src/lib/database/__tests__/pending-auto-merges-schema.test.ts
Bumps SCHEMA_VERSION to 57; creates backlog_sequence table with (project_key, issue_id) uniqueness and (project_key, rank) index in initSchema and v56→v57 migration; exports upsert/clear/rebuild/read helpers; schema column and migration-compatibility tests included.
Backlog HTTP routes and server wiring
src/dashboard/server/routes/backlog.ts, src/dashboard/server/server.ts, src/cli/index.ts, tests/unit/lib/overdeck/no-loss-matrix.ts
Adds backlogRouteLayer (GET sequence enriching inPipeline/hasPrd/ready; POST regenerate spawning sequencer; POST gate with parked-label side effect; POST planning); registers in server; adds pan backlog write-sequence CLI command and --seed-from-legacy flag; registers four routes in no-loss matrix.
ReactFlow DAG component and side-panel controls
src/dashboard/frontend/src/components/backlog/BacklogDAG.tsx
Implements BacklogDAG with Dagre TB layout, styled IssueNode cards with importance colors and condition/gate/size chips, live indicator for in-pipeline nodes, RationaleSidePanel with gate/planning POST controls and busy state, query cache invalidation, and BacklogDAGViewer with React Query fetching.
Backlog page, filters, actions, and navigation wiring
src/dashboard/frontend/src/pages/BacklogSequencerPage.tsx, src/dashboard/frontend/src/App.tsx, src/dashboard/frontend/src/components/Header.tsx, src/dashboard/frontend/src/components/Sidebar.tsx
Adds BacklogSequencerPage with list/DAG views, importance/condition/pipeline/ready filters, DAG node budget with top-slice and edge-neighbor expansion, stale-close/PRD-draft/run-pass actions, and collapsed-count banner; wires the backlog tab into App.tsx routing, Header Tab union, and Sidebar Operations group.
Test environment helpers, FTS timestamp fix, author tracking, and misc fixes
src/lib/tmux.ts, src/lib/memory/fts-operations.ts, src/lib/overdeck/memory.ts, src/lib/tracker/interface.ts, src/lib/tracker/github.ts, src/dashboard/server/services/issue-data-service.ts, tests/cli/commands/work/done.test.ts, tests/integration/post-review-rebase-scenario.test.ts, tests/lib/cloister/*, tests/unit/lib/pan-dir/verdict-restore.test.ts, tests/fixtures/synced-skills.txt
Adds tmux warning-state reset helper; changes reset_markers FTS columns to INTEGER and converts timestamps to epoch ms; adds optional author field to Issue interface; updates review-agent and reactive-scheduler test mocking; refactors done.test.ts to use fake timers and stubbed fetch across suites; fixes post-review rebase integration test with in-memory journal; adds deacon no-resume mock; updates verdict-restore assertion.

Sequence Diagram(s)

sequenceDiagram
  rect rgba(173, 216, 230, 0.5)
    Note over Operator,BacklogDAG: Trigger and View Sequence
  end
  participant Operator
  participant BacklogSequencerPage
  participant backlogRouteLayer
  participant spawnSequencerAgent
  participant buildSequencerPrompt
  participant SequencerAgent as Sequencer tmux agent
  participant writeSequenceMd
  participant getBacklogSequenceForRoot
  BacklogSequencerPage->>backlogRouteLayer: POST /api/backlog/sequence/regenerate {pass}
  backlogRouteLayer->>spawnSequencerAgent: pass, issues
  spawnSequencerAgent->>buildSequencerPrompt: pass, manifest, batchSize
  buildSequencerPrompt-->>spawnSequencerAgent: prompt string
  spawnSequencerAgent->>SequencerAgent: spawnRun (allowHost, registerConversation)
  SequencerAgent->>writeSequenceMd: pan backlog write-sequence /tmp/result.json
  writeSequenceMd-->>SequencerAgent: .pan/backlog/sequence.md written
  backlogRouteLayer-->>BacklogSequencerPage: {status: spawned, agentId, pass}
  BacklogSequencerPage->>backlogRouteLayer: GET /api/backlog/sequence
  backlogRouteLayer->>getBacklogSequenceForRoot: projectRoot
  getBacklogSequenceForRoot-->>backlogRouteLayer: nodes + edges
  backlogRouteLayer-->>BacklogSequencerPage: enriched nodes (inPipeline, hasPrd, ready)
  BacklogSequencerPage->>Operator: renders list/DAG with ReactFlow
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related issues

Poem

🐇 Hop hop, the backlog grows tall,
A sequencer ranks them, one and all!
With tmux and dagre the nodes align,
Operator gates keep the pipeline fine.
ReactFlow renders each rank in view —
The bunnies know just what to do!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The PR title 'PAN-1866' is overly vague and does not clearly convey the main change. It only references an issue ID without summarizing the actual implementation work. Expand the title to briefly describe the primary change, such as 'Implement backlog sequencer with ReactFlow DAG visualization' or 'Add AI-ranked whole-backlog sequencing system'.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/pan-1866

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed: dependency version conflict. Check your lock file or package.json.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@mintlify

mintlify Bot commented Jun 19, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
Panopticon 🟢 Ready View Preview Jun 19, 2026, 8:18 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@eltmon

eltmon commented Jun 20, 2026

Copy link
Copy Markdown
Owner Author

Review CHANGES REQUESTED for PAN-1866

BLOCKED: sequencer agent prompt references non-existent 'pan backlog bodies'/'pan backlog write-sequence' (no CLI/route), Backlog page has no Regenerate control, and Close/Draft-PRD buttons call wrong endpoints and fail silently; FR-14 emergency-promote unimplemented, FR-9 cache is dead code.

Required action

Fix every blocking review finding, commit the fixes, then re-request review with:

pan review request PAN-1866 -m "Fixed review issues"

@eltmon

eltmon commented Jun 20, 2026

Copy link
Copy Markdown
Owner Author

Review CHANGES REQUESTED for PAN-1866

Sequencer spawns with empty backlog and missing body/write CLI

Required action

Fix every blocking review finding, commit the fixes, then re-request review with:

pan review request PAN-1866 -m "Fixed review issues"

@eltmon

eltmon commented Jun 20, 2026

Copy link
Copy Markdown
Owner Author

Review CHANGES REQUESTED for PAN-1866

Sequencer spawns with an empty backlog and references missing backlog CLI commands

Required action

Fix every blocking review finding, commit the fixes, then re-request review with:

pan review request PAN-1866 -m "Fixed review issues"

@eltmon

eltmon commented Jun 20, 2026

Copy link
Copy Markdown
Owner Author

Review CHANGES REQUESTED for PAN-1866

Regenerate spawns the sequencer with an empty backlog, so it cannot produce the required sequence

Required action

Fix every blocking review finding, commit the fixes, then re-request review with:

pan review request PAN-1866 -m "Fixed review issues"

@eltmon

eltmon commented Jun 20, 2026

Copy link
Copy Markdown
Owner Author

Review CHANGES REQUESTED for PAN-1866

Regenerate spawns sequencer with an empty backlog manifest

Required action

Fix every blocking review finding, commit the fixes, then re-request review with:

pan review request PAN-1866 -m "Fixed review issues"

@eltmon

eltmon commented Jun 20, 2026

Copy link
Copy Markdown
Owner Author

Review CHANGES REQUESTED for PAN-1866

Regenerate spawns sequencer with empty backlog

Required action

Fix every blocking review finding, commit the fixes, then re-request review with:

pan review request PAN-1866 -m "Fixed review issues"

@eltmon

eltmon commented Jun 20, 2026

Copy link
Copy Markdown
Owner Author

Review CHANGES REQUESTED for PAN-1866

sequence pickup bypasses Flywheel author/assignee safety gate

Required action

Fix every blocking review finding, commit the fixes, then re-request review with:

pan review request PAN-1866 -m "Fixed review issues"

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 19

🧹 Nitpick comments (6)
src/lib/database/__tests__/backlog-sequence-schema.test.ts (1)

31-37: ⚡ Quick win

Add assertions for unique/index contracts, not just table existence.

Current checks validate columns, but they don’t verify (project_key, issue_id) uniqueness and (project_key, rank) index presence.

Also applies to: 52-57

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/database/__tests__/backlog-sequence-schema.test.ts` around lines 31 -
37, The test for backlog_sequence schema only validates column existence but
does not verify the uniqueness constraint on (project_key, issue_id) and the
index on (project_key, rank). Add additional assertions after the column checks
using PRAGMA index_list and PRAGMA index_info to verify that the required unique
constraint and index actually exist on the backlog_sequence table with the
correct column combinations.
src/lib/cloister/flywheel.ts (1)

85-92: ⚡ Quick win

Build an issue→labels map once before selection.

Lines 85-92 do a linear find for each candidate. For large sequences this is avoidable overhead.

Suggested refactor
-          const issueLabelsLookup = (issueId: string): string[] => {
+          const issueLabelsLookup = (() => {
             try {
               // eslint-disable-next-line `@typescript-eslint/no-require-imports`
               const { getSharedIssueService } = require('../../dashboard/server/services/issue-service-singleton.js') as
                 typeof import('../../dashboard/server/services/issue-service-singleton.js');
-              const issue = (getSharedIssueService().getIssues() as Array<{ ref: string; labels?: string[] }>)
-                .find((i) => i.ref === issueId);
-              return issue?.labels ?? [];
-            } catch { return []; }
-          };
+              const issues = getSharedIssueService().getIssues() as Array<{ ref: string; labels?: string[] }>;
+              const byRef = new Map(issues.map((i) => [i.ref, i.labels ?? []]));
+              return (issueId: string): string[] => byRef.get(issueId) ?? [];
+            } catch {
+              return (_issueId: string): string[] => [];
+            }
+          })();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/cloister/flywheel.ts` around lines 85 - 92, The issueLabelsLookup
function performs a linear search through all issues for each lookup call, which
is inefficient for large datasets. Refactor by creating a Map that maps issue
refs to their labels once before the selection process begins, then replace the
find-based lookup inside issueLabelsLookup with a simple Map.get() call using
the issueId as the key. This changes the lookup from O(n) to O(1) complexity.
The Map should be constructed from getSharedIssueService().getIssues() by
iterating through the array once and storing each issue's ref and labels pair
before issueLabelsLookup is defined or called.
src/dashboard/frontend/src/types.ts (1)

111-115: ⚡ Quick win

Keep the role comment in sync with the union.

The doc block still lists the pre-sequencer role set, so it no longer matches the accepted values here.

✏️ Suggested tweak
-   * 'plan' | 'work' | 'review' | 'test' | 'ship' | 'flywheel'.
+   * 'plan' | 'work' | 'review' | 'test' | 'ship' | 'flywheel' | 'strike' | 'sequencer'.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/dashboard/frontend/src/types.ts` around lines 111 - 115, Update the JSDoc
comment for the role property to include all current role values. The comment
currently lists only 'plan' | 'work' | 'review' | 'test' | 'ship' | 'flywheel'
but the actual type union for the role field now includes 'strike' and
'sequencer'. Modify the comment to reflect the complete set of accepted role
values: 'plan' | 'work' | 'review' | 'test' | 'ship' | 'flywheel' | 'strike' |
'sequencer'.
src/lib/backlog/__tests__/spawn-sequencer.test.ts (1)

27-88: ⚡ Quick win

Add a regression test for omitted issues input.

Given the sequencer’s dependency on real backlog input, add a test for spawnSequencerAgent(..., { projectRoot }) that asserts the expected safeguard behavior (throw/fetch), so empty-manifest regressions are caught.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/backlog/__tests__/spawn-sequencer.test.ts` around lines 27 - 88, Add
a new test case to verify the behavior when the issues parameter is omitted from
the spawnSequencerAgent call. Create a test that invokes spawnSequencerAgent
with only the projectRoot property and no issues property in the options object,
then assert the expected safeguard behavior such as throwing an error or
fetching backlog from the file system via collectOpenBacklog to prevent silent
failures with empty manifests.
src/lib/backlog/__tests__/backlog-input.test.ts (1)

44-67: ⚡ Quick win

Add a regression test for malformed createdAt handling.

Please add a case where createdAt is invalid and assert ageMs remains a number (fallback path), so ranking inputs stay stable.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/backlog/__tests__/backlog-input.test.ts` around lines 44 - 67, Add a
new test case following the existing pattern in the test file that verifies the
behavior when createdAt is malformed or invalid. Create an issue using makeIssue
with an invalid createdAt value, call collectOpenBacklog, and assert that the
resulting manifest entry has ageMs as a valid number, confirming that the
fallback logic properly handles the edge case and maintains stable ranking
inputs.
src/lib/backlog/__tests__/sequencer-agent.test.ts (1)

33-103: ⚡ Quick win

Add a negative assertion for unsupported sequence-write command text.

Please add a test assertion that generated prompts do not include deprecated/unsupported command hints (e.g., pan backlog write-sequence) to prevent regression.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/backlog/__tests__/sequencer-agent.test.ts` around lines 33 - 103, The
test suite checks that buildSequencerPrompt includes specific instructions for
various passes, but it lacks negative assertions to verify that
deprecated/unsupported commands are excluded from the prompts. Add a test
assertion (or assertions) using expect(prompt).not.toContain() to verify that
unsupported sequence-write command text like "pan backlog write-sequence" is not
present in the generated prompts. This can be added as a new test case or
integrated into the existing loop-based tests (such as the "all passes
contain..." tests) to ensure the deprecated command hints are never included
across the creation, incremental, and review passes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.pan/records/pan-1866.json:
- Around line 106-115: The verificationStatus field is set to "passed" but the
verificationNotes field contains detailed TypeScript compilation errors from the
frontend-typecheck verification, indicating the verification actually failed.
Change the verificationStatus value from "passed" to "failed" to accurately
reflect the failure state recorded in verificationNotes. This ensures the state
file provides accurate signals to downstream automation.

In `@roles/sequencer.md`:
- Around line 115-121: The documentation incorrectly references a non-existent
CLI command `pan backlog write-sequence` alongside the `writeSequenceMd`
function. Remove the reference to the CLI command from the instruction text in
the roles/sequencer.md file where it mentions the writeSequenceMd function.
Replace the text that reads "(or its CLI equivalent `pan backlog
write-sequence`)" with nothing, and change the following sentence starter from
"The function handles:" to "It handles:" to maintain proper grammar after the
removal.

In `@src/dashboard/frontend/src/components/backlog/BacklogDAG.tsx`:
- Around line 108-110: The clickable div element in BacklogDAG.tsx that triggers
onSelect(node) lacks keyboard accessibility for users who cannot use a mouse.
Replace this div with a semantic button element or add keyboard event handlers
(such as onKeyDown to handle Enter and Space keys) and appropriate ARIA
attributes (role="button", tabIndex="0") to the existing div. Ensure that
keyboard users can navigate to and activate the node selection interaction.
- Around line 411-427: Both handleGateChange and handlePlanningChange functions
use fetch() without checking the HTTP response status, allowing 4xx/5xx errors
to silently succeed. In both functions, after the fetch call completes, check
that response.ok is true and throw an error if it is false, or alternatively
check the response status and handle non-success responses appropriately before
calling queryClient.invalidateQueries. This ensures that API failures are
properly caught and handled rather than silently proceeding with stale data.

In `@src/dashboard/frontend/src/pages/BacklogSequencerPage.tsx`:
- Line 360: The onClick handler has a state update race condition where
setSpawnPass('creation') schedules a state update but handleRunPass() executes
synchronously and reads the stale spawnPass value from closure instead of the
newly set value. Refactor the handleRunPass function to accept an optional pass
mode parameter instead of reading from the spawnPass state variable. Then update
the onClick handler in this button to pass 'creation' directly as an argument to
handleRunPass, removing the separate setSpawnPass call entirely. This ensures
the correct pass mode is used immediately without relying on asynchronous state
updates.

In `@src/dashboard/server/read-model.ts`:
- Around line 721-725: The empty catch block in the promise chain for the
dynamic import of backlog-auto-trigger.js is silently swallowing all errors from
both the import failure and the triggerDebouncedIncrementalPass function call,
making debugging impossible. Replace the empty catch handler with an error
logging statement that captures and logs the error details to provide visibility
into failures. This ensures that any issues with the import or the incremental
pass triggering are properly reported instead of being hidden.

In `@src/dashboard/server/routes/backlog.ts`:
- Around line 45-47: The parseSequenceMd function in the backlog route currently
returns a 200 status with empty nodes and edges when parsing fails, which masks
actual format or data errors. Instead of checking if parsed.ok is true and
silently returning empty data on failure, handle the failure case by returning
an appropriate error response (such as a 400 Bad Request status) that includes
error details from the parsed result. This should be applied consistently to
both occurrences of this pattern at lines 45-47 and lines 92-96.
- Around line 147-157: The sequence.md file operations suffer from a
read-modify-write race condition where concurrent requests can lose updates. The
current pattern reads the file with readFileSync, parses it with
parseSequenceMd, modifies the node, and writes back with writeSequenceMd without
any synchronization. To fix this, implement a file-locking mechanism (using a
library like proper-lockfile or similar) that acquires an exclusive lock before
the readFileSync call and releases it after the writeSequenceMd call completes,
ensuring that concurrent requests are serialized and changes are not lost. Apply
this same locking pattern to both the gate update handler (around readFileSync
and writeSequenceMd) and the other affected handler mentioned at lines 192-201.

In `@src/lib/backlog/backlog-input.ts`:
- Around line 69-77: The computation of createdMs on line 69 can produce NaN
when issue.createdAt contains a malformed timestamp, which then causes ageMs to
be non-numeric. After computing createdMs from new
Date(issue.createdAt).getTime(), add a check to verify that createdMs is a
finite number using Number.isFinite(). If createdMs is not finite, set createdMs
to the now value as a fallback. This ensures the ageMs calculation always
results in a valid numeric value in the returned object.

In `@src/lib/backlog/sequence-io.ts`:
- Around line 52-57: The machineBlock serializes the unsorted doc object, but
sortedNodes is used for rendering the table. To ensure consistency between the
UI and JSON consumers, modify the machineBlock construction to serialize a
version of doc where the nodes property contains sortedNodes instead of the
original unsorted nodes. Create an object that spreads the doc properties but
overrides the nodes field with sortedNodes before passing it to JSON.stringify.

In `@src/lib/backlog/sequencer-agent.ts`:
- Around line 69-70: The instruction text in the sequencer-agent.ts file
contains inconsistent guidance about writing results. In the section around
lines 69-70, the instruction mentions both writeSequenceMd and the pan backlog
write-sequence command, but lines 120-121 specify that only the Write tool is
supported. Remove the mention of pan backlog write-sequence from the earlier
instruction block and keep only a reference to the canonical Write tool approach
to ensure users are directed to a single supported method for writing output.
- Around line 33-35: The code on line 33 defaults `opts.issues` to an empty
array when not provided, which allows the sequencer to proceed with no issues to
sequence, resulting in invalid output. Remove the default empty array fallback
and either implement validation to require issues be provided for the sequencer
to work, or implement logic to fetch the backlog issues before passing them to
the collectOpenBacklog function call. Ensure that the sequencer cannot proceed
without actual issues to sequence.

In `@src/lib/backlog/types.ts`:
- Around line 67-68: The validation for the dependsOn field at line 67 only
checks if it is an array but does not validate the type of elements within that
array. Add an additional validation check to ensure that each element in the
dependsOn array is of type string, preventing invalid dependency IDs from
passing validation and being coerced to string[] at line 82. The validation
should iterate through the array elements and verify they are all strings before
accepting the node as valid.
- Around line 114-116: The code casts array items to Record<string, unknown>
without first verifying they are valid objects, which can cause parser crashes
if entries are null or non-objects. In the validateNode call for nodes iteration
and the similar call for edges iteration (also around line 122), add a guard
check before the typecast to ensure the item is actually an object. If the item
is null or not an object, return an error result with ok: false instead of
attempting to cast and process it. This prevents unsafe type assumptions from
causing runtime errors.

In `@src/lib/cloister/__tests__/reactive-scheduler.test.ts`:
- Around line 231-235: The afterEach cleanup block in the
reactive-scheduler.test.ts file only restores the OVERDECK_NO_RESUME environment
variable when savedNoResume is not undefined, which allows test-set values to
leak into subsequent tests. Modify the afterEach hook to handle both cases: when
savedNoResume is defined, restore it to process.env.OVERDECK_NO_RESUME, and when
savedNoResume is undefined (meaning the variable was not originally set), delete
the OVERDECK_NO_RESUME property from process.env to fully restore the original
state.

In `@src/lib/database/backlog-sequence-db.ts`:
- Line 94: The JSON.parse call for the depends_on field in the
getBacklogSequence() function can throw an exception if the stored JSON is
malformed, causing the entire function to fail. Wrap the JSON.parse statement in
a try-catch block to guard against parsing errors, and provide a fallback value
such as an empty array when parsing fails, allowing the function to continue
reading other records gracefully instead of hard-failing on malformed data.

In `@src/lib/flywheel-merge-order.ts`:
- Around line 275-276: The parked-label filtering logic is performing a
case-sensitive comparison against the PARKED_LABELS set, which causes labels
with different casing (like "Needs-Design" versus "needs-design") to not be
properly detected and filtered. In the block where labels are filtered using
labels.some((l) => PARKED_LABELS.has(l)), normalize each label to lowercase
before checking it against PARKED_LABELS to ensure case-insensitive matching
works correctly.

In `@src/lib/tmux.ts`:
- Around line 248-251: The function _resetWarnedManagedServerDirtyForTest()
references an undefined variable warnedManagedServerDirty on line 250, causing a
typecheck failure. Change the assignment target from warnedManagedServerDirty to
the correct existing guard variable warnedManagedServerDirtyCmdline to properly
reset the state in tests.

In `@tests/unit/lib/overdeck/no-loss-matrix.ts`:
- Line 633: The matrix entry for 'GET /api/backlog/sequence' contains a stale
description in the door field that describes a cache-based read path with
parseSequenceMd fallback, but the actual route implementation reads and parses
sequence.md directly. Update the door description for this matrix entry to
accurately reflect the current implementation behavior instead of the outdated
cache path, or alternatively implement the cache-based approach that the current
note describes.

---

Nitpick comments:
In `@src/dashboard/frontend/src/types.ts`:
- Around line 111-115: Update the JSDoc comment for the role property to include
all current role values. The comment currently lists only 'plan' | 'work' |
'review' | 'test' | 'ship' | 'flywheel' but the actual type union for the role
field now includes 'strike' and 'sequencer'. Modify the comment to reflect the
complete set of accepted role values: 'plan' | 'work' | 'review' | 'test' |
'ship' | 'flywheel' | 'strike' | 'sequencer'.

In `@src/lib/backlog/__tests__/backlog-input.test.ts`:
- Around line 44-67: Add a new test case following the existing pattern in the
test file that verifies the behavior when createdAt is malformed or invalid.
Create an issue using makeIssue with an invalid createdAt value, call
collectOpenBacklog, and assert that the resulting manifest entry has ageMs as a
valid number, confirming that the fallback logic properly handles the edge case
and maintains stable ranking inputs.

In `@src/lib/backlog/__tests__/sequencer-agent.test.ts`:
- Around line 33-103: The test suite checks that buildSequencerPrompt includes
specific instructions for various passes, but it lacks negative assertions to
verify that deprecated/unsupported commands are excluded from the prompts. Add a
test assertion (or assertions) using expect(prompt).not.toContain() to verify
that unsupported sequence-write command text like "pan backlog write-sequence"
is not present in the generated prompts. This can be added as a new test case or
integrated into the existing loop-based tests (such as the "all passes
contain..." tests) to ensure the deprecated command hints are never included
across the creation, incremental, and review passes.

In `@src/lib/backlog/__tests__/spawn-sequencer.test.ts`:
- Around line 27-88: Add a new test case to verify the behavior when the issues
parameter is omitted from the spawnSequencerAgent call. Create a test that
invokes spawnSequencerAgent with only the projectRoot property and no issues
property in the options object, then assert the expected safeguard behavior such
as throwing an error or fetching backlog from the file system via
collectOpenBacklog to prevent silent failures with empty manifests.

In `@src/lib/cloister/flywheel.ts`:
- Around line 85-92: The issueLabelsLookup function performs a linear search
through all issues for each lookup call, which is inefficient for large
datasets. Refactor by creating a Map that maps issue refs to their labels once
before the selection process begins, then replace the find-based lookup inside
issueLabelsLookup with a simple Map.get() call using the issueId as the key.
This changes the lookup from O(n) to O(1) complexity. The Map should be
constructed from getSharedIssueService().getIssues() by iterating through the
array once and storing each issue's ref and labels pair before issueLabelsLookup
is defined or called.

In `@src/lib/database/__tests__/backlog-sequence-schema.test.ts`:
- Around line 31-37: The test for backlog_sequence schema only validates column
existence but does not verify the uniqueness constraint on (project_key,
issue_id) and the index on (project_key, rank). Add additional assertions after
the column checks using PRAGMA index_list and PRAGMA index_info to verify that
the required unique constraint and index actually exist on the backlog_sequence
table with the correct column combinations.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f065768b-431a-4ccb-a48e-308216d2cab8

📥 Commits

Reviewing files that changed from the base of the PR and between 7a4b7f4 and 4dadf0e.

📒 Files selected for processing (48)
  • .beads/issues.jsonl
  • .pan/continue.json
  • .pan/drafts/PAN-1866.md
  • .pan/records/pan-1866.json
  • .pan/spec.vbrief.json
  • packages/contracts/src/types.ts
  • roles/sequencer.md
  • src/cli/index.ts
  • src/dashboard/frontend/src/App.tsx
  • src/dashboard/frontend/src/components/Agents/FleetAgentsView.tsx
  • src/dashboard/frontend/src/components/Header.tsx
  • src/dashboard/frontend/src/components/Settings/RolesPanel.tsx
  • src/dashboard/frontend/src/components/Sidebar.tsx
  • src/dashboard/frontend/src/components/backlog/BacklogDAG.tsx
  • src/dashboard/frontend/src/components/primitives/AgentCard.tsx
  • src/dashboard/frontend/src/pages/BacklogSequencerPage.tsx
  • src/dashboard/frontend/src/types.ts
  • src/dashboard/server/read-model.ts
  • src/dashboard/server/routes/backlog.ts
  • src/dashboard/server/server.ts
  • src/lib/__tests__/config-yaml-roles.test.ts
  • src/lib/__tests__/settings-api.test.ts
  • src/lib/__tests__/tmux-server.test.ts
  • src/lib/agents.ts
  • src/lib/backlog/__tests__/backlog-input.test.ts
  • src/lib/backlog/__tests__/sequence-io.test.ts
  • src/lib/backlog/__tests__/sequencer-agent.test.ts
  • src/lib/backlog/__tests__/spawn-sequencer.test.ts
  • src/lib/backlog/__tests__/types.test.ts
  • src/lib/backlog/__tests__/write-sequence.test.ts
  • src/lib/backlog/backlog-auto-trigger.ts
  • src/lib/backlog/backlog-input.ts
  • src/lib/backlog/sequence-io.ts
  • src/lib/backlog/sequencer-agent.ts
  • src/lib/backlog/types.ts
  • src/lib/cloister/__tests__/reactive-scheduler.test.ts
  • src/lib/cloister/flywheel.ts
  • src/lib/config-yaml.ts
  • src/lib/database/__tests__/backlog-sequence-schema.test.ts
  • src/lib/database/__tests__/pending-auto-merges-schema.test.ts
  • src/lib/database/backlog-sequence-db.ts
  • src/lib/database/schema.ts
  • src/lib/flywheel-merge-order.ts
  • src/lib/overdeck/agents.ts
  • src/lib/router-config.ts
  • src/lib/settings-api.ts
  • src/lib/tmux.ts
  • tests/unit/lib/overdeck/no-loss-matrix.ts

Comment thread .pan/records/pan-1866.json Outdated
Comment thread roles/sequencer.md Outdated
Comment on lines +108 to +110
<div
onClick={() => onSelect(node)}
className={isInPipeline ? 'plan-glow' : undefined}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Issue node selection is not keyboard-accessible.

Using a clickable <div> without keyboard handlers/semantics makes node selection inaccessible for keyboard-only users.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/dashboard/frontend/src/components/backlog/BacklogDAG.tsx` around lines
108 - 110, The clickable div element in BacklogDAG.tsx that triggers
onSelect(node) lacks keyboard accessibility for users who cannot use a mouse.
Replace this div with a semantic button element or add keyboard event handlers
(such as onKeyDown to handle Enter and Space keys) and appropriate ARIA
attributes (role="button", tabIndex="0") to the existing div. Ensure that
keyboard users can navigate to and activate the node selection interaction.

Comment on lines +411 to +427
async function handleGateChange(issueId: string, gate: string) {
await fetch('/api/backlog/sequence/gate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ issueId, gate }),
});
queryClient.invalidateQueries({ queryKey: ['backlog-sequence'] });
}

async function handlePlanningChange(issueId: string, planning: string) {
await fetch('/api/backlog/sequence/planning', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ issueId, planning }),
});
queryClient.invalidateQueries({ queryKey: ['backlog-sequence'] });
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Mutation handlers ignore non-OK HTTP responses.

fetch() only throws on network errors; 4xx/5xx currently flow as success, so gate/planning updates can fail silently.

Suggested fix
   async function handleGateChange(issueId: string, gate: string) {
-    await fetch('/api/backlog/sequence/gate', {
+    const res = await fetch('/api/backlog/sequence/gate', {
       method: 'POST',
       headers: { 'Content-Type': 'application/json' },
       body: JSON.stringify({ issueId, gate }),
     });
+    if (!res.ok) throw new Error(`Gate update failed: HTTP ${res.status}`);
     queryClient.invalidateQueries({ queryKey: ['backlog-sequence'] });
   }

   async function handlePlanningChange(issueId: string, planning: string) {
-    await fetch('/api/backlog/sequence/planning', {
+    const res = await fetch('/api/backlog/sequence/planning', {
       method: 'POST',
       headers: { 'Content-Type': 'application/json' },
       body: JSON.stringify({ issueId, planning }),
     });
+    if (!res.ok) throw new Error(`Planning update failed: HTTP ${res.status}`);
     queryClient.invalidateQueries({ queryKey: ['backlog-sequence'] });
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/dashboard/frontend/src/components/backlog/BacklogDAG.tsx` around lines
411 - 427, Both handleGateChange and handlePlanningChange functions use fetch()
without checking the HTTP response status, allowing 4xx/5xx errors to silently
succeed. In both functions, after the fetch call completes, check that
response.ok is true and throw an error if it is false, or alternatively check
the response status and handle non-success responses appropriately before
calling queryClient.invalidateQueries. This ensures that API failures are
properly caught and handled rather than silently proceeding with stale data.

<p className="text-sm">No backlog sequence yet.</p>
<p className="text-xs">Run a creation pass to rank the open backlog.</p>
<button
onClick={() => { setSpawnPass('creation'); void handleRunPass(); }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

State update race: creation pass intent lost.

The inline setSpawnPass('creation') schedules a state update, but React doesn't apply it synchronously within the current closure. When handleRunPass() runs on the next line, it reads the stale spawnPass value (likely 'auto') from its captured closure scope—not the intended 'creation'.

Impact: The user clicks "Run creation pass" expecting a creation pass, but the backend receives a different pass mode (whatever was previously selected in the dropdown).

🛠️ Recommended fix: pass the mode as an argument

Refactor handleRunPass to accept an optional pass parameter:

-async function handleRunPass() {
+async function handleRunPass(passOverride?: typeof spawnPass) {
   if (spawning) return;
   setSpawning(true);
   setShowPassPicker(false);
   try {
     await fetch('/api/backlog/sequence/regenerate', {
       method: 'POST',
       headers: { 'Content-Type': 'application/json' },
-      body: JSON.stringify({ pass: spawnPass }),
+      body: JSON.stringify({ pass: passOverride ?? spawnPass }),
     });
     setTimeout(() => refetch(), 2000);
   } finally {
     setSpawning(false);
   }
 }

Then update the empty-state button:

-onClick={() => { setSpawnPass('creation'); void handleRunPass(); }}
+onClick={() => { setSpawnPass('creation'); void handleRunPass('creation'); }}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/dashboard/frontend/src/pages/BacklogSequencerPage.tsx` at line 360, The
onClick handler has a state update race condition where setSpawnPass('creation')
schedules a state update but handleRunPass() executes synchronously and reads
the stale spawnPass value from closure instead of the newly set value. Refactor
the handleRunPass function to accept an optional pass mode parameter instead of
reading from the spawnPass state variable. Then update the onClick handler in
this button to pass 'creation' directly as an argument to handleRunPass,
removing the separate setSpawnPass call entirely. This ensures the correct pass
mode is used immediately without relying on asynchronous state updates.

Comment thread src/lib/cloister/__tests__/reactive-scheduler.test.ts
importance: r['importance'] as string,
score: r['score'] as number,
condition: r['condition'] as string,
dependsOn: JSON.parse(r['depends_on'] as string) as string[],

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Guard depends_on parsing to avoid hard-failing reads.

Line 94 can throw on malformed JSON and fail getBacklogSequence() entirely.

Suggested fix
+function safeParseDependsOn(value: unknown): string[] {
+  if (typeof value !== 'string') return [];
+  try {
+    const parsed = JSON.parse(value) as unknown;
+    return Array.isArray(parsed) ? parsed.filter((v): v is string => typeof v === 'string') : [];
+  } catch {
+    return [];
+  }
+}
...
-    dependsOn: JSON.parse(r['depends_on'] as string) as string[],
+    dependsOn: safeParseDependsOn(r['depends_on']),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/database/backlog-sequence-db.ts` at line 94, The JSON.parse call for
the depends_on field in the getBacklogSequence() function can throw an exception
if the stored JSON is malformed, causing the entire function to fail. Wrap the
JSON.parse statement in a try-catch block to guard against parsing errors, and
provide a fallback value such as an empty array when parsing fails, allowing the
function to continue reading other records gracefully instead of hard-failing on
malformed data.

Comment on lines +275 to +276
const labels = opts?.issueLabels?.(node.issue) ?? [];
if (labels.some((l) => PARKED_LABELS.has(l))) continue;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Normalize label case before parked-label filtering.

At Line 276, parked-label detection is case-sensitive. Labels like Needs-Design won’t be filtered and may be incorrectly picked.

Suggested fix
-    const labels = opts?.issueLabels?.(node.issue) ?? [];
-    if (labels.some((l) => PARKED_LABELS.has(l))) continue;
+    const labels = opts?.issueLabels?.(node.issue) ?? [];
+    if (labels.some((l) => PARKED_LABELS.has(l.toLowerCase()))) continue;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const labels = opts?.issueLabels?.(node.issue) ?? [];
if (labels.some((l) => PARKED_LABELS.has(l))) continue;
const labels = opts?.issueLabels?.(node.issue) ?? [];
if (labels.some((l) => PARKED_LABELS.has(l.toLowerCase()))) continue;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/flywheel-merge-order.ts` around lines 275 - 276, The parked-label
filtering logic is performing a case-sensitive comparison against the
PARKED_LABELS set, which causes labels with different casing (like
"Needs-Design" versus "needs-design") to not be properly detected and filtered.
In the block where labels are filtered using labels.some((l) =>
PARKED_LABELS.has(l)), normalize each label to lowercase before checking it
against PARKED_LABELS to ensure case-insensitive matching works correctly.

Comment thread src/lib/tmux.ts
Comment thread tests/unit/lib/overdeck/no-loss-matrix.ts
@eltmon

eltmon commented Jun 20, 2026

Copy link
Copy Markdown
Owner Author

Review CHANGES REQUESTED for PAN-1866

sequence pickup still ignores ready-or-PRD eligibility

Required action

Fix every blocking review finding, commit the fixes, then re-request review with:

pan review request PAN-1866 -m "Fixed review issues"

@eltmon

eltmon commented Jun 20, 2026

Copy link
Copy Markdown
Owner Author

Review CHANGES REQUESTED for PAN-1866

Backlog mutation routes bypass dashboard mutation guard

Required action

Fix every blocking review finding, commit the fixes, then re-request review with:

pan review request PAN-1866 -m "Fixed review issues"

panopticon-agent[bot] and others added 24 commits June 20, 2026 17:47
…enceMd; add Issue.author

writeSequenceMd now merges against the prior sequence.md:
- FR-15: gate!='auto' in prior preserved (operator-set)
- FR-17: planning!='auto' in prior preserved (operator-set)
- FR-13: in-pipeline issues keep prior rank/why/rationale
- FR-16: prior operator edges kept; current doc operator edges unioned (dedup)

Issue.author added to tracker interface and populated from
ghIssue.user.login so flywheel isAuthorizedIssue correctly passes
unassigned operator-authored issues.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ad-only)

POST /api/backlog/sequence/gate was shelling out to `gh issue edit --add-label`
when gate=blocked, violating the explicit non-goal that GitHub metadata is read,
never written back. Gate state is already persisted in sequence.md — no tracker
side-effect needed. Remove the label-write block and its orphaned execAsync /
PARKED_LABEL / promisify / exec imports.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…surface

FR-17 fix: flywheel flywheelRunConfigurationSection now distinguishes
planning=interactive from skip/auto. Interactive picks emit a NEEDS OPERATOR
ACTION line (not MUST start next) and an instruction not to auto-start;
auto/skip continue to emit the MUST start next + mandatory pickup instruction.

FR-15 fix: add src/lib/backlog/label-ops.ts with exported applyIssueParkedLabel
that resolves the GitHub owner/repo/number via resolveGitHubIssueSync and calls
gh issue edit --add-label. The gate route imports and awaits it when gate=blocked.
3 unit tests in label-ops.test.ts verify the side effect fires for GitHub issues
and is a no-op for non-GitHub issues.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…torEdit flag

writeSequenceMd unconditionally loaded the prior sequence.md and carried
forward any non-auto gate/planning, making it impossible for the operator
to reset 'blocked' or 'interactive' back to 'auto' via the UI.

Add WriteSequenceMdOpts.operatorEdit: when true, skip the prior-file
preservation step entirely (the caller has already applied the intended value
to the doc). Both operator-facing routes pass { operatorEdit: true }.

AI resequence callers omit the flag so FR-13/FR-15/FR-16/FR-17
preservation continues to run.

Add 5 unit tests: blocked/ready/interactive/skip -> auto resets pass with
operatorEdit:true; AI resequence path still preserves non-auto fields.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ounds

Three review blockers addressed:

1. Add updatedAt to BacklogManifestEntry so the incremental pass can
   mechanically identify changed issues by comparing issue.updatedAt
   to priorSequence.generatedAt. The prompt now says explicitly:
   read bodies ONLY for issues whose updatedAt is LATER than generatedAt.

2. Wire GET /api/backlog/sequence through the cache via
   getBacklogSequenceForRoot(). This seeds the cache on each MD read and
   falls back to stale cache rows when sequence.md is absent/unparseable.
   Previously the cache table was dead on the dashboard read path.

3. Add bounds validation: score must be 0-100, confidence must be 0-1,
   dependsOn elements must be strings (not arbitrary array items).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ment

Two review blockers:

1. readFileSync and parseSequenceMd were removed from backlog.ts imports
   during the cache-read-path refactor but are still called by the gate and
   planning operator endpoints (lines 142/143 and 184/185). Both endpoints
   would throw ReferenceError at runtime. Restore the missing imports.

2. GET /api/backlog/sequence enriched inPipeline from review status only
   and ready from spec existence only. This diverges from the source-of-truth
   join in backlog-input.ts: inPipeline must also check for a live workspaces/
   feature-<id> directory, and ready requires both a spec and workspace beads
   at .beads/issues.jsonl. Mirror the backlog-input.ts logic exactly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The PRD defines version as a number (e.g. 1), but the validator checked
typeof v.version !== 'string' and the type declared version: string.
A sequencer agent following the PRD schema would produce version: 1 which
the validator would reject with "version missing", blocking the write-sequence
CLI path entirely (FR-1).

Change SequenceDoc.version to number, update the validator to check
typeof !== 'number', update all test fixtures from '1' to 1, and clarify
the prompt schema description. Add two explicit tests: one confirming numeric
version is accepted, one confirming string version is rejected.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Create src/lib/overdeck/backlog.ts door so backlog route imports
  via the overdeck boundary (fixes lint-overdeck-boundaries gate)
- Add missing POST /api/agents/:id/restart-fresh and
  POST /api/review/:issueId/purge entries to no-loss-matrix
- Mock no-resume-mode in deacon-orphan-recovery tests so
  OVERDECK_NO_RESUME=1 env doesn't suppress review re-dispatch
- Add getLatestSessionIdSync + resumeAgent to review-agent mock;
  fix synthesis→self-review regex in source-code assertion
- Add vi.useFakeTimers + vi.stubGlobal(fetch) + bd-mutex mock to
  done.test.ts; replace vi.runAllTimersAsync() with a
  vi.advanceTimersByTimeAsync loop so the line-710 cleanup timer
  fires correctly regardless of microtask interleaving depth

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
git diff --check failed: blank line at EOF in .beads/issues.jsonl.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…h in-memory journal

The test's verificationNotes assertion was failing because the async fire-and-forget
journal write (updateIssueRecordForIssue) was racing with afterEach cleanup that deleted
testRepoDir before the write completed.

Root cause analysis:
- review-status-sync.ts uses getOverdeckDatabaseSync() from overdeck/infra.ts (not
  getDatabase() from database/index.ts), so the testDb in-memory mock was never called
- verificationNotes are intentionally NOT stored in the DB (PAN-1988: journal-only)
- The async journal write failed because afterEach rm'd testRepoDir before the write ran

Fix: mock review-status-record-sync.js with an in-memory Map that captures writes
from updateIssueRecordForReviewStatusSync synchronously and returns them from
enrichReviewNotesFromRecordSync/readJournalStatusSync on the same tick. Eliminates
the async file I/O race entirely while faithfully simulating the journal behaviour.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
tests/cli/commands/work/done.test.ts (1)

242-251: ⚡ Quick win

Add a bounded timer-advance helper for the donePromise wait loops.

These polling loops are currently unbounded; if doneCommand(...) never settles, the test can hang instead of failing fast with a clear timeout cause.

Proposed refactor
+async function awaitDoneWithFakeTimers(
+  donePromise: Promise<void>,
+  maxMs = 120_000,
+  stepMs = 2_000,
+): Promise<void> {
+  let resolved = false;
+  donePromise.finally(() => { resolved = true; });
+
+  let elapsed = 0;
+  while (!resolved && elapsed < maxMs) {
+    await vi.advanceTimersByTimeAsync(stepMs);
+    elapsed += stepMs;
+  }
+
+  if (!resolved) {
+    throw new Error(`Timed out waiting for doneCommand after ${maxMs}ms`);
+  }
+  await donePromise;
+}
-const donePromise = doneCommand('PAN-714');
-let resolved5 = false;
-donePromise.finally(() => { resolved5 = true; });
-while (!resolved5) { await vi.advanceTimersByTimeAsync(2000); }
-await donePromise;
+await awaitDoneWithFakeTimers(doneCommand('PAN-714'));

Also applies to: 305-309, 354-362, 387-391, 510-514, 548-552

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/cli/commands/work/done.test.ts` around lines 242 - 251, The test
contains unbounded while loops that advance timers until a promise resolves,
which can cause the test to hang indefinitely if the promise never settles.
Create a bounded helper function that advances timers with a maximum iteration
limit and throws a timeout error if the limit is exceeded before the promise
resolves. Apply this helper to all occurrences of the while loop pattern that
waits for donePromise and similar promise-based waits (referenced at lines
242-251, 305-309, 354-362, 387-391, 510-514, 548-552) to ensure tests fail fast
with a clear timeout message rather than hanging.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.pan/records/pan-1866.json:
- Around line 108-111: The reviewStatus field is set to "passed" and mergeStatus
is set to "merging" in the records file, but this conflicts with the actual PR
state which has blocking "CHANGES REQUESTED" feedback that remains unresolved.
Update the reviewStatus field from "passed" to a state that reflects the
blocking feedback (such as "changes_requested" or "blocked"), and
correspondingly update the mergeStatus field from "merging" to a state that
indicates the merge is blocked or pending (such as "blocked" or
"pending_review") to accurately represent that the PR cannot be merged while
blocking feedback is outstanding.

In `@src/dashboard/server/routes/backlog.ts`:
- Around line 148-150: The validation checks for issueId and gate in the backlog
route are using Effect.fail with a generic Error, which causes the httpHandler
to return a 500 status code instead of the correct 400 Bad Request. Replace
these generic Error failures with a validation-specific error type or handler
that the httpHandler can recognize and map to a 400 response. Ensure both the
issueId validation and the gate value validation (checking if gate includes
'auto', 'ready', or 'blocked') return a 400 status code by using the appropriate
validation error mechanism available in your Effect framework setup.
- Around line 190-192: The validation checks for issueId and planning parameters
in the backlog route are currently returning Effect.fail instead of properly
formatted HTTP responses with status codes. Replace both validation error
returns (the issueId check and the planning parameter check) with jsonResponse({
error: ... }, { status: 400 }) to return a 400 Bad Request status with an error
object, matching the pattern used in the gate route.

In `@src/lib/cloister/flywheel.ts`:
- Around line 81-83: When parseSequenceMd returns a result where parsed.ok is
false, the code silently skips enrichment without any logging or error
indication, causing degradation in autoPickupBacklog to go unnoticed. Add an
else clause after the if (parsed.ok) block that logs the parse failure with the
actual error information so failures are visible. Apply the same fix to the
similar pattern at lines 151-153 where sequence reading may also fail silently.

---

Nitpick comments:
In `@tests/cli/commands/work/done.test.ts`:
- Around line 242-251: The test contains unbounded while loops that advance
timers until a promise resolves, which can cause the test to hang indefinitely
if the promise never settles. Create a bounded helper function that advances
timers with a maximum iteration limit and throws a timeout error if the limit is
exceeded before the promise resolves. Apply this helper to all occurrences of
the while loop pattern that waits for donePromise and similar promise-based
waits (referenced at lines 242-251, 305-309, 354-362, 387-391, 510-514, 548-552)
to ensure tests fail fast with a clear timeout message rather than hanging.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cac7f130-3ab0-451f-ae45-f187beb33e9d

📥 Commits

Reviewing files that changed from the base of the PR and between 408d1c7 and 2df5cbc.

⛔ Files ignored due to path filters (1)
  • sync-sources/hooks/record-cost-event.js.map is excluded by !**/*.map
📒 Files selected for processing (68)
  • .pan/continue.json
  • .pan/drafts/PAN-1866.md
  • .pan/records/pan-1866.json
  • .pan/spec.vbrief.json
  • .pan/test/result.json
  • packages/contracts/src/types.ts
  • roles/sequencer.md
  • src/cli/index.ts
  • src/dashboard/frontend/src/App.tsx
  • src/dashboard/frontend/src/components/Agents/FleetAgentsView.tsx
  • src/dashboard/frontend/src/components/Header.tsx
  • src/dashboard/frontend/src/components/Settings/RolesPanel.tsx
  • src/dashboard/frontend/src/components/Sidebar.tsx
  • src/dashboard/frontend/src/components/backlog/BacklogDAG.tsx
  • src/dashboard/frontend/src/components/primitives/AgentCard.tsx
  • src/dashboard/frontend/src/pages/BacklogSequencerPage.tsx
  • src/dashboard/frontend/src/types.ts
  • src/dashboard/server/read-model.ts
  • src/dashboard/server/routes/backlog.ts
  • src/dashboard/server/server.ts
  • src/dashboard/server/services/issue-data-service.ts
  • src/lib/__tests__/config-yaml-roles.test.ts
  • src/lib/__tests__/flywheel-sequence-auth.test.ts
  • src/lib/__tests__/settings-api.test.ts
  • src/lib/__tests__/tmux-server.test.ts
  • src/lib/agents.ts
  • src/lib/backlog/__tests__/backlog-auto-trigger.test.ts
  • src/lib/backlog/__tests__/backlog-input.test.ts
  • src/lib/backlog/__tests__/label-ops.test.ts
  • src/lib/backlog/__tests__/sequence-io.test.ts
  • src/lib/backlog/__tests__/sequencer-agent.test.ts
  • src/lib/backlog/__tests__/spawn-sequencer.test.ts
  • src/lib/backlog/__tests__/types.test.ts
  • src/lib/backlog/__tests__/write-sequence.test.ts
  • src/lib/backlog/backlog-auto-trigger.ts
  • src/lib/backlog/backlog-input.ts
  • src/lib/backlog/label-ops.ts
  • src/lib/backlog/sequence-io.ts
  • src/lib/backlog/sequencer-agent.ts
  • src/lib/backlog/types.ts
  • src/lib/cloister/__tests__/reactive-scheduler.test.ts
  • src/lib/cloister/__tests__/review-agent.test.ts
  • src/lib/cloister/flywheel.ts
  • src/lib/config-yaml.ts
  • src/lib/database/__tests__/backlog-sequence-schema.test.ts
  • src/lib/database/__tests__/pending-auto-merges-schema.test.ts
  • src/lib/database/backlog-sequence-db.ts
  • src/lib/database/schema.ts
  • src/lib/flywheel-merge-order.ts
  • src/lib/memory/fts-operations.ts
  • src/lib/overdeck/agents.ts
  • src/lib/overdeck/backlog.ts
  • src/lib/overdeck/memory.ts
  • src/lib/router-config.ts
  • src/lib/settings-api.ts
  • src/lib/tmux.ts
  • src/lib/tracker/github.ts
  • src/lib/tracker/interface.ts
  • sync-sources/hooks/record-cost-event.js
  • tests/cli/commands/work/done.test.ts
  • tests/fixtures/synced-skills.txt
  • tests/integration/post-review-rebase-scenario.test.ts
  • tests/lib/cloister/deacon-orphan-recovery.test.ts
  • tests/lib/cloister/review-agent.test.ts
  • tests/lib/tracker/github.test.ts
  • tests/unit/lib/overdeck/agents.test.ts
  • tests/unit/lib/overdeck/no-loss-matrix.ts
  • tests/unit/lib/pan-dir/verdict-restore.test.ts
💤 Files with no reviewable changes (1)
  • .pan/test/result.json
✅ Files skipped from review due to trivial changes (10)
  • tests/fixtures/synced-skills.txt
  • src/dashboard/frontend/src/types.ts
  • src/dashboard/frontend/src/components/Header.tsx
  • tests/unit/lib/pan-dir/verdict-restore.test.ts
  • src/lib/agents.ts
  • src/lib/tracker/interface.ts
  • src/lib/tests/tmux-server.test.ts
  • src/lib/database/tests/pending-auto-merges-schema.test.ts
  • src/lib/tests/config-yaml-roles.test.ts
  • .pan/drafts/PAN-1866.md
🚧 Files skipped from review as they are similar to previous changes (47)
  • src/lib/tmux.ts
  • src/dashboard/server/server.ts
  • src/lib/backlog/tests/sequence-io.test.ts
  • src/lib/tracker/github.ts
  • src/dashboard/frontend/src/App.tsx
  • src/lib/backlog/tests/label-ops.test.ts
  • src/dashboard/server/services/issue-data-service.ts
  • src/lib/overdeck/agents.ts
  • src/lib/settings-api.ts
  • src/dashboard/frontend/src/components/Sidebar.tsx
  • tests/unit/lib/overdeck/agents.test.ts
  • tests/unit/lib/overdeck/no-loss-matrix.ts
  • sync-sources/hooks/record-cost-event.js
  • src/dashboard/frontend/src/components/primitives/AgentCard.tsx
  • src/lib/tests/settings-api.test.ts
  • src/lib/backlog/tests/spawn-sequencer.test.ts
  • tests/lib/tracker/github.test.ts
  • src/lib/database/tests/backlog-sequence-schema.test.ts
  • src/lib/config-yaml.ts
  • src/lib/backlog/backlog-auto-trigger.ts
  • src/lib/router-config.ts
  • src/dashboard/frontend/src/components/Agents/FleetAgentsView.tsx
  • src/lib/backlog/types.ts
  • src/lib/cloister/tests/reactive-scheduler.test.ts
  • src/lib/backlog/tests/backlog-auto-trigger.test.ts
  • src/lib/cloister/tests/review-agent.test.ts
  • src/lib/backlog/tests/types.test.ts
  • src/lib/memory/fts-operations.ts
  • packages/contracts/src/types.ts
  • src/cli/index.ts
  • src/dashboard/frontend/src/components/Settings/RolesPanel.tsx
  • src/dashboard/server/read-model.ts
  • src/lib/backlog/tests/sequencer-agent.test.ts
  • src/lib/backlog/tests/write-sequence.test.ts
  • src/lib/tests/flywheel-sequence-auth.test.ts
  • src/lib/backlog/label-ops.ts
  • src/lib/backlog/sequence-io.ts
  • .pan/continue.json
  • src/lib/backlog/tests/backlog-input.test.ts
  • .pan/spec.vbrief.json
  • src/lib/overdeck/memory.ts
  • src/lib/backlog/sequencer-agent.ts
  • src/lib/database/backlog-sequence-db.ts
  • src/lib/database/schema.ts
  • src/lib/flywheel-merge-order.ts
  • src/dashboard/frontend/src/components/backlog/BacklogDAG.tsx
  • src/dashboard/frontend/src/pages/BacklogSequencerPage.tsx

Comment on lines +108 to +111
"reviewStatus": "passed",
"testStatus": "passed",
"verificationStatus": "passed",
"mergeStatus": "merging",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Review/merge state is inconsistent with active blocking feedback.

Line 108 ("reviewStatus": "passed") and Line 111 ("mergeStatus": "merging") conflict with the current PR state described in this PR context (blocking “CHANGES REQUESTED” comments still outstanding). This can produce incorrect downstream automation decisions from the record file.

🔧 Suggested fix
-    "reviewStatus": "passed",
+    "reviewStatus": "changes_requested",
...
-    "mergeStatus": "merging",
+    "mergeStatus": "blocked",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"reviewStatus": "passed",
"testStatus": "passed",
"verificationStatus": "passed",
"mergeStatus": "merging",
"reviewStatus": "changes_requested",
"testStatus": "passed",
"verificationStatus": "passed",
"mergeStatus": "blocked",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.pan/records/pan-1866.json around lines 108 - 111, The reviewStatus field is
set to "passed" and mergeStatus is set to "merging" in the records file, but
this conflicts with the actual PR state which has blocking "CHANGES REQUESTED"
feedback that remains unresolved. Update the reviewStatus field from "passed" to
a state that reflects the blocking feedback (such as "changes_requested" or
"blocked"), and correspondingly update the mergeStatus field from "merging" to a
state that indicates the merge is blocked or pending (such as "blocked" or
"pending_review") to accurately represent that the PR cannot be merged while
blocking feedback is outstanding.

Comment on lines +148 to +150
if (!issueId) return yield* Effect.fail(new Error('issueId is required') as never);
if (!['auto', 'ready', 'blocked'].includes(gate))
return yield* Effect.fail(new Error('gate must be auto, ready, or blocked') as never);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Validation errors incorrectly return 500 instead of 400.

Using Effect.fail(new Error(...) as never) causes validation failures to be caught by httpHandler's generic catchCause, which returns a 500 status. Client-side validation errors (missing issueId, invalid gate value) should return 400 Bad Request.

🐛 Proposed fix
-    if (!issueId) return yield* Effect.fail(new Error('issueId is required') as never);
-    if (!['auto', 'ready', 'blocked'].includes(gate))
-      return yield* Effect.fail(new Error('gate must be auto, ready, or blocked') as never);
+    if (!issueId) return jsonResponse({ error: 'issueId is required' }, { status: 400 });
+    if (!['auto', 'ready', 'blocked'].includes(gate))
+      return jsonResponse({ error: 'gate must be auto, ready, or blocked' }, { status: 400 });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/dashboard/server/routes/backlog.ts` around lines 148 - 150, The
validation checks for issueId and gate in the backlog route are using
Effect.fail with a generic Error, which causes the httpHandler to return a 500
status code instead of the correct 400 Bad Request. Replace these generic Error
failures with a validation-specific error type or handler that the httpHandler
can recognize and map to a 400 response. Ensure both the issueId validation and
the gate value validation (checking if gate includes 'auto', 'ready', or
'blocked') return a 400 status code by using the appropriate validation error
mechanism available in your Effect framework setup.

Comment on lines +190 to +192
if (!issueId) return yield* Effect.fail(new Error('issueId is required') as never);
if (!['skip', 'auto', 'interactive'].includes(planning))
return yield* Effect.fail(new Error('planning must be skip, auto, or interactive') as never);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Same validation error status issue as gate route.

Apply the same fix pattern: return jsonResponse({ error: ... }, { status: 400 }) instead of Effect.fail(new Error(...) as never).

🐛 Proposed fix
-    if (!issueId) return yield* Effect.fail(new Error('issueId is required') as never);
-    if (!['skip', 'auto', 'interactive'].includes(planning))
-      return yield* Effect.fail(new Error('planning must be skip, auto, or interactive') as never);
+    if (!issueId) return jsonResponse({ error: 'issueId is required' }, { status: 400 });
+    if (!['skip', 'auto', 'interactive'].includes(planning))
+      return jsonResponse({ error: 'planning must be skip, auto, or interactive' }, { status: 400 });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/dashboard/server/routes/backlog.ts` around lines 190 - 192, The
validation checks for issueId and planning parameters in the backlog route are
currently returning Effect.fail instead of properly formatted HTTP responses
with status codes. Replace both validation error returns (the issueId check and
the planning parameter check) with jsonResponse({ error: ... }, { status: 400 })
to return a 400 Bad Request status with an error object, matching the pattern
used in the gate route.

Comment on lines +81 to +83
const parsed = parseSequenceMd(md);
if (parsed.ok) {
// Build issue lookups from the shared issue service (lazy-require avoids

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Surface sequence parse/read failures instead of silently disabling sequence guidance.

When parsing or reading fails, the code quietly drops enrichment, so autoPickupBacklog can degrade without any visible signal.

Suggested patch
-        if (parsed.ok) {
+        if (parsed.ok) {
           // Build issue lookups from the shared issue service (lazy-require avoids
           // circular module load during CLI startup).
           type IssueRow = { ref?: string; identifier?: string; labels?: string[]; author?: string; assignee?: { name?: string } | string };
           const getIssueRows = (): IssueRow[] => {
@@
           }
           sequenceSection = `\n\nBacklog sequence (${parsed.doc.nodes.length} issues ranked):\n${top10.join('\n')}\n${nextLine}${pickInstruction}`;
+        } else {
+          sequenceSection = `\n\nBacklog sequence unavailable: ${parsed.error}. Falling back to normal priority for this pass.`;
         }
-      } catch {
-        // sequence.md exists but couldn't be parsed — skip enrichment
+      } catch (error) {
+        sequenceSection =
+          '\n\nBacklog sequence unavailable: failed to read .pan/backlog/sequence.md. Falling back to normal priority for this pass.';
       }

Also applies to: 151-153

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/cloister/flywheel.ts` around lines 81 - 83, When parseSequenceMd
returns a result where parsed.ok is false, the code silently skips enrichment
without any logging or error indication, causing degradation in
autoPickupBacklog to go unnoticed. Add an else clause after the if (parsed.ok)
block that logs the parse failure with the actual error information so failures
are visible. Apply the same fix to the similar pattern at lines 151-153 where
sequence reading may also fail silently.

@eltmon

eltmon commented Jun 20, 2026

Copy link
Copy Markdown
Owner Author

Merge Blocked — Post-Rebase Verification Failed

Failed check: test

The branch was rebased successfully but verification failed. The work agent needs to fix the errors and resubmit.

@eltmon eltmon merged commit 2527f92 into main Jun 20, 2026
6 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Jun 21, 2026
14 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant