Finish stateless execution cleanup
This commit is contained in:
@@ -17,9 +17,10 @@ whose recovery and compatibility obligations are disproportionate to the value
|
|||||||
of an ephemeral weather report. It also exists solely to support local Recent
|
of an ephemeral weather report. It also exists solely to support local Recent
|
||||||
Changes comparison for a rarely used report section.
|
Changes comparison for a rarely used report section.
|
||||||
|
|
||||||
The accepted [Stateless Execution Roadmap](../roadmap/ephemeral-state.md)
|
The temporary roadmap that defined the feature scope and implementation plan
|
||||||
defines the feature scope and implementation plan. This decision records the
|
has been retired under the repository's documentation lifecycle. The
|
||||||
durable rationale for the resulting architecture.
|
[architecture policy](../policy/architecture.md) defines the resulting system
|
||||||
|
invariants; this decision records their durable rationale.
|
||||||
|
|
||||||
## Decision
|
## Decision
|
||||||
|
|
||||||
|
|||||||
@@ -1,324 +0,0 @@
|
|||||||
# Stateless Execution Roadmap
|
|
||||||
|
|
||||||
Status: Implemented.
|
|
||||||
|
|
||||||
## Purpose
|
|
||||||
|
|
||||||
Weatherreporter should be a stateless report transformation pipeline. Each
|
|
||||||
invocation fetches current inputs from the Weather API, derives deterministic
|
|
||||||
facts, executes Promptkit, renders Markdown, and writes or distributes the
|
|
||||||
completed report. It does not retain application-owned history or operational
|
|
||||||
state between invocations.
|
|
||||||
|
|
||||||
Weather reports are ephemeral products rather than business records. Forecasts
|
|
||||||
and current conditions change continuously, and the normal response to an old
|
|
||||||
or failed report is to generate a new one rather than recover, replay, or
|
|
||||||
inspect the prior generation.
|
|
||||||
|
|
||||||
This roadmap defines the desired lifecycle, output contract, compatibility
|
|
||||||
policy, and architectural boundaries.
|
|
||||||
|
|
||||||
## User Intent
|
|
||||||
|
|
||||||
The target design reflects these product expectations:
|
|
||||||
|
|
||||||
- each invocation is independent and requires no prior Weatherreporter state;
|
|
||||||
- routine operation creates only the requested report outputs;
|
|
||||||
- reports are written somewhere useful even when the operator omits an
|
|
||||||
explicit output flag;
|
|
||||||
- Weatherreporter does not maintain forecast history merely to compare runs;
|
|
||||||
- regenerating replaces a same-named output atomically instead of creating an
|
|
||||||
archive;
|
|
||||||
- Distributor receives the completed report produced by the current
|
|
||||||
invocation; and
|
|
||||||
- sensitive diagnostics are retained only through an explicit,
|
|
||||||
operator-controlled debug option.
|
|
||||||
|
|
||||||
## Former State
|
|
||||||
|
|
||||||
Before the stateless implementation, each generation wrote a run-addressed collection containing a
|
|
||||||
module snapshot, data package, prompt preparation receipt, prompt execution
|
|
||||||
receipt, raw generated text, validated generated text, render context, managed
|
|
||||||
report, metadata, and optional notification receipt. Successful and failed
|
|
||||||
runs accumulated beneath the configured workspace.
|
|
||||||
|
|
||||||
Metadata linked these artifacts and supported lookup by RunID. The CLI could list
|
|
||||||
historical runs and inspect their metadata, modules, data packages, prior
|
|
||||||
snapshots, and source provenance. The reader retained V1 compatibility while
|
|
||||||
new runs wrote V2 metadata.
|
|
||||||
|
|
||||||
Weatherreporter also loaded a compatible prior module snapshot for Daily,
|
|
||||||
Today, and Tomorrow and compared it with the current snapshot. Configurable
|
|
||||||
thresholds determined whether structured Recent Changes were included in the
|
|
||||||
Promptkit data package. This was the only product behavior that depended on
|
|
||||||
state from an earlier invocation.
|
|
||||||
|
|
||||||
## Desired End State
|
|
||||||
|
|
||||||
Weatherreporter has no durable application-managed workspace. Its dataflow is:
|
|
||||||
|
|
||||||
```text
|
|
||||||
Weather API input
|
|
||||||
-> deterministic facts and modules
|
|
||||||
-> Promptkit data package and generated text
|
|
||||||
-> repository-owned Markdown rendering
|
|
||||||
-> operator-owned report output
|
|
||||||
-> optional Distributor upload
|
|
||||||
```
|
|
||||||
|
|
||||||
Intermediate values remain in memory wherever practical. A narrowly scoped
|
|
||||||
temporary file may be used when an external interface requires a file path,
|
|
||||||
but it exists only for the active invocation and is removed on return.
|
|
||||||
Temporary material is not a supported recovery or inspection surface.
|
|
||||||
|
|
||||||
RunIDs may remain as in-process correlation identifiers in action summaries,
|
|
||||||
errors, Distributor idempotency values, and explicit debug paths. They do not
|
|
||||||
identify a collection that Weatherreporter can locate or decode later.
|
|
||||||
|
|
||||||
Configuration files, operator-owned report outputs, and explicitly requested
|
|
||||||
debug captures are inputs or outputs, not Weatherreporter state.
|
|
||||||
|
|
||||||
## Report Output Contract
|
|
||||||
|
|
||||||
Every successful report generation writes one operator-owned Markdown output.
|
|
||||||
An explicit output option selects its destination. When no explicit destination
|
|
||||||
is supplied, Weatherreporter uses the process working directory captured at
|
|
||||||
invocation start.
|
|
||||||
|
|
||||||
Default single-report filenames are:
|
|
||||||
|
|
||||||
| Report | Default output |
|
|
||||||
| --- | --- |
|
|
||||||
| Daily | `daily-YYYY-MM-DD.md`, using the report's valid local date |
|
|
||||||
| Today | `today.md` |
|
|
||||||
| Tomorrow | `tomorrow.md` |
|
|
||||||
| Hourly | `hourly.md` |
|
|
||||||
|
|
||||||
`--out PATH` replaces the default destination for `generate`; it no longer
|
|
||||||
means an extra copy of a separately managed report. Relative paths are resolved
|
|
||||||
from the invocation's working directory.
|
|
||||||
|
|
||||||
For `run`, `--out-dir PATH` selects the report-output directory and the current
|
|
||||||
working directory is the default when it is omitted. Existing batch filenames
|
|
||||||
remain: `today.md`, `tomorrow.md`, and `daily-YYYY-MM-DD.md`. Successful items
|
|
||||||
in a partially failed batch retain their outputs.
|
|
||||||
|
|
||||||
Output publication is atomic. A completed generation may atomically replace an
|
|
||||||
existing file at the selected path, but a failed or canceled generation must
|
|
||||||
not truncate or partially replace it. Weatherreporter does not rotate, archive,
|
|
||||||
expire, or otherwise manage an output after publication.
|
|
||||||
|
|
||||||
The JSON action summary reports the final output path. It does not expose paths
|
|
||||||
to transient intermediates. `--quiet` continues to suppress the action summary,
|
|
||||||
not report creation.
|
|
||||||
|
|
||||||
## Distributor Contract
|
|
||||||
|
|
||||||
Distributor uploads use the completed Markdown output from the active
|
|
||||||
invocation. There is no separate durable managed-report copy and no workspace
|
|
||||||
scan.
|
|
||||||
|
|
||||||
For a single report, local output publication completes before notification.
|
|
||||||
A notification failure leaves the report output intact and returns a safe
|
|
||||||
error through the active result. It does not create a notification receipt.
|
|
||||||
|
|
||||||
Batches continue to suppress per-report notification and attempt their
|
|
||||||
batch-level upload only after every planned report succeeds. A report failure
|
|
||||||
leaves other successful output files intact, skips the batch upload, and
|
|
||||||
returns an aggregate failure. Distributor status remains active-workflow
|
|
||||||
information rather than retained Weatherreporter history.
|
|
||||||
|
|
||||||
## Recent Changes Deprecation And Removal
|
|
||||||
|
|
||||||
The local Recent Changes feature was deprecated by this accepted direction and
|
|
||||||
removed when the stateless execution change landed. Its low practical value did
|
|
||||||
not justify retaining a historical state subsystem.
|
|
||||||
|
|
||||||
Removal includes:
|
|
||||||
|
|
||||||
- prior-snapshot discovery and compatibility rules;
|
|
||||||
- the `internal/changes` comparison implementation;
|
|
||||||
- report-registry comparison strategies and compatible-prior declarations;
|
|
||||||
- `recent_change` configuration and threshold validation;
|
|
||||||
- Recent Changes values in application results, prompt inputs, fixtures, and
|
|
||||||
tests;
|
|
||||||
- the `recent_changes` Promptkit data-package stanza; and
|
|
||||||
- prompt instructions that refer to supplied recent changes.
|
|
||||||
|
|
||||||
Removing the prompt-input field was a breaking prompt contract change. The data
|
|
||||||
package schema advanced from `weatherreporter.data_package.v3` to
|
|
||||||
`weatherreporter.data_package.v4`, and all four prompt definitions advanced
|
|
||||||
from version `1.1.0` to `2.0.0` together. Generated-text schemas and report
|
|
||||||
templates did not change; neither had a direct dependency on Recent Changes.
|
|
||||||
|
|
||||||
Weatherreporter does not retain an empty compatibility field and does not keep
|
|
||||||
a local fallback comparator. Reports simply omit change commentary after this
|
|
||||||
feature is removed.
|
|
||||||
|
|
||||||
A future upstream Weather API change product may allow Weatherreporter to
|
|
||||||
reintroduce structured change commentary without local state. That work is
|
|
||||||
recorded in [Future Roadmap](future.md#upstream-forecast-change-product) and is
|
|
||||||
not a prerequisite for this refactor.
|
|
||||||
|
|
||||||
## Debugging
|
|
||||||
|
|
||||||
Prompt inspection before weather collection and prepared execution remain
|
|
||||||
runtime safety requirements. Preparation details, effective profile and model,
|
|
||||||
validation outcomes, source warnings, and safe errors remain available to the
|
|
||||||
active workflow and its action summary where useful.
|
|
||||||
|
|
||||||
The existing `--llm-debug-dir PATH` option remains the explicit exception to
|
|
||||||
stateless output. When supplied, it may retain rendered prompts, schemas, input
|
|
||||||
bodies, generated bodies, and effective parameters according to its secure
|
|
||||||
capture contract. The operator selects and manages that location;
|
|
||||||
Weatherreporter does not clean or archive it. Credentials remain excluded.
|
|
||||||
|
|
||||||
Adding a broader logging framework or an implicit persistent debug directory
|
|
||||||
is outside this feature. A future rename or generalization to `--debug` should
|
|
||||||
be considered separately so this state refactor does not expand the sensitive
|
|
||||||
capture contract incidentally.
|
|
||||||
|
|
||||||
## Removed State And Inspection Contracts
|
|
||||||
|
|
||||||
The implementation removed:
|
|
||||||
|
|
||||||
- the `workspace` configuration section and its directory settings;
|
|
||||||
- durable module snapshots, data packages, preparation and execution receipts,
|
|
||||||
raw and validated generated text, render contexts, run metadata, managed
|
|
||||||
reports, and notification receipts;
|
|
||||||
- historical artifact path fields from action results and summaries;
|
|
||||||
- V1 and V2 metadata models, readers, validators, and compatibility behavior;
|
|
||||||
- state lookup, prior selection, and run-history discovery; and
|
|
||||||
- `inspect reports`, `inspect metadata`, `inspect modules`,
|
|
||||||
`inspect data-package`, `inspect prior`, and `inspect sources`.
|
|
||||||
|
|
||||||
No replacement inspection command or current-state manifest is introduced.
|
|
||||||
The active action summary, final output, command error, and opt-in debug capture
|
|
||||||
are the supported diagnostic surfaces.
|
|
||||||
|
|
||||||
State-independent atomic file helpers remain outside a state abstraction. The
|
|
||||||
implemented architecture does not preserve `internal/state` merely as a
|
|
||||||
compatibility wrapper after its state responsibilities disappeared.
|
|
||||||
|
|
||||||
## Compatibility And Upgrade Policy
|
|
||||||
|
|
||||||
This was an intentional breaking change to the CLI, configuration, prompt-input,
|
|
||||||
workspace, and inspection contracts. Weatherreporter does not migrate old V1
|
|
||||||
or V2 metadata or other historical artifacts.
|
|
||||||
|
|
||||||
Legacy workspace trees are no longer ignored by the repository. Weatherreporter
|
|
||||||
must not discover, interpret, or automatically delete them. Release notes and
|
|
||||||
operations documentation should identify the obsolete configuration and
|
|
||||||
commands and provide a precise manual cleanup procedure for operators who want
|
|
||||||
to remove the legacy workspace.
|
|
||||||
|
|
||||||
Existing report files explicitly written through `--out` or `--out-dir` remain
|
|
||||||
operator-owned and are never treated as legacy workspace material.
|
|
||||||
|
|
||||||
Any release containing this implementation should identify the removed Recent
|
|
||||||
Changes behavior, configuration fields, inspection commands, artifact paths,
|
|
||||||
and metadata compatibility; the new default output destinations; and any
|
|
||||||
operator action required during upgrade.
|
|
||||||
|
|
||||||
## Required Architecture Decision Record
|
|
||||||
|
|
||||||
The implemented state includes an Accepted ADR recording the durable decision to
|
|
||||||
make Weatherreporter stateless.
|
|
||||||
|
|
||||||
The ADR records:
|
|
||||||
|
|
||||||
- the mismatch between run-addressed provenance storage and ephemeral weather
|
|
||||||
reports;
|
|
||||||
- the decision to remove local change detection rather than retain state for a
|
|
||||||
rarely used report section;
|
|
||||||
- the transformation-pipeline model and operator-owned output boundary;
|
|
||||||
- the absence of historical inspection and backward-compatibility guarantees;
|
|
||||||
- atomic output and failure behavior;
|
|
||||||
- the separation of explicit debug capture from ordinary execution;
|
|
||||||
- the upstream-service path for any future forecast comparison; and
|
|
||||||
- alternatives considered, including the former bounded-current-state design,
|
|
||||||
time-based retention, and a bounded run history.
|
|
||||||
|
|
||||||
Once accepted, the ADR owns the rationale. Architecture owns the resulting
|
|
||||||
stateless invariant, while CLI, operations, configuration, integrations, and
|
|
||||||
focused internal documents own implemented contracts.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
The completed feature includes:
|
|
||||||
|
|
||||||
- removal of local Recent Changes and all prior-run dependencies;
|
|
||||||
- removal of the durable workspace, artifact persistence, metadata
|
|
||||||
compatibility, and historical inspection surfaces;
|
|
||||||
- in-memory or invocation-temporary processing with safe cleanup;
|
|
||||||
- atomic operator-owned output for every successful report;
|
|
||||||
- current-working-directory defaults and explicit output overrides;
|
|
||||||
- preservation of report generation, batch membership, Promptkit execution,
|
|
||||||
Distributor delivery, action summaries, and opt-in secure debug capture;
|
|
||||||
- removal or simplification of state-only packages, fields, configuration,
|
|
||||||
fixtures, tests, and documentation;
|
|
||||||
- exact-version prompt updates for the breaking input-contract change;
|
|
||||||
- deterministic offline tests for output atomicity, failure isolation, default
|
|
||||||
path selection, Distributor sequencing, and absence of durable state;
|
|
||||||
- an Accepted ADR documenting the decision; and
|
|
||||||
- canonical documentation describing the implemented contract and clear
|
|
||||||
release-note requirements for the release that publishes it.
|
|
||||||
|
|
||||||
## Non-Goals
|
|
||||||
|
|
||||||
This feature does not include:
|
|
||||||
|
|
||||||
- the future upstream forecast-change API or reintroduced change commentary;
|
|
||||||
- a cache, database, archive, retention engine, manifest, or resume mechanism;
|
|
||||||
- migration or automatic deletion of legacy workspace artifacts;
|
|
||||||
- retaining a bounded report or snapshot history;
|
|
||||||
- changing weather derivation, report periods, report membership, generated
|
|
||||||
prose schemas, profile selection, or the provider model ladder;
|
|
||||||
- changing Distributor's remote API or bundle-content contract;
|
|
||||||
- deleting or managing operator-owned report and debug outputs;
|
|
||||||
- a general logging or observability subsystem; or
|
|
||||||
- live external services in the default test suite.
|
|
||||||
|
|
||||||
## Safety And Testing Policy
|
|
||||||
|
|
||||||
Tests should emphasize observable stateless behavior rather than removed file
|
|
||||||
choreography. Important risks requiring durable offline coverage include:
|
|
||||||
|
|
||||||
- a failed or canceled generation truncating or replacing an existing output;
|
|
||||||
- default paths resolving somewhere other than the invocation working
|
|
||||||
directory;
|
|
||||||
- Daily output using the wrong valid local date;
|
|
||||||
- a batch item overwriting another planned output;
|
|
||||||
- a notification attempt occurring before its report output is complete;
|
|
||||||
- one batch failure deleting or corrupting another report's successful output;
|
|
||||||
- summaries exposing nonexistent transient paths;
|
|
||||||
- temporary files surviving an ordinary success or handled failure;
|
|
||||||
- default operation recreating a workspace or historical artifact tree; and
|
|
||||||
- debug capture leaking credentials or being created without explicit opt-in.
|
|
||||||
|
|
||||||
Tests remain deterministic, offline, credential-free, and use real temporary
|
|
||||||
directories plus narrow external-boundary fakes. Existing tests whose only
|
|
||||||
purpose is to preserve removed state, metadata, inspection, or Recent Changes
|
|
||||||
contracts should be deleted rather than translated into assertions about
|
|
||||||
private replacement mechanics.
|
|
||||||
|
|
||||||
## Completion Criteria
|
|
||||||
|
|
||||||
The implemented state has these properties:
|
|
||||||
|
|
||||||
- a clean invocation requires no prior Weatherreporter-created files;
|
|
||||||
- ordinary runs leave only their selected Markdown outputs;
|
|
||||||
- omitted output flags resolve to the documented filenames in the current
|
|
||||||
working directory;
|
|
||||||
- failed and canceled attempts preserve any existing destination file;
|
|
||||||
- no local prior-run comparison or Recent Changes contract remains;
|
|
||||||
- no workspace configuration, run-history command, metadata compatibility, or
|
|
||||||
durable intermediate-artifact contract remains;
|
|
||||||
- Distributor uses only completed current-invocation outputs;
|
|
||||||
- debug files are created only through explicit secure capture;
|
|
||||||
- repeated successful and failed runs do not create application-owned history;
|
|
||||||
- the test suite proves the stateless, atomic-output, batch, notification, and
|
|
||||||
security boundaries offline;
|
|
||||||
- an Accepted ADR records the architectural decision; and
|
|
||||||
- canonical current-state documentation describes only the implemented
|
|
||||||
stateless lifecycle.
|
|
||||||
@@ -7,8 +7,8 @@ status; current behavior is documented outside `docs/roadmap/`.
|
|||||||
|
|
||||||
Status: Proposed upstream feature request; unimplemented.
|
Status: Proposed upstream feature request; unimplemented.
|
||||||
|
|
||||||
Weatherreporter's local Recent Changes feature is deprecated for removal by
|
Weatherreporter's local Recent Changes feature was removed by the accepted
|
||||||
the accepted [stateless execution roadmap](ephemeral-state.md). Forecast
|
[stateless execution decision](../adr/0001-stateless-execution.md). Forecast
|
||||||
version history and comparison are better owned by the Weather API, where the
|
version history and comparison are better owned by the Weather API, where the
|
||||||
underlying forecast issuances can be retained and compared consistently for
|
underlying forecast issuances can be retained and compared consistently for
|
||||||
all consumers.
|
all consumers.
|
||||||
|
|||||||
@@ -1,381 +0,0 @@
|
|||||||
# 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.
|
|
||||||
@@ -367,7 +367,7 @@ func RunBatchDetailed(ctx context.Context, req BatchRequest) (*BatchResult, erro
|
|||||||
result.Reports = append(result.Reports, item)
|
result.Reports = append(result.Reports, item)
|
||||||
}
|
}
|
||||||
result.Total = len(result.Reports)
|
result.Total = len(result.Reports)
|
||||||
batchNotification, _ := notifyBatch(ctx, req.Config, req.Batch, batchRunID(startedAt, req.Batch), startedAt, result, plannedReports, req.Notifier)
|
batchNotification := notifyBatch(ctx, req.Config, req.Batch, batchRunID(startedAt, req.Batch), startedAt, result, plannedReports, req.Notifier)
|
||||||
if batchNotification != nil {
|
if batchNotification != nil {
|
||||||
result.Notification = batchNotification
|
result.Notification = batchNotification
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,31 +46,31 @@ func batchRunID(startedAt time.Time, batch BatchKind) string {
|
|||||||
return startedAt.UTC().Format(runIDTimestampLayout) + "_" + string(batch)
|
return startedAt.UTC().Format(runIDTimestampLayout) + "_" + string(batch)
|
||||||
}
|
}
|
||||||
|
|
||||||
func notifyBatch(ctx context.Context, cfg config.Config, batch BatchKind, runID string, startedAt time.Time, result *BatchResult, planned []plannedBatchReport, notifier Notifier) (*BatchNotificationResult, error) {
|
func notifyBatch(ctx context.Context, cfg config.Config, batch BatchKind, runID string, startedAt time.Time, result *BatchResult, planned []plannedBatchReport, notifier Notifier) *BatchNotificationResult {
|
||||||
if !cfg.Notify.Distributor.Enabled {
|
if !cfg.Notify.Distributor.Enabled {
|
||||||
return nil, nil
|
return nil
|
||||||
}
|
}
|
||||||
if !cfg.Notify.Distributor.Batch.Enabled {
|
if !cfg.Notify.Distributor.Batch.Enabled {
|
||||||
return nil, nil
|
return nil
|
||||||
}
|
}
|
||||||
if result == nil {
|
if result == nil {
|
||||||
return nil, fmt.Errorf("batch result is required")
|
return failedBatchNotificationResult(batchNotificationRequest{}, fmt.Errorf("batch result is required"))
|
||||||
}
|
}
|
||||||
if result.Failed > 0 {
|
if result.Failed > 0 {
|
||||||
return &BatchNotificationResult{
|
return &BatchNotificationResult{
|
||||||
Status: "skipped",
|
Status: "skipped",
|
||||||
Reason: "one or more reports failed",
|
Reason: "one or more reports failed",
|
||||||
}, nil
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := buildBatchNotificationRequest(cfg, batch, runID, startedAt, result.Reports, planned)
|
req, err := buildBatchNotificationRequest(cfg, batch, runID, startedAt, result.Reports, planned)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return failedBatchNotificationResult(batchNotificationRequest{}, err), err
|
return failedBatchNotificationResult(batchNotificationRequest{}, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
batchNotifier, err := resolveBatchNotifier(cfg, notifier)
|
batchNotifier, err := resolveBatchNotifier(cfg, notifier)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return failedBatchNotificationResult(req, err), err
|
return failedBatchNotificationResult(req, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
notification, notifyErr := batchNotifier.NotifyBatch(ctx, req)
|
notification, notifyErr := batchNotifier.NotifyBatch(ctx, req)
|
||||||
@@ -82,9 +82,9 @@ func notifyBatch(ctx context.Context, cfg config.Config, batch BatchKind, runID
|
|||||||
if wrappedErr != nil {
|
if wrappedErr != nil {
|
||||||
batchResult.Status = "failed"
|
batchResult.Status = "failed"
|
||||||
batchResult.Error = wrappedErr.Error()
|
batchResult.Error = wrappedErr.Error()
|
||||||
return batchResult, wrappedErr
|
return batchResult
|
||||||
}
|
}
|
||||||
return batchResult, nil
|
return batchResult
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveBatchNotifier(cfg config.Config, notifier Notifier) (batchNotifier, error) {
|
func resolveBatchNotifier(cfg config.Config, notifier Notifier) (batchNotifier, error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user