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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions