Skip to content

fix(resume): silent-skip event + Failed→Skipped regression test (#125, #126)#130

Merged
EffortlessSteven merged 1 commit into
mainfrom
fix/125-126-resume-audit-trail
Apr 18, 2026
Merged

fix(resume): silent-skip event + Failed→Skipped regression test (#125, #126)#130
EffortlessSteven merged 1 commit into
mainfrom
fix/125-126-resume-audit-trail

Conversation

@EffortlessSteven

Copy link
Copy Markdown
Member

Summary

Two trust-core regressions surfaced by the #90 synthetic rehearsal test (PR #124). Both are small, focused fixes — no refactor.

#125 — Silent resume-skip

Before: resume's already-complete match arm (`Published | Skipped`) logged a stdout info line and `continue`d, without emitting any event. `events.jsonl` had no record that resume had looked at the package.

After: emit `PackageSkipped { reason: "resume: state already " }` before continuing. An auditor reading only events.jsonl can now reconstruct resume's decisions for every planned package.

Receipt shape unchanged — already-complete packages still omit from `receipt.packages`, matching prior contract (tests `run_resume_runs_publish_when_state_exists` et al depend on that emptiness).

#126 — Failed → Skipped state drift

Investigated. Turned out the current sequential code already handles this correctly: when resume sees `Failed` state and `reg.version_exists` returns true, `update_state(Skipped)` is called and flushes to state.json. My targeted regression test passes with zero code changes.

The bad observation reported in the issue likely came from a stale state-dir artifact in the synthetic fixture, not a real bug in this path.

Keeping the test anyway as a regression guard: if a future refactor (parallel mode, concurrent writes, state-write reordering) breaks the semantics, the guard fires before the bug hits release CI.

Tests

Two new `#[serial]` tests in `engine::tests`:

  • `resume_emits_package_skipped_event_for_already_published_state` — seeds `Published`, runs `run_publish`, asserts `package_skipped` event appears in events.jsonl.
  • `resume_from_failed_ambiguous_updates_state_to_skipped_when_registry_visible` — seeds `Failed{class:Ambiguous}`, registry returns 200, reloads state.json, asserts `Skipped`.

All 49 existing resume/publish tests still pass.

Test plan

  • `cargo test -p shipper --lib -- run_publish resume_` — 49/49
  • `cargo clippy -p shipper --all-targets --all-features -- -D warnings` clean
  • `cargo fmt --all --check` clean
  • CI multi-OS green

Closes

         plus regression test for Failed->Skipped transition (#126)

## #125 — silent resume-skip

When resume encounters a package whose state is already \`Published\` or
\`Skipped\`, the sequential path short-circuited with a stdout info line
and \`continue\`, without recording an event. Since \`events.jsonl\` is
the **authoritative audit log** (state.json is a projection of it),
this left a user-visible gap: an auditor reading only events.jsonl
couldn't tell "resume looked at this package and trusted its terminal
state" from "resume never touched this package." Surfaced by the
synthetic rehearsal test (\`e2e_rehearse.rs\`, PR #124).

**Fix:** emit a \`PackageSkipped { reason: \"resume: state already <s>\" }\`
event in the already-complete match arm. No receipt entry is pushed —
the receipt vector has always excluded already-terminal packages on
the resume path, and callers depend on that shape (e.g.
\`run_resume_runs_publish_when_state_exists\`). Only the audit-trail
behavior changes.

## #126 — Failed->Skipped state drift

The issue speculated that resume's stdout-said-Skipped but
state.json-still-says-Failed could indicate \`update_state\` not firing
on the version-exists short-circuit when the prior state was Failed.

**Finding:** the current sequential code handles this correctly —
\`reg.version_exists(p) \=\\= true\` triggers
\`update_state(Skipped)\` which flushes to state.json, and a targeted
test (\`resume_from_failed_ambiguous_updates_state_to_skipped_when_registry_visible\`)
passes without any code change. The observation in the issue may have
been a stale-state-file artifact in the synthetic rehearsal fixture.

Landing the test anyway as a regression guard: if a future refactor
breaks this semantics (e.g. parallel mode taking a different path, or
state write getting clobbered), the guard fires immediately.

## Tests

Two new \`#[serial]\` tests in \`engine::tests\`:
- \`resume_emits_package_skipped_event_for_already_published_state\`
  — seeds state with Published, runs \`run_publish\`, asserts
  \`PackageSkipped\` appears in events.jsonl.
- \`resume_from_failed_ambiguous_updates_state_to_skipped_when_registry_visible\`
  — seeds state with Failed{Ambiguous}, registry returns 200, asserts
  reloaded state.json shows Skipped.

All 49 existing resume/publish tests remain green.
@gemini-code-assist

Copy link
Copy Markdown

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@coderabbitai

coderabbitai Bot commented Apr 17, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@EffortlessSteven has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 1 minutes and 52 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 1 minutes and 52 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7fe59cb1-b024-4770-9754-6952103e0e17

📥 Commits

Reviewing files that changed from the base of the PR and between cc0dc68 and 5faed53.

📒 Files selected for processing (1)
  • crates/shipper/src/engine/mod.rs
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/125-126-resume-audit-trail

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.

@codecov

codecov Bot commented Apr 17, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@EffortlessSteven EffortlessSteven merged commit 5c8d950 into main Apr 18, 2026
21 checks passed
@EffortlessSteven EffortlessSteven deleted the fix/125-126-resume-audit-trail branch April 18, 2026 00:03
EffortlessSteven added a commit that referenced this pull request Apr 18, 2026
Three-column reconciliation of what's actually merged vs each issue's
acceptance checklist. Single source of truth for what closes each
pillar issue.

**Findings:**

- **#90 Recover** — honestly closable. Code side is done (#124 + #130);
  operator-side real rehearsal is an ops action, not a code gap.

- **#97 Prove tier 2** — 85% done. Rehearsal + visibility + hard gate +
  plan_id binding all landed (#127 + #133). Missing: install/smoke
  check (cargo install against the rehearsal registry / consumer
  build). One narrow follow-up PR closes it.

- **#98 Remediate** — 60% done. Receipt schema + plan-yank (from-receipt)
  + yank primitive + fix-forward planning all landed (#121 + #132 +
  #134). Missing: plan-yank's --starting-crate graph mode, plan
  execution for yank + fix-forward. Two narrow follow-ups.

Also captures the two review concerns on #122 (Trusted Publishing)
that were addressed in a follow-up commit to that PR.

Recommended next merge order and follow-up PRs spelled out at bottom.
EffortlessSteven added a commit that referenced this pull request Apr 18, 2026
Three-column reconciliation of what's actually merged vs each issue's
acceptance checklist. Single source of truth for what closes each
pillar issue.

**Findings:**

- **#90 Recover** — honestly closable. Code side is done (#124 + #130);
  operator-side real rehearsal is an ops action, not a code gap.

- **#97 Prove tier 2** — 85% done. Rehearsal + visibility + hard gate +
  plan_id binding all landed (#127 + #133). Missing: install/smoke
  check (cargo install against the rehearsal registry / consumer
  build). One narrow follow-up PR closes it.

- **#98 Remediate** — 60% done. Receipt schema + plan-yank (from-receipt)
  + yank primitive + fix-forward planning all landed (#121 + #132 +
  #134). Missing: plan-yank's --starting-crate graph mode, plan
  execution for yank + fix-forward. Two narrow follow-ups.

Also captures the two review concerns on #122 (Trusted Publishing)
that were addressed in a follow-up commit to that PR.

Recommended next merge order and follow-up PRs spelled out at bottom.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant