Skip to content

T-0AKS-an-issues-loop-joins-the-runner-roadmap-tasks-and-issues

State: closed/done · Impact: medium · Complexity: medium

Add an issues loop to sdlc orchestrate run, so issues advance up to their autonomy ceiling alongside roadmap tasks. Design: D-0019-sdlc-chain-stages-and-labels.

  1. Extend LOOP_NAMES in solutions/ontological/lib/services/orchestrator/ops/run.ts from ['work', 'prs', 'merges'] to include 'issues', and thread a runIssuesTick dependency through runPasses() the same way runWorkTick/runPrsTick/runMergesTick are already threaded (default from the real module; injectable in tests, same pattern the existing three ticks use).
  2. Add solutions/ontological/lib/services/orchestrator/ticks/issues.ts (runIssuesTick(ctx)), modeled on ticks/work.ts’s structure but for GitHub issues instead of task frontmatter:
    • List open issues carrying any sdlc:stage/<x> label (via ctx.gh.issueList(...), from T-3YQO’s new easy-gh verbs — an issue with no sdlc:stage/* label is not chain-managed and is skipped).
    • Per issue: parse its current stage from the sdlc:stage/<x> label (first/only match — two stage labels on one issue is a data error, log and skip); parse its ceiling from sdlc:auto/<x> (default when absent: the ceiling equals the current stage — D-0019’s “silence means wait for a human” default, i.e. no auto-advance until triage or a human sets a ceiling label).
    • If the current stage’s index in CHAIN_STAGE_NAMES (lib/config/chain.ts, from T-8JJE) is < the ceiling’s index: resolve the next stage’s bound workflow via resolveChain(). A checkpoint stage (workflow: null) advances the label immediately with no dispatch. A bound stage dispatches via the existing step-runner (../step-runner.ts — no new dispatch mechanism); on success, relabel the issue to the next stage (ctx.gh.setIssueLabel, from T-3YQO); on failure, leave the issue at its current label and log (a “park” for issues, mirroring the work tick’s park-and-move-on behavior — no in-tick retry). One stage advance per issue per tick, matching a label-driven board moving one column at a time.
    • The define stage’s dispatch also calls T-3YQO’s ensureLinkedTask() before running the define workflow, so an issue reaching define for the first time gets its task link.
    • If the current stage already equals the ceiling: fire stage.ceiling_reached through the existing deps.notify seam (lib/services/notify/dispatch.ts’s notify() — the same function work.ts’s notifyParked already calls for task.parked). Add 'stage.ceiling_reached' to the NotifyEvent union (its own doc comment says “extend freely as later ticks/phases need new event kinds” — task.parked already set the precedent of a tick firing a Phase-8-named event ahead of Phase 8’s full notify: channel config). Full channel delivery (desktop/ntfy config) stays Phase 8’s; this phase only needs the event to fire and be visible in the pass’s log-tick digest, exactly like task.parked is today. Then move on to the next issue — “blocked is not stopped”.
    • Respect ctx.dryRun: report what would be dispatched/relabeled without calling any mutating gh verb, mirroring work.ts’s claim: 'would-dispatch' convention.
  3. Add solutions/ontological/lib/services/orchestrator/ticks/issues.test.ts and .../ops/run.test.ts coverage: inject a fake ctx.gh returning a fixture list of open issues with labels (no real gh call, per External-write safety). Cover: an issue below ceiling advances exactly one stage; an issue at ceiling is skipped and logged, not advanced; a checkpoint-stage advance needs no workflow dispatch; a --loop issues --once --dry-run run reports the would-be move without mutating any fixture state; --loop work,issues --once dispatches both loops in one pass (no cross-loop interference).
AreaNote
solutions/ontological/lib/services/orchestrator/ops/run.tsExtend LOOP_NAMES, thread runIssuesTick
solutions/ontological/lib/services/orchestrator/ticks/issues.tsNew: the issues tick
solutions/ontological/lib/services/orchestrator/ticks/issues.test.tsNew: fixture-backed tick tests
  • AC-1: runIssuesTick given a fixture issue at stage implement with ceiling check advances it to check (one stage) and dispatches the check workflow’s bound step(s) via the step-runner.
  • AC-2: The same fixture issue, once at stage check with ceiling check, is left unchanged by a subsequent tick (ceiling reached, logged, not advanced).
  • AC-3: sdlc orchestrate run --loop issues --once (against a fixture ctx.gh) advances at least one fixture issue exactly one stage and reports it in the pass’s log-tick digest.
  • AC-4: sdlc orchestrate run --loop work,issues --once dispatches both ticks in one pass with no shared-state interference (task dispatch counts and issue dispatch counts are independent).
  • AC-5: No test in this task’s suite calls a real gh binary — every ctx.gh is a fixture double.
  • The label vocabulary’s existence/management (apply, T-WPBJ) and the issue-task linking/sync primitives themselves (T-3YQO) — this task only calls them.
  • A dedicated Phase-8 notify: config section/event catalog for stage.ceiling_reached — this task fires it through the existing notify() seam (lib/services/notify/dispatch.ts), which already delivers for real over the two channels orchestrator.notify config gates (desktop/ntfy), same as work-park/task.parked today; it does not add a new channel or config surface of its own.
  • Cross-host lease coordination for issue dispatch analogous to the work tick’s slot lease (_op_lease.ts) — a single-host, single-loop-iteration assumption is acceptable for this phase’s scope; revisit if concurrent multi-host issue dispatch becomes a real requirement.
  • solutions/ontological/lib/config/chain.ts (T-8JJE) for CHAIN_STAGE_NAMES/resolveChain.
  • packages/ts/easy-gh’s new issue verbs and lib/services/issues/{link,mapping}.ts (T-3YQO).
  • packages/ts/easy-gh’s new label verbs (T-WPBJ) for setIssueLabel’s underlying issue edit --add-label/--remove-label calls (label values come from T-WPBJ; label mutation on an issue is a gh issue edit call added alongside T-3YQO’s issue verbs — coordinate so the two tasks don’t both add a conflicting setIssueLabel-shaped method).

← Back to Tasks