Skip to content

Pipeline Design 324

ezigus edited this page Apr 5, 2026 · 2 revisions

Now I have everything needed. Here is the ADR:


Design: feat(ruflo): close the learning feedback loop — call ruflo_learn_from_shipwright() at pipeline completion

Context

Shipwright's ruflo integration currently handles detection, MCP lifecycle, circuit-breaking, and memory indexing (ruflo-adapter.sh, 195 lines on this branch). The main branch already has 3 functions needed for learning — ruflo_store() (line 257), _ruflo_resolve_repo_hash() (line 314), and ruflo_learn_from_shipwright() (line 900) — totaling ~780 additional lines of adapter code including review/CQ stage helpers. However, none of these functions are called from the pipeline completion paths.

The pipeline has two terminal outcomes: success (end of stage_validate() at pipeline-stages-monitor.sh:115) and failure (threshold exceeded in stage_monitor() at line 254). Neither currently feeds outcome data back to ruflo's HNSW index for semantic recall on future runs. This means ruflo_recall_similar_outcomes() has no data to search — the feedback loop is open.

Constraints:

  • All ruflo calls must be fail-open (|| true) per adapter convention
  • Must guard with ruflo_available() + function-existence check (adapter may not be sourced)
  • Bash 3.2 compatible — no associative arrays, no ${var,,}
  • Outcome artifacts must go to $ARTIFACTS_DIR for pipeline traceability
  • Event schema (config/event-schema.json) must register new event types

Decision

Direct function calls at two pipeline exit points, with fail-open guards. Extract the three missing functions from main into this branch's ruflo-adapter.sh, then add call sites in pipeline-stages-monitor.sh.

Data Flow

stage_validate() success                   stage_monitor() threshold exceeded
        │                                           │
        ▼                                           ▼
Build outcome JSON                         Build outcome JSON
  {status:"success", issue,                  {status:"failure", issue,
   goal, task_type, duration_s}               goal, task_type, errors,
        │                                     error_threshold}
        ▼                                           │
Write to ARTIFACTS_DIR/                             ▼
  pipeline-outcome-validate.json           Write to ARTIFACTS_DIR/
        │                                    pipeline-outcome-monitor-failure.json
        ▼                                           │
ruflo_learn_from_shipwright(file)          ruflo_learn_from_shipwright(file)
        │                                           │
        ├─ _ruflo_resolve_repo_hash()      (same flow)
        ├─ jq parse task_type
        ├─ ruflo_store(key, content,
        │    "learning-<hash>",
        │    "skill-memory,outcome,<type>")
        └─ emit_event "ruflo.learn_from_shipwright"

Guard Pattern (both call sites identical)

if [[ -n "${ARTIFACTS_DIR:-}" ]]; then
    # ... build and write outcome JSON ...
    if ruflo_available && type ruflo_learn_from_shipwright >/dev/null 2>&1; then
        ruflo_learn_from_shipwright "$_outcome_file" || true
    fi
fi

Three layers of protection: (1) ARTIFACTS_DIR existence, (2) ruflo_available() boolean + function existence, (3) || true catch-all. The function itself has internal guards: ruflo_available || return 0 and _ruflo_resolve_repo_hash || return 0.

Error Handling

  • ruflo unavailable: ruflo_available() returns 1 → skip silently, pipeline unaffected
  • Function not sourced: type ruflo_learn_from_shipwright fails → skip silently
  • Bad JSON / jq failure: Function returns 0 (fail-open), no outcome indexed
  • ruflo timeout: Circuit-breaker fires (ruflo_with_timeout), disables ruflo for remainder of run
  • Disk write failure: echo > file 2>/dev/null || true — outcome not persisted, pipeline continues

Alternatives Considered

  1. Inline learning code at call sites — Pros: no function extraction, self-contained / Cons: duplicates ~30 lines of jq + store + emit logic across two sites, violates DRY, breaks if learning logic evolves. Rejected.

  2. Batch learning via post-pipeline job — Pros: decouples learning from pipeline, easier standalone testing / Cons: adds async orchestration, learning doesn't feed back to the next run's stage_plan/stage_design recall, more operational surface. Rejected.

  3. Event-callback mechanism — Pros: flexible, could enable/disable per template / Cons: current emit_event is fire-and-forget (writes JSONL), not a callback dispatch; building callback infra for one call site is over-engineering. Rejected.

Implementation Plan

Files to modify

File Change Estimated lines
scripts/lib/ruflo-adapter.sh Cherry-pick ruflo_store(), _ruflo_resolve_repo_hash(), ruflo_learn_from_shipwright() and their transitive dependencies (_ruflo_run_quiet, ruflo_recall, _ruflo_repo_hash_candidates, _ruflo_shipwright_memory_dir) from main (lines 230–940) ~700 lines (extract, not new code)
scripts/lib/pipeline-stages-monitor.sh Add outcome JSON + ruflo_learn_from_shipwright call at end of stage_validate() (before line 115) and after emit_event "monitor.alert" in stage_monitor() (after line 264) ~30 lines
config/event-schema.json Add ruflo.learn_from_shipwright event type ~4 lines
scripts/sw-ruflo-adapter-test.sh Add unit test for ruflo_learn_from_shipwright + integration tests for both call sites ~80 lines

Files to create

None.

Dependencies

None (all ruflo dependencies already present via MCP and the adapter's fail-open pattern).

Risk Areas

Risk Mitigation
Cherry-pick from main brings unrelated code Only extract the 3 target functions + their direct helpers; verify with grep that no new globals or side-effects leak
now_epoch / PIPELINE_START_EPOCH not available in validate context Both are set by pipeline-stages.sh before any stage runs; verify with read of pipeline-stages.sh
Outcome JSON missing task_type Function handles this: falls back to issue_type, then "unknown" (line 917 on main)
jq not installed jq is already a hard dependency of the pipeline (used in every stage config read); not a new requirement
Monitor failure path has complex branching (rollback, hotfix issue, etc.) Insert call before rollback logic so outcome is captured regardless of rollback success

Validation Criteria

  • ruflo_learn_from_shipwright exists in scripts/lib/ruflo-adapter.sh and accepts a file path argument
  • stage_validate() writes pipeline-outcome-validate.json to $ARTIFACTS_DIR and calls ruflo_learn_from_shipwright with it
  • stage_monitor() failure path writes pipeline-outcome-monitor-failure.json and calls ruflo_learn_from_shipwright with it
  • Both call sites are wrapped with ruflo_available && type ruflo_learn_from_shipwright >/dev/null 2>&1 guard and || true fallback
  • ruflo.learn_from_shipwright event type registered in config/event-schema.json with optional: ["task_type", "repo"]
  • When RUFLO_AVAILABLE=false, neither call site produces errors or slows the pipeline
  • Existing 13 tests in sw-ruflo-adapter-test.sh remain green
  • New unit test: ruflo_learn_from_shipwright returns 0 and emits event when given valid JSON
  • New unit test: ruflo_learn_from_shipwright returns 0 silently when ruflo unavailable
  • Integration test: mock stage_validate success path produces outcome artifact
  • Integration test: mock stage_monitor failure path produces outcome artifact
  • npm test passes with no regressions

Clone this wiki locally