Files
paperclip/server/src/__tests__/issue-continuation-summary.test.ts
T
Devin Foley a72731f118 fix: harden release registry verification against npm lag (#4816)
## Thinking Path

> - Paperclip orchestrates AI agents for zero-human companies
> - Its release automation publishes canary packages to npm and then
validates the published registry state before considering the release
healthy
> - The failing canary run `25139465018` showed that npm can expose a
newly published version through version-specific endpoints before the
root package document has fully converged
> - That made a successful canary publish look like a failed release
because the verifier trusted stale root metadata too early
> - This pull request hardens the registry verification path by
preferring version-specific manifest checks, retrying
convergence-sensitive failures, and distinguishing permanent failures
from propagation lag
> - While validating that change in CI, a separate teardown race in
`heartbeat-stale-queue-invalidation.test.ts` surfaced and was hardened
so the PR could pass reliably
> - The benefit is that transient npm propagation lag no longer fails a
successful canary publish, while genuine registry-state and
dependency-integrity failures still stop the release flow promptly

## What Changed

- Hardened `scripts/verify-release-registry-state.mjs` so it prefers
version-specific manifest resolution over stale root metadata, adds
bounded registry-fetch timeouts, and classifies failures as retriable vs
non-retriable.
- Updated `scripts/release-lib.sh` and `scripts/release.sh` so
post-publish registry verification retries only convergence-sensitive
failures and reports immediate permanent failures clearly.
- Expanded `scripts/verify-release-registry-state.test.mjs` with
regression coverage for stale root metadata, fetch timeout behavior,
peer dependency range handling, non-retriable canary-latest cases, and
related verifier edge cases.
- Hardened
`server/src/__tests__/heartbeat-stale-queue-invalidation.test.ts`
teardown to tolerate the late-comment foreign-key race that CI exposed
while validating this branch.

## Verification

- `pnpm run test:release-registry`
- `node --check scripts/verify-release-registry-state.mjs`
- `bash -n scripts/release.sh && bash -n scripts/release-lib.sh`
- PR checks passed on head `5c422600fc12acac61f6b7c267a4dc915df622b1`:
`policy`, `verify`, `e2e`, `security/snyk`, and `Greptile Review`

## Risks

- Low risk. The main behavioral changes are limited to release
automation and verifier retry semantics, plus a test-only teardown
hardening for a CI race.

> I checked [`ROADMAP.md`](ROADMAP.md). This is a narrow release bugfix
and does not overlap planned core feature work.

## Model Used

- OpenAI Codex via Paperclip `codex_local` with tool use and local code
execution enabled. This agent session runs on a GPT-5-class coding
model; the exact backend model ID/context window is not exposed by the
local adapter runtime.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [ ] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I have addressed all Greptile and reviewer comments before
requesting merge
2026-05-09 22:18:12 -07:00

116 lines
3.7 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
ISSUE_CONTINUATION_SUMMARY_MAX_BODY_CHARS,
buildContinuationSummaryMarkdown,
continuationSummaryParksExecutor,
extractContinuationSummaryNextAction,
} from "../services/issue-continuation-summary.js";
describe("issue continuation summaries", () => {
it("builds bounded issue-local handoff context with required sections", () => {
const body = buildContinuationSummaryMarkdown({
issue: {
id: "issue-1",
identifier: "PAP-1579",
title: "Add continuation summaries",
description: [
"## Objective",
"",
"Keep work resumable after adapter session reset.",
"",
"## Acceptance Criteria",
"",
"- Summary is issue-local",
"- Wake context includes the summary",
].join("\n"),
status: "in_progress",
priority: "medium",
},
run: {
id: "run-1",
status: "succeeded",
error: null,
resultJson: {
summary: "Updated server/src/services/heartbeat.ts and packages/adapter-utils/src/server-utils.ts.",
},
stdoutExcerpt: null,
stderrExcerpt: null,
finishedAt: new Date("2026-04-18T12:00:00.000Z"),
},
agent: {
id: "agent-1",
name: "CodexCoder",
adapterType: "codex_local",
},
});
expect(body).toContain("# Continuation Summary");
expect(body).toContain("## Objective");
expect(body).toContain("Keep work resumable after adapter session reset.");
expect(body).toContain("## Acceptance Criteria");
expect(body).toContain("- Summary is issue-local");
expect(body).toContain("## Recent Concrete Actions");
expect(body).toContain("Run `run-1` finished with status `succeeded`");
expect(body).toContain("`server/src/services/heartbeat.ts`");
expect(body).toContain("## Commands Run");
expect(body).toContain("## Blockers / Decisions");
expect(body).toContain("## Next Action");
expect(body.length).toBeLessThanOrEqual(ISSUE_CONTINUATION_SUMMARY_MAX_BODY_CHARS);
});
it("uses failure state to point the next run at the error", () => {
const body = buildContinuationSummaryMarkdown({
issue: {
id: "issue-1",
identifier: "PAP-1579",
title: "Add continuation summaries",
description: null,
status: "in_progress",
priority: "medium",
},
run: {
id: "run-2",
status: "failed",
error: "adapter failed",
errorCode: "adapter_failed",
resultJson: null,
},
agent: {
id: "agent-1",
name: "CodexCoder",
adapterType: "codex_local",
},
});
expect(body).toContain("Latest run error (adapter_failed): adapter failed");
expect(body).toContain("Inspect the failed run, fix the cause");
});
it("detects continuation summaries that explicitly park executor work for review", () => {
const body = [
"# Continuation Summary",
"",
"## Next Action",
"",
"- Wait for reviewer feedback or approval before continuing executor work.",
].join("\n");
expect(extractContinuationSummaryNextAction(body)).toBe(
"Wait for reviewer feedback or approval before continuing executor work.",
);
expect(continuationSummaryParksExecutor(body)).toBe(true);
});
it("does not park executor work when the next action is still runnable", () => {
const body = [
"# Continuation Summary",
"",
"## Next Action",
"",
"- Re-check run `25145432006`, then move the issue to `in_review` if the final step is green.",
].join("\n");
expect(continuationSummaryParksExecutor(body)).toBe(false);
});
});