Skip to content

Pipeline ships wrong deliverable when command-step output_artifacts feed downstream inject_artifacts #1490

@nextlevelshit

Description

@nextlevelshit

TL;DR

executeCommandStep does not call writeOutputArtifacts. So command-type steps never register their declared output_artifacts in execution.ArtifactPaths. Any downstream step using memory.inject_artifacts: { step: <command-step>, artifact: <name> } falls through to the stdout-fallback path (executor.go:4566), and since most command steps redirect their work to a file (not stdout), the injection delivers a 0-byte file under .agents/artifacts/<as-name>. The persona then dutifully reports "no input" and the pipeline ships a vacuous deliverable.

Concrete trigger

ops-pr-respond on PR #1472 just now (run ops-pr-respond-20260428-182026-1f67):

Stage What happened
filter-scope (command) Wrote 12046-byte scoped-findings.json (15 real security/quality findings)
triage (planner, inject_artifacts: scoped_findings) Got 0-byte .agents/artifacts/scoped_findings
Triage prompt Step 0 short-circuit fired: "empty input → write empty result"
comment-back posted #1472 (comment): "No findings."

Pipeline status: green. Deliverable: wrong — the PR has 15 actionable findings the auditors found, none of which reached the contributor.

This is exactly the failure mode "green ≠ correct" — don't monitor only the green lights.

File-system evidence

```
$ stat -c '%s %n' .agents/workspaces/ops-pr-respond-20260428-182026-1f67/triage/.agents/artifacts/*
4096 .../triage/.agents/artifacts/fetch-pr ← persona step, fine
532 .../triage/.agents/artifacts/pr_context ← persona step, fine
0 .../triage/.agents/artifacts/scoped_findings ← command step, broken
0 .../triage/.agents/artifacts/scope_stats ← command step, broken
```

Root cause (executor.go)

  • Persona/adapter path: line 3572-3583 calls `writeOutputArtifacts` after the adapter returns. This populates `execution.ArtifactPaths[step.ID+":"+art.Name]`.
  • Command path (`executeCommandStep`, lines 1424-1614, dispatched from 1786): writes the script's output files to disk, but never populates `ArtifactPaths`. Returns directly to `validateStepContracts` (which reads files from disk and so doesn't notice).
  • Downstream `injectArtifacts` (line 4555) looks up by `step.ID+":"+ref.Artifact`. Miss → falls through to `execution.Results[ref.Step]["stdout"]` (line 4567). Command steps almost never echo their artifact bytes to stdout, so the injection writes empty data.

Scope of impact

Any pipeline that uses a `type: command` step to produce JSON for a downstream persona via `inject_artifacts`. At least:

  • `ops-pr-respond` — `filter-scope` → `triage`
  • Likely others — needs grep audit

The newer auto-injection path (#1452, `injectDependencyArtifacts`) does register paths via `ArtifactPaths` for command-step deps when it walks the upstream output_artifacts list. But `memory.inject_artifacts` is the legacy explicit path and bypasses that wiring.

Fix sketch

In `executeCommandStep` (or just after it returns at line 1788), call `writeOutputArtifacts(execution, step, workspacePath, nil)`. The function already handles the "persona/script wrote the file, don't overwrite" case correctly (line 4715: `os.Stat(artPath) == nil` → register existing path).

This is the minimum fix. A better fix is to unify the artifact-registration pass for both paths so the lookup logic is identical regardless of step type.

Test for the fix

Live re-run of `ops-pr-respond 1472`: expect non-empty `scoped_findings` injection, non-empty `triaged-findings.actionable`, substantive PR comment listing actual findings.

Severity

High. Composition primitives, audit-* aggregation, and any "command preprocessor → LLM consumer" pattern silently degrade to no-op. The pipeline shipping pretends success was 100% the spec's fault until you read the deliverable.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions