# Stateless Execution Implementation Plan Status: Complete. ## Purpose This plan implements the accepted [Stateless Execution Roadmap](ephemeral-state.md). The roadmap is authoritative for product intent, policy choices, and the desired end state. This document records the completed migration and records the remediation work completed during post-implementation review. Stages 1-18 are complete and are summarized below. ## Implementation Rules 1. Keep the repository buildable and `go test ./...` passing after every stage. 2. Follow all policies under `docs/policy/`, especially the architecture, documentation, and risk-based testing requirements. 3. Preserve the accepted stateless contracts: no application-owned durable workspace, no local Recent Changes comparison, one operator-owned output per successful report, atomic publication, and explicit-only prompt debug capture. 4. Preserve prompt and profile inspection before weather collection, exact prompt versions, structured-output validation, repository-owned rendering, deterministic report periods, batch membership, and Distributor upload of published Markdown files. 5. Keep default tests deterministic and offline. Do not contact live Weather API, Promptkit provider, or Distributor services. 6. Use behavior-focused regression tests at the narrowest stable boundary. Avoid tests that merely encode private helper structure or call order. 7. Update canonical documentation in the same stage as any user-visible or architectural behavior change. Do not create release notes until a release version is selected. 8. Do not modify or reinterpret the Accepted stateless-execution ADR. A changed architectural decision would require a new ADR; the remaining work does not require one. ## Fixed Remediation Decisions The following decisions are complete and require no further product input: - A canceled generation must not publish or replace its selected output if cancellation is observable before atomic publication begins. - A batch must validate every planned report destination before starting any report's Promptkit execution or output publication. Dynamic Daily destinations may be validated after weather collection and batch planning, because those dates are not known earlier. - `BatchResult.Total`, `Succeeded`, and `Failed` count reports only. A batch notification failure changes overall batch status and exit behavior through the top-level notification result; it does not increment `Failed`. - Batch report items do not expose per-report notification fields because batch reports deliberately suppress per-report notification. - Completed roadmap prose must distinguish the former persistent architecture from current stateless behavior, and repository hygiene must no longer hide an accidentally recreated root-level `workspace` directory. ## Completed Stages ### Stage 1: Record The Stateless Architecture Decision — Complete Added the Accepted ADR recording the stateless transformation pipeline, operator-owned output boundary, removal of local comparison and historical inspection, atomic publication, and explicit debug-capture exception. ### Stage 2: Remove Recent Changes From The Prompt Contract — Complete Removed Recent Changes from prompt input, advanced the data package to `weatherreporter.data_package.v4`, and advanced all four embedded prompts to exact version `2.0.0`. ### Stage 3: Delete Dormant Local Comparison Policy — Complete Removed the local comparison implementation and `recent_change` configuration; strict configuration loading now rejects the obsolete field. ### Stage 4: Establish The Operator-Owned Output Contract — Complete Made every successful report publish one atomic Markdown output, added current-working-directory defaults and explicit destination overrides, and centralized report output naming. ### Stage 5: Separate Explicit Debug Capture From State — Complete Moved secure opt-in Promptkit diagnostics into `internal/promptdebug` without introducing implicit diagnostics or ordinary artifact persistence. ### Stage 6: Remove Notification Persistence — Complete Removed notification receipts and changed Distributor delivery to consume the published operator-owned output from the active workflow. ### Stage 7: Replace The Persisted Generation Workflow — Complete Converted single and batch generation to in-memory orchestration and removed ordinary persistence of snapshots, prompt packages, generated text, render contexts, metadata, and managed reports. ### Stage 8: Remove Historical Inspection And Prior Compatibility — Complete Removed the `inspect` command family, historical lookup, prior-run selection, and metadata compatibility surfaces. ### Stage 9: Delete The Workspace And State Subsystem — Complete Removed `internal/state`, workspace configuration, state-only helpers, and legacy artifact models. Strict loading rejects the obsolete `workspace` stanza. ### Stage 10: Consolidate Stateless Behavioral Coverage — Complete Replaced state-oriented fixtures with focused offline coverage of generation, batching, atomic output, partial success, notification ordering, prompt inspection, summaries, and explicit debug capture. ### Stage 11: Publish User, Operator, And Policy Documentation — Complete Updated the CLI, operations, architecture, configuration, and documentation policy owners for stateless operation and manual legacy-workspace cleanup. ### Stage 12: Reconcile Internal And Integration Documentation — Complete Updated focused internal and integration documents to describe in-memory orchestration, published-output notification sources, and the absence of historical inspection. ### Stage 13: Run The Repository Exit Gate — Complete Ran the complete offline test, race, vet, build, help, formatting, stale-string, and documentation review gates and marked the initial migration implemented. ## Stage 14: Prevent Publication After Cancellation — Complete ### Goal Close the cancellation window between successful Promptkit execution and atomic output publication. ### Work 1. In the report workflow, check the active context after validation and rendering have completed and immediately before calling the atomic file writer. 2. If `ctx.Err()` is `context.Canceled`, wrap it in a `promptexec.Canceled` error; if it is `context.DeadlineExceeded`, wrap it in a `promptexec.DeadlineExceeded` error. Return that classified error through the existing report-error surface. Preserve `errors.Is` behavior for the underlying context error; do not collapse either case into a generic rendering or output error. 3. Do not remove an output that was published before a later cancellation. Cancellation observed after publication remains subject to the existing notification and result behavior. 4. Add a focused app regression test whose fake executor returns valid output but cancels the context before returning. Prove that generation fails and a pre-existing destination remains byte-for-byte unchanged. This test must not depend on the fake executor voluntarily returning a cancellation error. 5. Ensure the same workflow check protects every single-report and batch item; do not add duplicate cancellation logic in CLI or batch orchestration. ### Tests Run: ```sh go test ./internal/app go test ./... go test -race ./internal/app git diff --check ``` ### Exit Gate Cancellation observable before publication prevents the atomic write, the selected destination remains unchanged, and cancellation identity is retained. ## Stage 15: Preflight Every Planned Batch Output — Complete ### Goal Ensure a structural destination error cannot appear midway through a batch after earlier reports have already been published. ### Work 1. After weather collection and `planBatchRun` have produced the complete dynamic report set, resolve and validate the output path for every planned report before executing the first report prompt. 2. Store each validated absolute output path with its planned report for use by the generation loop. Do not recompute or revalidate destinations inside the loop. 3. Treat any invalid path, including an existing directory at a report's final filename, as a batch preflight error. Return before Promptkit execution and before publication of any batch item. Weather collection may already have occurred because eligible Daily dates depend on collected coverage. 4. Keep missing parent-directory creation in the atomic publication helper; preflight must not create report files or introduce a new managed directory lifecycle. 5. Add a focused app test using a real temporary output directory where a later planned filename, such as `tomorrow.md`, already exists as a directory. Assert that prompt inspection still occurs before collection, the executor's prompt-execution method is never called, and no earlier report output is created or replaced. 6. Retain the existing independent report-failure behavior after successful preflight: a provider, validation, rendering, or publication failure for one report remains an item failure, later items continue, and successful outputs remain available. ### Tests Run: ```sh go test ./internal/app go test ./internal/fileutil go test ./... git diff --check ``` ### Exit Gate Every selected batch destination is validated before any batch report executes, and a destination collision cannot yield an unreported partial batch. ## Stage 16: Separate Report Counts From Batch Notification Status — Complete ### Goal Restore coherent batch counters while preserving failed status and non-zero exit behavior when the batch notification fails. ### Work 1. Define and enforce the invariant `Total == Succeeded + Failed == len(Reports)` after report execution. `Succeeded` and `Failed` count only report-item statuses. 2. Remove the increment of `BatchResult.Failed` when `notifyBatch` returns an error. Preserve the failed top-level `BatchNotificationResult`, including its safe error and identity fields. 3. Update the `RunBatch` wrapper to return `BatchError` when either a report failed or the top-level batch notification failed. Keep `RunBatchDetailed` returning the populated result according to its existing detailed-result contract. 4. Retain CLI behavior in which the batch summary status is failed and the command exits unsuccessfully for a batch notification failure, even though all report counters show success. 5. Make `BatchError` use report counts only for report-failure wording and its existing notification-specific wording when the reports succeeded but the notification failed. 6. Add focused app and CLI tests for a batch whose reports all publish successfully but whose batch notifier fails. Assert: - `Total == Succeeded == len(Reports)` and `Failed == 0`; - every output remains present; - the top-level notification status is `failed`; - the summary status is `failed`; - the returned error describes notification failure rather than claiming a report failed; and - the action exits unsuccessfully. ### Tests Run: ```sh go test ./internal/app ./internal/cli go test ./... git diff --check ``` ### Exit Gate Batch report counters are internally consistent, while notification failure still produces a failed summary, safe diagnostic, retained outputs, and non-zero command result. ## Stage 17: Remove Impossible Per-Report Notification State — Complete ### Goal Align batch result types and stderr output with the architecture in which per-report notification is suppressed and delivery is represented once at the batch level. ### Work 1. Remove `NotificationStatus`, `NotificationRunID`, `NotificationPipelineID`, and `NotificationError` from `BatchReportResult`. 2. Remove the unreachable copying of `ReportResult.Notification` into a batch item. Rename `copyBatchReportPaths` to reflect that it copies the current safe report result fields rather than only paths, or replace it with an equally clear narrow helper. 3. Remove per-report notification formatting from `writeBatchStatus`. Keep the top-level `batchNotification` status line and the batch summary `notification` object unchanged. 4. Search tests and documentation for the removed per-report fields. Delete stale assertions or descriptions rather than adding compatibility fields; no backward compatibility is required for this pre-release result cleanup. 5. Retain single-report notification fields and behavior. This stage changes only batch report items. ### Tests Run: ```sh go test ./internal/app ./internal/cli go test ./... git diff --check ``` ### Exit Gate Batch items expose only report-generation facts, and all batch delivery state is represented by the single top-level notification result. ## Stage 18: Reconcile Documentation And Run The Remediation Exit Gate — Complete ### Goal Remove the remaining documentation and repository-hygiene traces of the former workspace architecture and verify the remediated implementation as a whole. ### Work 1. In `docs/roadmap/ephemeral-state.md`, rename `Current State` to `Former State`, convert its description of persistence and Recent Changes to past tense, and reconcile other implementation-future phrasing with the roadmap's Implemented status. Preserve the roadmap's product intent and desired end state; do not turn it back into a staged plan. 2. Remove the root-level `/workspace` ignore rule from `.gitignore` and update its adjacent comment. Legacy workspace cleanup remains an explicit operator procedure in `docs/operations.md`; removing the ignore rule must not delete any operator data or add automated cleanup. 3. Review the canonical CLI, operations, architecture, app-orchestration, and testing documentation for the Stage 14-17 behavior. Update only documents whose owned contract changed, avoiding duplicate definitions. 4. Search production code, tests, examples, and current-state documentation for: - stale per-report batch notification fields; - report counters that include notification failures; - output validation performed inside the batch execution loop; - claims that cancellation can publish an output; - present-tense descriptions of the removed workspace; and - active ignore rules or defaults that conceal a workspace tree. 5. Confirm ordinary generation and batch tests use isolated temporary directories and leave only the selected Markdown outputs unless explicit prompt debug capture is requested. 6. Run formatting and all repository validation gates. Review the complete remediation diff for unrelated changes, content leakage, compatibility shims, and unnecessary abstractions. 7. When every exit gate passes, mark Stages 14-18 and this plan Complete. Keep the feature roadmap marked Implemented. Do not create, tag, or publish a release in this plan. ### Tests Run: ```sh git diff --name-only --diff-filter=ACM -- '*.go' | xargs -r gofmt -w go test ./... go test -race ./... go vet ./... go build ./... go run ./cmd/weatherreporter --help git diff --check ``` Verify all changed repository-relative documentation links resolve and inspect `git status --short` for an accidentally created root-level `workspace` tree. ### Exit Gate The remediated stateless workflow honors cancellation before publication, preflights complete batch destinations, reports coherent batch counts, exposes only reachable notification state, and has documentation and repository hygiene consistent with the implemented architecture. ## Open Questions None. The roadmap, accepted ADR, and remediation decisions above provide all product and architectural choices required to implement Stages 14-18.