Skip to content

fix: validate artifact content before marking workflow step done#1098

Open
mvanhorn wants to merge 3 commits into
Fission-AI:mainfrom
mvanhorn:fix/1084-openspec-artifact-content-validation
Open

fix: validate artifact content before marking workflow step done#1098
mvanhorn wants to merge 3 commits into
Fission-AI:mainfrom
mvanhorn:fix/1084-openspec-artifact-content-validation

Conversation

@mvanhorn
Copy link
Copy Markdown

@mvanhorn mvanhorn commented May 15, 2026

Summary

Workflow interruptions (network blip, Ctrl-C, agent timeout) leave partially-written artifact files behind. On resume, detectCompleted sees proposal.md exists and marks the artifact done, so the workflow skips re-generating it. The half-written file then flows into downstream tasks, degrading every subsequent artifact silently.

Add a content-validation layer in artifact-graph/outputs.ts:

  • artifactOutputContentValid(changeDir, generates) — every resolved output file has at least one non-blank, non-HTML-comment line
  • artifactOutputComplete(changeDir, generates) — composes artifactOutputExists with content validation

Switch isArtifactComplete in state.ts to use artifactOutputComplete. Keep artifactOutputExists and resolveArtifactOutputs semantics unchanged so other callers (apply instructions, glob resolution) aren't affected.

Why this matters

Reporter @jiehu03 in #1084 identified the root cause precisely: detectCompleted -> isArtifactComplete -> artifactOutputExists only check file existence (fs.statSync / fast-glob). An empty file and a complete file produce identical "done" verdicts; there's no warning, no diff, no error — only a half-written artifact silently flowing into the next step.

Tests: 7 new cases under test/core/artifact-graph/ cover empty, whitespace-only, comment-only, valid content, and glob-pattern scenarios. pnpm vitest run test/core/artifact-graph/ -> 145 passed.

Fixes #1084

AI was used for assistance.

Summary by CodeRabbit

  • Bug Fixes

    • Workflow resume now regenerates artifact files that are empty, whitespace-only, or contain only comments instead of treating them as complete.
    • Artifact completion now requires meaningful content in generated files; glob-generated artifacts are marked incomplete if any matched file lacks meaningful content.
  • Tests

    • Expanded tests to validate content-based completion rules and glob behavior.
  • Documentation

    • Added a patch release note documenting the bug fix.

Review Change Stack

@mvanhorn mvanhorn requested a review from TabishB as a code owner May 15, 2026 23:53
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 15, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5817997d-b1f2-4bd8-8ead-276308efbed9

📥 Commits

Reviewing files that changed from the base of the PR and between ce0c1d7 and e608230.

📒 Files selected for processing (4)
  • .changeset/validate-artifact-content.md
  • src/core/artifact-graph/outputs.ts
  • test/core/artifact-graph/outputs.test.ts
  • test/core/artifact-graph/state.test.ts
✅ Files skipped from review due to trivial changes (1)
  • .changeset/validate-artifact-content.md

📝 Walkthrough

Walkthrough

This PR changes artifact completion detection to require generated outputs contain meaningful content (not empty, whitespace-only, or comment-only), ensuring workflow resumes regenerate incomplete artifact files instead of treating them as completed.

Changes

Artifact Content Validation for Workflow Resume

Layer / File(s) Summary
Output content validation helpers
src/core/artifact-graph/outputs.ts
New exported validators artifactOutputContentValid and artifactOutputComplete check that generated outputs resolve and that every resolved file contains at least one meaningful non-empty line after stripping HTML comment blocks. Internal isArtifactOutputFileContentValid reads files as UTF-8 and rejects empty, whitespace-only, comment-only, or unreadable outputs.
Completion detection integration
src/core/artifact-graph/state.ts
detectCompleted and isArtifactComplete now call artifactOutputComplete instead of artifactOutputExists, switching completion from "file exists" to "file exists and contains content." JSDoc updated accordingly.
Output validation test coverage
test/core/artifact-graph/outputs.test.ts
Tests extended to assert artifactOutputContentValid and artifactOutputComplete behavior: empty, whitespace-only, and HTML-comment-only files fail validation; files with non-comment content (e.g., a heading) pass; glob outputs fail if any matched file lacks content.
Completion detection integration tests
test/core/artifact-graph/state.test.ts
detectCompleted tests added/extended to assert artifacts are incomplete when outputs are empty/whitespace/comment-only and complete when outputs contain meaningful lines; glob-pattern artifacts are incomplete if any matched file is empty.
Release notes
.changeset/validate-artifact-content.md
Documents a patch release for @fission-ai/openspec noting that on workflow resume, empty/whitespace-only/HTML-comment-only artifact files are regenerated rather than marked complete (heading-only partials may still be treated as complete).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • Fission-AI/OpenSpec#967: Refactors artifact output resolution/completion and is related to the completion-path changes used by this PR.

Suggested reviewers

  • alfred-openspec
  • TabishB

Poem

🐰 I found a file, empty and shy,
I nudged and sniffed and gave a try.
Comments and whitespace — gone, be gone,
Now real content wakes with the dawn.
Hooray — resumed work hops along!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: validate artifact content before marking workflow step done' directly and accurately summarizes the main change: adding content validation to artifact completion checks to prevent incomplete files from being marked as done.
Linked Issues check ✅ Passed The pull request fully addresses the requirements from issue #1084: it adds content validation to detectCompleted() via new artifactOutputComplete and artifactOutputContentValid functions, prevents partially-written files from being marked complete, and includes comprehensive test coverage for empty, whitespace-only, and comment-only files.
Out of Scope Changes check ✅ Passed All changes are directly scoped to addressing issue #1084: new output validation functions, updated completion detection logic, corresponding tests, and a changeset entry documenting the fix. No extraneous modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.changeset/validate-artifact-content.md:
- Line 7: Update the changeset note sentence so it explicitly mentions
"comment-only" artifact files in addition to empty or whitespace-only files;
change the phrasing in the existing summary line (the sentence that currently
reads "Fixed workflow resumes so empty or whitespace-only artifact files are
regenerated instead of being treated as completed.") to include a short clause
like "and files containing only comments (e.g., HTML comment-only files)" so the
note matches the actual behavior.

In `@src/core/artifact-graph/outputs.ts`:
- Around line 56-59: artifactOutputComplete currently triggers
resolveArtifactOutputs twice via artifactOutputExists and
artifactOutputContentValid; change it to call resolveArtifactOutputs once, store
the resolved outputs, and use those results to perform the existence and
content-validity checks. Either refactor artifactOutputExists and
artifactOutputContentValid to accept the pre-resolved outputs (e.g., add
parameters like resolvedOutputs) or implement the existence/content checks
directly inside artifactOutputComplete using resolveArtifactOutputs' return;
reference artifactOutputComplete, artifactOutputExists,
artifactOutputContentValid, and resolveArtifactOutputs when updating code so you
only resolve once and reuse the resolved list for both checks.

In `@test/core/artifact-graph/outputs.test.ts`:
- Around line 46-58: Add a new unit test in outputs.test.ts that writes
HTML-comment-only content into the same temp file used by the other cases (e.g.,
write only an HTML comment like <!-- note --> to proposal.md in tempDir) and
assert that artifactOutputContentValid(tempDir, 'proposal.md') === false and
artifactOutputComplete(tempDir, 'proposal.md') === false; place this test
alongside the existing whitespace-only and heading tests so the helper-level
contract for comment-only content is explicitly covered.

In `@test/core/artifact-graph/state.test.ts`:
- Around line 90-101: Add a negative test case to ensure files that contain only
HTML comments are not treated as completed: in
test/core/artifact-graph/state.test.ts add an it(...) similar to the existing
heading test that uses createSchema and ArtifactGraph.fromSchema to create the
graph, writes a file at path.join(tempDir, 'proposal.md') containing only HTML
comments (e.g. "<!-- comment -->\n") via fs.writeFileSync, calls
detectCompleted(graph, tempDir), and asserts
expect(completed.has('proposal')).toBe(false); this mirrors the existing
single-heading test but verifies the "non-comment meaningful content" rule.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f1d893d1-6079-4df1-ba92-cf0de9c45737

📥 Commits

Reviewing files that changed from the base of the PR and between 8498042 and dca846e.

📒 Files selected for processing (5)
  • .changeset/validate-artifact-content.md
  • src/core/artifact-graph/outputs.ts
  • src/core/artifact-graph/state.ts
  • test/core/artifact-graph/outputs.test.ts
  • test/core/artifact-graph/state.test.ts

Comment thread .changeset/validate-artifact-content.md Outdated
Comment thread src/core/artifact-graph/outputs.ts
Comment thread test/core/artifact-graph/outputs.test.ts
Comment thread test/core/artifact-graph/state.test.ts
…tests

- artifactOutputComplete now resolves outputs once instead of twice
- changeset note mentions comment-only files (matches impl behavior)
- outputs.test.ts: comment-only artifact rejected by content validator
- state.test.ts: detectCompleted excludes comment-only files

Tests: pnpm vitest run test/core/artifact-graph/ — 147 passed.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/core/artifact-graph/outputs.ts`:
- Around line 67-73: The current check uses trimmed.startsWith('<!--') so
multi-line HTML comments like <!--\nfoo\n--> cause the middle lines (e.g. "foo")
to be treated as content; update the logic that determines "meaningful" content
by either stripping all HTML comment blocks from content before splitting (use a
non-greedy block removal for <!-- ... -->) or by iterating lines with an
inComment boolean that flips when encountering <!-- and --> so lines inside a
comment are ignored; adjust the code that uses content.split(...).some(...) (and
the trimmed variable/startsWith('<!--') test) to use this new comment-aware
approach so only real non-comment, non-whitespace lines count as meaningful
content.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 985611bb-85fe-48b1-8c7e-dfddfc6cb0b2

📥 Commits

Reviewing files that changed from the base of the PR and between dca846e and ce0c1d7.

📒 Files selected for processing (4)
  • .changeset/validate-artifact-content.md
  • src/core/artifact-graph/outputs.ts
  • test/core/artifact-graph/outputs.test.ts
  • test/core/artifact-graph/state.test.ts
✅ Files skipped from review due to trivial changes (1)
  • .changeset/validate-artifact-content.md

Comment thread src/core/artifact-graph/outputs.ts Outdated
@mvanhorn
Copy link
Copy Markdown
Author

Addressed in ce0c1d7:

  • outputs.ts:59 (Major) - artifactOutputComplete now resolves outputs once and reuses the result, dropping the duplicate resolveArtifactOutputs call through the existence path.
  • .changeset - note updated to mention comment-only files alongside empty/whitespace-only (matches what the validator actually filters).
  • outputs.test.ts - new case asserts artifactOutputContentValid and artifactOutputComplete both return false for a file containing only HTML comments.
  • state.test.ts - new detectCompleted case asserts a comment-only proposal.md is excluded from the completed set.

Tests: pnpm vitest run test/core/artifact-graph/ -- 147 passed.

Copy link
Copy Markdown
Collaborator

@alfred-openspec alfred-openspec left a comment

Choose a reason for hiding this comment

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

This is the right fix shape, and I like that artifactOutputExists stays existence-only while completion gets the stricter check. I ran the artifact-graph tests on the PR branch and they pass: pnpm vitest run test/core/artifact-graph/ → 147 passed.

One blocker remains from the latest CodeRabbit note: multiline HTML comment-only files still count as meaningful content. I added a temporary regression test with <!--\nplaceholder\n-->, and artifactOutputContentValid(tempDir, 'proposal.md') returned true. The current line-based trimmed.startsWith('<!--') only skips the opening line, so the middle comment line is treated as real content.

Please strip HTML comment blocks before checking lines, or track an inComment state across lines, and add that regression test. I would not block this PR on full template/schema-aware validation yet. This is a good narrow guard once comment-only detection is actually robust, but the release note/body should avoid implying it catches every possible partially-written artifact, since a heading-only partial still passes.

Multi-line HTML comments like:
  <!--
  placeholder
  -->
were incorrectly treated as meaningful content because the line-based
check only skipped lines starting with '<!--'. The middle line
("placeholder") was counted as real content, so comment-only files
were marked complete.

Strip HTML comment blocks via /<!--[\s\S]*?(-->|$)/g before splitting
into lines, then any non-whitespace line counts as meaningful content.
The non-greedy (-->|$) tail handles unterminated comment blocks safely.

Add regression tests in outputs.test.ts and state.test.ts covering the
multi-line comment-only case for both artifactOutputContentValid /
artifactOutputComplete and detectCompleted.

Update the changeset to mention HTML-comment-only specifically and
note that heading-only or other partially-written artifacts are not
yet detected (heading content still counts as meaningful).

Signed-off-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
@mvanhorn
Copy link
Copy Markdown
Author

Fixed in e608230. Applied CodeRabbit's suggested approach: strip HTML comment blocks via /<!--[\s\S]*?(-->|$)/g before the line-based content check. The non-greedy (-->|$) tail handles unterminated blocks too.

Added regression tests in both layers:

  • outputs.test.ts: "rejects multi-line comment-only artifact output content" asserts artifactOutputContentValid and artifactOutputComplete both return false for <!--\nplaceholder\n-->\n.
  • state.test.ts: "should not mark artifact complete when file only contains a multi-line HTML comment" covers the detectCompleted integration path.

Updated the changeset wording to specify "HTML-comment-only" and added the heading-only caveat you flagged, so the note doesn't overpromise.

Local vitest run test/core/artifact-graph/ reports 149 passing (up from 147), and ESLint reports 0 errors on the changed source. Thanks for catching the multi-line case.

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.

Incomplete artifacts marked as "done" after workflow interruption

2 participants