pipeline-mode.md
78 lines · skills/ce-debug/references/pipeline-mode.md at 3449230
Most marks come from matching words in the file, so treat them as pointers.
ce-debug — pipeline mode (non-interactive)
Loaded when ce-debug is invoked with mode:pipeline by an orchestrator (ce-babysit-pr, lfg). The skill runs to completion without ever asking the user and returns a structured result the caller composes. The investigation rigor is unchanged. Only the interaction and the fix-authority boundary change.
Authority: you act under the orchestrator's inherited scope
Being invoked by an orchestrator is not itself authorization. You mutate under the inherited scope the orchestrator holds from the user: actions = fix / commit / push on the current branch; exclusions = merge, rebase, force-push, approve a gated CI run. That authorized scope is fixed. You may narrow it (defer a fix, return needs-human) but never broaden it. If the only way to make CI green is an excluded action (a rebase or force-push to untangle history, or approving a gated run), that action is outside the authorized scope: **defer as needs-human** with a decision_context, do not perform it. This is a boundary on the mechanism of a change. It sits alongside the convergent/divergent content boundary below. A fix can be convergent in content yet still outside the authorized scope in mechanism.
Non-interactive overrides (per phase)
- Phase 0 (triage): If an issue fetch fails, do not ask the user to paste content. Proceed with the input you have and note the gap in the return. Do not ask "what have you tried"; infer prior attempts from the input.
- Phase 1 (reproduce): When reproduction cannot run in this environment (a CI- or production-only failure), do not ask for access, artifacts, or a go-ahead. Continue on the best evidence already in reach: the failing job's logs, captured artifacts, the seeded log tails. If a gap-free root cause is still established, the ordinary statuses apply. If not, return
needs-humanwith adecision_contextnaming what reproduction requires and what was tried. - Phase 2 (root cause + fix gate): There is no "Fix it now / Diagnosis only" question. The caller invoked this skill to fix, so fix by default, but only convergent fixes (see the boundary below). A divergent fix is deferred, not applied.
- Phase 3 (workspace/branch): Operate on the current branch. The orchestrator decides branch context, so never prompt to create a branch and never prompt about uncommitted work. Commit the fix using the project's commit message conventions already in context; otherwise follow the recent log pattern, then use conventional commits (
fix(ci): <summary>for a CI failure,fix: <summary>otherwise). User override wins. Push the fix. Never weaken, skip, or mock a failing assertion to make it pass. Repair the real issue or defer. - Phase 4 (handoff): No prompt. Emit the structured return below as the last thing this skill writes, then skip the compound offer. The return ends this skill, not the turn. The caller runs in this same session, and its next step follows the return.
- Post-fix simplify and review steps: Skip them in pipeline to bound cost and nesting depth; the orchestrator scopes review at its own level. Keep the Phase 3 tests.
The fix-authority boundary: convergent vs divergent
Apply a fix only when it converges to intended behavior. That means it repairs the real defect so the code meets its planned/tested intent (a genuine bug: null deref, off-by-one, a broken call, a regression against a test that encodes intended behavior).
Defer (do not apply) any fix that would diverge from intended behavior: it would change a deliberate contract, API shape, default, or product/UX decision rather than repair a bug; or the "failure" is a test asserting a deliberate behavior that the fix would reverse; or making CI green would require a product/design call. This mirrors the ce-resolve-pr-feedback intent-conflict check. It requires evidence and is rare, and it is never a reason to dodge a real fix. When genuinely unsure whether a failure is a bug or a deliberate-behavior conflict, prefer deferring with a crisp decision_context over guessing.
Emergent trade-offs (when the caller passes a trajectory)
Some divergence is not visible in one pass. It emerges across rounds as ping-pong: your fix for A causes B to appear, and the fix for B brings A back. When the orchestrator seeds you with a trajectory (recurring_checks, check_recur_max, heads_since_progress), reason over it before fixing again, and do not raise a false alarm:
- Progressive failure migration (A fixed, B appears once, you fix B, done) is ordinary multi-step repair. Keep fixing. Do not park it.
- Oscillation (the same check/invariant returns after a fix aimed at it, defects cycle, or each fix trades one failure for another) means A and B cannot both hold without a larger change. That larger change is a product/design decision, so defer: apply nothing this round and return
needs-human, with adecision_contextthat names the two failures in tension, why they cannot be reconciled without a divergent change, the options, and your lean. - Moving-target guard: if the recurrence traces to an external cause (a base-branch merge, a dep bump, flaky infra) rather than your fixes fighting each other, it is not an emergent trade-off. Keep fixing, and note the external cause. Recurrence is only meaningful when your own fixes are what oscillate.
To defer, name the invariant the fix would need to satisfy and why no bounded convergent change satisfies it. If unsure whether it is genuine oscillation or one more real bug, prefer one more convergent attempt over a premature park.
Raw lines 24 to 33Reporting a deferred (divergent / needs-human) item
Never write a PR-body section. Never block. Report it so the human sees it after the run:
- If it maps to an open review thread, leave that thread open (and attach the
decision_contextas a reply when a thread reply is in scope). - Otherwise, **return it in the
residualslist** for the caller to place in its single run-report comment. For a barece-debuginvocation with no orchestrator and no PR, file it as a ticket in the project's tracker (detected in Phase 1.4) with enough background to action it standalone. When no tracker is reachable, return it in the structured result and say plainly that nothing else recorded it.
Return each decision in the shared typed residual contract. Its sources enumerate every item this one decision covers: each failing check key with kind: "check", plus the stable ID and kind of every open review thread, comment, or review body represented by the same decision. decision_context contains the quoted failure, investigation, decision reason, options with tradeoffs, and nullable recommendation. thread_urls includes every owned open thread (every open thread this decision covers) and is empty only when no source is a thread. The caller persists and invalidates the complete source set as one unit, so never split, omit, or summarize which items a decision covers.
Structured return
The return in pipeline mode is machine-readable (the caller parses it):
{
"status": "fixed-and-pushed | fixed-not-pushed | diagnosed-no-fix | flaky-infra | needs-human",
"summary": "<one line: what happened>",
"root_cause": "<causal chain, brief>",
"changed_files": ["..."],
"head_sha": "<sha of the fix commit, when fixed-and-pushed or fixed-not-pushed>",
"residuals": [
{
"type": "needs-human",
"sources": [
{ "id": "<failing-check-key>", "kind": "check" },
{ "id": "<owned-open-thread-id, when any>", "kind": "thread" }
],
"decision_context": {
"quoted_feedback": "<the failure or constraint in tension>",
"investigation": "<what was inspected and found>",
"decision_reason": "<why no bounded convergent fix is safe>",
"options": [ { "option": "<choice>", "tradeoff": "<gain and loss>" } ],
"recommendation": "<lean and why, or null>"
},
"thread_urls": ["<URL for every owned open thread, or empty when none>"]
}
]
}fixed-and-pushed: a convergent fix was applied, tests pass, committed, and the push succeeded.fixed-not-pushed: the same fix is applied and committed locally, but the push did not happen (no remote, no push access, an authorized scope that excludes pushing, a rejected push).head_shais the local commit; the first residual says why. Never report this asfixed-and-pushed(the caller re-snapshots a remote head that has not moved) or asdiagnosed-no-fix(the fix is applied).flaky-infra: a flake or infrastructure failure, not a code defect (the caller may retry).needs-human: the failure requires a divergent/product decision; nothing applied; seeresiduals.diagnosed-no-fix: root cause found but no safe convergent fix available this run; seeresiduals.