Files
weatherreporter/docs/roadmap/implementation.md

16 KiB

Stateless Execution Implementation Plan

Status: Complete.

Purpose

This plan implements the accepted Stateless Execution Roadmap. 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:

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:

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:

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:

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:

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.