Files
weatherreporter/docs/roadmap/implementation.md

382 lines
16 KiB
Markdown

# 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.