Wire the publisher's onPublished hook to lifecycle.MarkPublished so each
confirmed-201 publish immediately transitions that report pending->published,
completing cross-run de-duplication.
- internal/publish: add the narrow Marker seam (write half of lifecycle.Manager)
and WithMarkPublished(m) option. It sets onPublished to call m.MarkPublished on
each confirmed create; on a mark failure it logs loudly (naming the report and
the duplicate-next-run risk) and surfaces the error so the run is recorded
failed. Mirrors the existing PendingGetter read-half seam, so publish stays
host-testable and free of the Wasm-only storage backends.
- worker/scheduled_wasm.go: buildPublish now passes WithMarkPublished(manager);
the one Manager instance is both pending getter and marker. worker/main.go
untouched.
Partial-failure guarantee falls out of #14's seam: the hook runs only on a 201
and per-report failures are isolated, so successes leave the pending set and a
failed report stays pending and is retried next run without duplicating the
already-published ones.
Tests (host, real lifecycle.Manager over MemoryStore + mock issue creator):
all-succeed run marks all published and a second run creates no new issues;
partial failure retries only the failed report next run without duplicating the
rest; MarkPublished is idempotent on an already-published report; and the
mark-failure edge case is surfaced, logged, and (honestly) re-publishes once.
Closes#15
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>