Skip to content

feat(pipeline): skill phase kind + PhaseRunner seam + halt/answer/resume - #206

Merged
saucam merged 1 commit into
feat/sdlc-pipeline-wiringfrom
feat/sdlc-pipeline-halt
Jul 20, 2026
Merged

feat(pipeline): skill phase kind + PhaseRunner seam + halt/answer/resume#206
saucam merged 1 commit into
feat/sdlc-pipeline-wiringfrom
feat/sdlc-pipeline-halt

Conversation

@saucam

@saucam saucam commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator

What

Third slice: phase execution + the halt → answer → resume path. Still dark — pipeline off by default.

  • makeSkillPhaseKind(runner?) — the "skill" phase kind. fn skills run natively (no backend); prompt / slash skills drive through an injectable PhaseRunner (the backend seam). With no runner configured, a prompt/slash skill fails with a clear reason, so the kind stays usable in pure tests and degrades safely.
  • PhaseRunner (runner.ts) — the seam between a phase and a backend (runPrompt with per-phase provider / model for cross-provider-per-phase routing). The SessionManager-backed adapter that actually drives a worker turn lands in a follow-up.
  • PipelineManager.answer(id, requestId, { approved, value }) — the daemon side of halt → answer-from-a-frontend → resume (§4.1, §5.3): resolves a halted phase (pass/fail) and continues advancing to the next halt or terminal. Persists across restart.

Verification

  • bun test src/daemon/pipeline42 pass / 0 fail (16 new: skill-kind + answer/resume)
  • tsc --noEmit — clean project-wide
  • biome check — clean

Stacked

Base = feat/sdlc-pipeline-wiring (#205). This branch also carries a one-line lint fix (import type in manager.ts) that the rebased stack applies to #204/#205 as well.

Deferred to follow-ups

🤖 Generated with Claude Code

if (skill.kind === "fn") {
const res = await skill.run(ctx);
return { outcome: "passed", summary: res.summary, artifacts: res.artifacts };
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Medium — Uncaught exceptions in native fn skills

If a native fn skill throws an exception during execution, the error will propagate directly and reject the execution promise. Wrapping the skill execution in a try-catch block allows capturing the error and returning a graceful failure outcome (outcome: "failed") so the pipeline status can be updated gracefully in the database.

Suggested change
}
if (skill.kind === "fn") {
try {
const res = await skill.run(ctx);
return { outcome: "passed", summary: res.summary, artifacts: res.artifacts };
} catch (err: any) {
return { outcome: "failed", reason: `Skill execution failed: ${err?.message ?? err}` };
}
}

@github-actions

Copy link
Copy Markdown

🤖 Gemini code review

This PR introduces the skill phase execution flow (with PhaseRunner abstraction for external model/slash skills) and the halt-answer-resume mechanism for pipeline execution.

Findings: 🔴 0 · 🟠 0 · 🟡 1 · 🟢 0


Tokens spent · ⬆️ Input: 4,569 · ⬇️ Output: 238 · Σ Total: 9,452
Total may be higher due to thinking token counts.

Add the runtime layer. A 'skill' PhaseKind runs fn skills natively and drives prompt/slash skills through an injectable PhaseRunner (the backend seam; a SessionManager-backed adapter lands in a follow-up). PipelineManager.answer(id, requestId, {approved, value}) is the daemon side of halt -> answer-from-a-frontend -> resume: it resolves a halted phase (pass/fail) and continues advancing to the next halt or terminal. Fully unit-tested with fakes; no live backend required. Still dark (pipeline off by default).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@saucam

saucam commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed the Gemini finding: skill-kind.ts now wraps native fn skill execution in try/catch, returning a graceful outcome:"failed" attributed to the skill. (The engine-level guard from #204 already prevents a crash; this adds a clearer, skill-scoped failure reason.) Added a test for the throwing-fn path.

@saucam
saucam force-pushed the feat/sdlc-pipeline-halt branch from 9fa086c to 4822afd Compare July 19, 2026 23:18
@saucam
saucam merged commit f7a2f3f into feat/sdlc-pipeline-wiring Jul 20, 2026
saucam added a commit that referenced this pull request Jul 20, 2026
…205)

* feat(pipeline): config schema (off by default) + daemon boot wiring

Add a PipelineSchema to config (enabled=false default, defaultPack=null) with a CODEOID_PIPELINE_ENABLED env switch, and construct a PipelineManager in SessionManager when enabled — sharing the daemon DB and rehydrating non-terminal pipelines on boot (resume). Undefined when disabled, so the daemon stays dark by default. Pure createPipelineManagerFromConfig() factory keeps the enable/disable + share-DB + restart-survival behavior unit-tested without a full SessionManager.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(pipeline): skill phase kind + PhaseRunner seam + halt/answer/resume (#206)

Add the runtime layer. A 'skill' PhaseKind runs fn skills natively and drives prompt/slash skills through an injectable PhaseRunner (the backend seam; a SessionManager-backed adapter lands in a follow-up). PipelineManager.answer(id, requestId, {approved, value}) is the daemon side of halt -> answer-from-a-frontend -> resume: it resolves a halted phase (pass/fail) and continues advancing to the next halt or terminal. Fully unit-tested with fakes; no live backend required. Still dark (pipeline off by default).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
saucam added a commit that referenced this pull request Jul 20, 2026
…dark) (#204)

* docs: SDLC pipeline design — external prior-art & SOTA validation + refinements

Add §2a (Spec Kit / BMAD / Kiro / Roo / Aider + 2026 papers + omnigent/kiss_ai) and §5a (per-phase tool scoping, typed artifacts, entry/grounding gates, constitution/steering layer, gate enforcement tiers). Resolve open Q3/Q4/Q6; default the pipeline off (freestyle) until a pack is selected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(pipeline): SDLC pipeline primitive — engine, store, registries (dark)

Methodology-agnostic pipeline foundation (docs/sdlc-pipeline.md §5): PhaseDef/PipelineState types + the four plugin seams (PhaseKind, GatePlugin, SkillPlugin, Pack), a Map-backed registry, a built-in noop phase kind + always/manual gates, the advance engine (entry/exit gates; onFail halt/retry/abort), durable bun:sqlite state, and PipelineManager with resume() for restart survival. No daemon wiring, no methodology content — lands dark. 26 unit tests; tsc + biome clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(pipeline): config schema (off by default) + daemon boot wiring (#205)

* feat(pipeline): config schema (off by default) + daemon boot wiring

Add a PipelineSchema to config (enabled=false default, defaultPack=null) with a CODEOID_PIPELINE_ENABLED env switch, and construct a PipelineManager in SessionManager when enabled — sharing the daemon DB and rehydrating non-terminal pipelines on boot (resume). Undefined when disabled, so the daemon stays dark by default. Pure createPipelineManagerFromConfig() factory keeps the enable/disable + share-DB + restart-survival behavior unit-tested without a full SessionManager.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(pipeline): skill phase kind + PhaseRunner seam + halt/answer/resume (#206)

Add the runtime layer. A 'skill' PhaseKind runs fn skills natively and drives prompt/slash skills through an injectable PhaseRunner (the backend seam; a SessionManager-backed adapter lands in a follow-up). PipelineManager.answer(id, requestId, {approved, value}) is the daemon side of halt -> answer-from-a-frontend -> resume: it resolves a halted phase (pass/fail) and continues advancing to the next halt or terminal. Fully unit-tested with fakes; no live backend required. Still dark (pipeline off by default).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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