660 lines
37 KiB
Markdown
660 lines
37 KiB
Markdown
# Conversation Continuation And Execution Groups Implementation Plan
|
|
|
|
## Objective And Sprint Rules
|
|
|
|
Implement the accepted [feature roadmap](conversation-continuation.md), using
|
|
[Audita's request](audita-request.md) as consumer evidence. The feature roadmap
|
|
owns product behavior and boundaries; this plan selects the API and work order.
|
|
Existing structural-output repair remains a separate, unchanged capability.
|
|
|
|
Each numbered stage is one Luna-sized implementation prompt. Execute them in
|
|
order during one end-to-end sprint. Intermediate stages need not compile or
|
|
constitute independently releasable changes. Do not build compatibility shims or
|
|
temporary implementations solely to make a stage green. Carry a concise record
|
|
of remaining wiring into the next stage; the final stage must leave the whole
|
|
repository coherent and validated. There are no intermediate human-review,
|
|
external-publication, provider-access, or dependency-release gates.
|
|
|
|
Start with [development guidance](../development.md). Follow the applicable
|
|
[agent](../policy/agents.md), [architecture](../policy/architecture.md),
|
|
[testing](../policy/testing.md), and [documentation](../policy/documentation.md)
|
|
policies. Preserve the accepted [configuration boundary](../adr/0001-configuration-boundary.md).
|
|
This sprint needs no configuration format, dependency, Go minimum, release
|
|
tooling, or licensing changes. It does not authorize tagging or publication.
|
|
|
|
Use real fast internal collaborators and replace only external boundaries in
|
|
tests. Select focused checks during stages and run the complete checks at the
|
|
end. Do not duplicate existing retry, repair, schema, or capacity test matrices.
|
|
Protect the new interactions with those mechanisms.
|
|
|
|
## Current Baseline And Integration Points
|
|
|
|
- The root facade in `engine.go` converts public requests, masks inherited
|
|
observation state, invokes the internal engine, maps errors, and finishes
|
|
observations after the runner releases admission. Preserve this boundary.
|
|
- `internal/usecase/runner.go` has shared resolution and completion preparation
|
|
phases. `completePreparationWithStructuredOutput` currently combines rendered
|
|
definition messages with appended messages and hashes the resulting prompt.
|
|
`executePreparedRun` is shared by ordinary and prepared execution and returns
|
|
the final `RawOutput`, including after exhausted structural repair.
|
|
- `internal/usecase/prepared_execution.go` retains a single-claim payload,
|
|
validation plan, direct credential, retry budget, and observer separately from
|
|
durable prepared details. Claims remain bound to the originating execution
|
|
owner. Details survive execution and discard.
|
|
- `internal/domain/message.go` owns message cloning and cache-control ownership.
|
|
Reuse it rather than inventing a second normalization contract.
|
|
- `observations.go`, `internal/domain/observation.go`, and
|
|
`internal/observation/recorder.go` own value-only events, run IDs, terminal
|
|
counters, and presence-aware usage. Terminal observations already include
|
|
completed work preceding operational failure. Do not total result usage or
|
|
attempt events on top of terminal totals.
|
|
- `internal/engine/engine.go` applies namespace views to new caller selections.
|
|
Captured history requires no original-engine lookups or namespace rewriting.
|
|
- Root conversion, `json.go`, formatting, and contract tests must account for
|
|
additions without changing existing timing, credential, or error behavior.
|
|
|
|
## Selected Public API
|
|
|
|
Implement these names and shapes in the root `promptkit` package. Code below
|
|
specifies additions and complete new types, not replacement definitions for
|
|
existing types. Keep all implementation machinery under `internal/`.
|
|
|
|
### Capture, Continuation, And Ancestry
|
|
|
|
```go
|
|
// Add to RunRequest.
|
|
CaptureExchange bool
|
|
ContinueFrom *Exchange `json:"-" yaml:"-"`
|
|
|
|
// Add to RunResult.
|
|
Exchange *Exchange `json:"-" yaml:"-"`
|
|
ParentRunID string `json:"parent_run_id,omitempty"`
|
|
|
|
// Add to PreparedRun.
|
|
ParentRunID string `json:"parent_run_id,omitempty"`
|
|
|
|
// Add to ExecutionEvent, following its existing field/tag style.
|
|
ParentRunID string
|
|
|
|
// Exchange has private state only.
|
|
type Exchange struct { /* private immutable snapshot reference */ }
|
|
|
|
type ExchangeDetails struct {
|
|
RunID string `json:"run_id"`
|
|
ParentRunID string `json:"parent_run_id,omitempty"`
|
|
Messages []RenderedMessage `json:"messages"`
|
|
}
|
|
|
|
func (e *Exchange) Details() ExchangeDetails
|
|
```
|
|
|
|
- `Run` with `CaptureExchange: true` returns `result.Exchange`. A non-nil
|
|
`ContinueFrom` supplies history and implies capture, regardless of the Boolean.
|
|
No separate continuation execution method is necessary.
|
|
- `Prepare`, `ValidateRequest`, and `PrepareExecution` accept the same request
|
|
fields and assemble the same effective messages. They create no completed
|
|
exchange. `PrepareExecution` retains capture selection and ancestry privately
|
|
for `RunPrepared`; plain preparation only returns its ordinary metadata.
|
|
- Nil `ContinueFrom` means no history. A non-nil, zero-value exchange is not a
|
|
captured exchange and fails as `ErrInvalidRequest` before source/model work.
|
|
`Details` on a nil or zero-value exchange returns a zero details value,
|
|
matching the prepared-handle accessor pattern.
|
|
- Details contain the complete logical history ending in the final assistant
|
|
content. They are detached and encodable with ordinary encoders. Give the
|
|
opaque handle constant `String` and `GoString` representations, following
|
|
`PreparedExecution`. Serializing ordinary results does not serialize the
|
|
capability or automatically disclose the additional conversation history;
|
|
callers explicitly encode `Details()` when desired. Do not add deserialization,
|
|
persistence, reconstruction, lookup, or lifecycle APIs.
|
|
- `ParentRunID` is the immediate source exchange's originating run ID, or empty
|
|
for initial runs. Expose it in prepared details, completed results, exchange
|
|
details, and ordinary observation events. Reuse the run ID already assigned to
|
|
the execution. It is metadata, not a provider request parameter, session ID,
|
|
group ID, or new source of execution settings.
|
|
- The follow-up's `PromptID`, definition hash, selected profile/backend, and input
|
|
hashes describe the new request. The rendered-prompt hash covers its complete
|
|
effective message sequence and selected session, using the existing hash
|
|
function. Do not hash capture flags or run identities into content equality.
|
|
|
|
### Optional Coordinator
|
|
|
|
```go
|
|
type ConversationTurn struct {
|
|
Exchange *Exchange
|
|
Result *RunResult
|
|
RemainingTurns int
|
|
}
|
|
|
|
type CoordinateRequest struct {
|
|
Start *Exchange
|
|
AdditionalTurns int
|
|
Next func(context.Context, ConversationTurn) (*RunRequest, error)
|
|
Observer ExecutionObserver
|
|
}
|
|
|
|
type CoordinationOutcome string
|
|
|
|
const (
|
|
CoordinationStopped CoordinationOutcome = "stopped"
|
|
CoordinationExhausted CoordinationOutcome = "exhausted"
|
|
)
|
|
|
|
type CoordinationResult struct {
|
|
Outcome CoordinationOutcome `json:"outcome,omitempty"`
|
|
Undelivered *RunResult `json:"undelivered,omitempty"`
|
|
}
|
|
|
|
func (e *Engine) Coordinate(ctx context.Context, req CoordinateRequest) (CoordinationResult, error)
|
|
```
|
|
|
|
- `Next` returning `(nil, nil)` stops. A non-nil request asks to continue; an
|
|
error stops with that error. No separate next-action enum or domain-outcome
|
|
taxonomy is needed. A zero request pointed to by a non-nil pointer is still
|
|
a continuation request, subject to ordinary validation if execution starts.
|
|
- `Start` must hold a captured exchange, `Next` must be non-nil, and
|
|
`AdditionalTurns` must be nonnegative; invalid values match `ErrInvalidRequest`.
|
|
A nil/uninitialized engine follows the existing `ErrInvalidConfig` convention.
|
|
Zero is a useful budget, not a default that expands into extra turns.
|
|
- Initial callback state has `Exchange: Start`, `Result: nil`, and the full
|
|
remaining budget. Later states contain the newly completed result and exchange
|
|
with the remaining start budget, including zero after the last permitted run.
|
|
The initial exchange is not presented as a newly delivered response.
|
|
- The coordinator copies each selected request value and sets its `ContinueFrom`
|
|
to the current exchange. It owns this one branch's progression, so a callback's
|
|
`ContinueFrom` does not redirect that branch. The rest of the request uses
|
|
ordinary rules. Callers wanting independent branches use `Run` themselves.
|
|
- Compose the request's observer first and the coordinator's observer second.
|
|
The latter selects all continuation runs, not the starting run or nested
|
|
callback calls. Applications explicitly compose additional group observers.
|
|
- On error leave `Outcome` empty. Set `Undelivered` only for a completed new
|
|
response withheld from callback invocation because cancellation was observed.
|
|
That result includes its exchange. A callback that was invoked has received
|
|
its response even if it errors or cancels. Return no result archive, latest
|
|
delivered response, failure-stage taxonomy, or coordinator accounting totals.
|
|
- Callbacks receive caller-owned public values. Retain the coordinator's current
|
|
immutable exchange separately, so replacing fields in delivered values cannot
|
|
rewrite retained history or the next continuation's ancestry.
|
|
|
|
### Observer Composition And Execution Groups
|
|
|
|
```go
|
|
func ComposeObservers(observers ...ExecutionObserver) ExecutionObserver
|
|
|
|
// Zero value ready for use; contains private synchronization/state only.
|
|
type ExecutionGroup struct { /* private collector */ }
|
|
|
|
func (g *ExecutionGroup) Observe(event ExecutionEvent)
|
|
func (g *ExecutionGroup) Snapshot() ExecutionGroupSummary
|
|
|
|
type ExecutionGroupMember struct {
|
|
RunID string `json:"run_id"`
|
|
TerminalObserved bool `json:"terminal_observed"`
|
|
}
|
|
|
|
type ExecutionGroupSummary struct {
|
|
Members []ExecutionGroupMember `json:"members"`
|
|
TerminalCoverageComplete bool `json:"terminal_coverage_complete"`
|
|
Attempts uint64 `json:"attempts"`
|
|
TransientRetries uint64 `json:"transient_retries"`
|
|
RepairCalls uint64 `json:"repair_calls"`
|
|
Usage ObservedTokenUsage `json:"usage"`
|
|
}
|
|
```
|
|
|
|
- Attach a group with `Observer: group.Observe`, or use `ComposeObservers` for
|
|
diagnostics and multiple groups. No request group field, implicit context
|
|
membership, constructor, registry, close, seal, or wait method is needed.
|
|
- Composition copies the supplied function slice, skips nil functions, and
|
|
invokes the remainder synchronously in supplied order. With no non-nil
|
|
functions return nil. Do not deduplicate functions or recover panics; a panic
|
|
prevents delivery to later observers. Closures retain their ordinary identity.
|
|
- Groups enroll a run when any ordinary event for that run is observed. The
|
|
first terminal event seen for that identity contributes counters and usage
|
|
exactly once, including when it is the first observed event. Repeated delivery
|
|
must not change totals or regress terminal coverage. Handling contradictory
|
|
caller-fabricated events is not a new public validation contract.
|
|
- Summary counters and token subtotals use terminal observations only. Pending
|
|
runs contribute no invented attempt counts or known zero usage. Members without
|
|
terminals remain identifiable. `TerminalCoverageComplete` says only that every
|
|
currently observed member has a terminal observation; it says nothing about
|
|
future members or application completion.
|
|
- For each token field, sum known terminal values; `Known` is the OR of terminal
|
|
`Known` flags and `Complete` is the AND of terminal `Complete` flags. These flags
|
|
describe received terminal subtotals; missing terminal coverage is separate.
|
|
If members exist but none has a terminal, usage is unknown/incomplete. An empty
|
|
group has complete terminal coverage and known, complete zero totals. Preserve
|
|
explicit provider zero and existing per-run failure-path usage semantics.
|
|
- Snapshots are detached and internally consistent under concurrent observation
|
|
and reads. There is no promised member ordering; tests compare by identity.
|
|
The mutex-bearing group must not be copied after first use, as with ordinary
|
|
synchronized Go values. Overlapping group totals are not generally additive.
|
|
|
|
## Internal Implementation Decisions
|
|
|
|
### Immutable History And Execution-Owned Messages
|
|
|
|
Put the internal immutable exchange value and history operations in
|
|
`internal/domain/exchange.go`. The public handle privately references this value;
|
|
internal packages never import the root facade. Retain only history and the two
|
|
run identities, not engine owners, definitions, requests, credentials, validation
|
|
plans, results, admission leases, or observer/callback closures.
|
|
|
|
Use a simple immutable parent-plus-turn representation: a parent exchange,
|
|
detached new-turn messages, the final assistant message, and a cached total
|
|
message count. Initial exchanges have no parent. New exchanges retain only their
|
|
new suffix rather than another flat copy of all ancestors. Flatten iteratively
|
|
into one correctly sized, caller/execution-owned destination when preparing a
|
|
provider request or returning details; do not recurse through arbitrary depth.
|
|
Reuse message/cache-control clone helpers at mutable boundaries. This is an
|
|
internal choice, not a public allocation or asymptotic guarantee. Avoid unrelated
|
|
history caches, ropes, eviction, limits, or pooling.
|
|
|
|
Prepare the new prompt normally, then assemble history, new definition messages,
|
|
and appended messages in the accepted order before hashing. Captured history
|
|
is not normalized or rendered again. Save capture/parent state separately in the
|
|
private prepared operation/payload. Prepared metadata contains `ParentRunID` and
|
|
the already assembled messages, not the exchange capability or capture settings.
|
|
Pass the private capture state through the shared execution function. After
|
|
successful final validation processing, capture the new-turn suffix from the
|
|
executed snapshot and the exact final `RawOutput`; failed structural validation
|
|
is still a completed result. Do not capture internal repair instructions or
|
|
intermediate candidates. On an operational error returning no result, create no
|
|
exchange. Do not change underlying execution cancellation or partial-result
|
|
rules to recover an assistant message.
|
|
|
|
### Coordinator Placement And Boundary Adapters
|
|
|
|
Put the loop in `internal/usecase/coordinate.go`, not in root business logic.
|
|
Use one small internal function generic over request/result value types, with
|
|
typed callback and execution functions and a `*domain.Exchange` current state.
|
|
Its internal turn/termination values contain only the fields projected by the
|
|
public API. This lets the facade inject ordinary `Engine.Run` and the application
|
|
callback without importing facade types internally or converting an entire run
|
|
result to domain and back just for delivery. No generic public API is added.
|
|
|
|
The execution adapter accepts `(context, currentExchange, selectedRequest)` and
|
|
returns `(completedResult, nextExchange, error)`. It creates a fresh public
|
|
exchange wrapper, copies the request, supplies the current history and composed
|
|
observer, and calls `e.Run`. Obtain the next internal exchange before delivering
|
|
the result to application code. The callback adapter projects a fresh public
|
|
wrapper and the current result into `ConversationTurn`. The shared loop does not
|
|
inspect results, interpret output, or access generation/capacity directly.
|
|
|
|
Use this exact progression:
|
|
|
|
1. With no completed response pending initially, check context before callback.
|
|
2. Invoke `Next` with the current exchange, pending result if any, and remaining
|
|
budget. Invocation marks that pending result delivered.
|
|
3. Check context again; cancellation wins over the callback's action/error.
|
|
Otherwise return a callback error unchanged, stop on nil request, or continue.
|
|
4. If a request was selected with no remaining budget, return exhaustion before
|
|
converting/preparing that request. Otherwise check context before execution,
|
|
consume one start from the budget, and call the execution adapter.
|
|
5. Return an execution error unchanged; retain no partial output. On a completed
|
|
result, update the private current exchange and repeat from step 1.
|
|
|
|
At step 1, cancellation with a pending new completed response returns that
|
|
single response in `Undelivered`; cancellation before the initial callback
|
|
returns none. Callback errors and cancellation observed after invocation return
|
|
no undelivered response. Panics propagate naturally without recovery. These
|
|
context checks do not forcibly interrupt callbacks or eliminate races.
|
|
|
|
Calling ordinary `Run` per turn ensures its logical admission and terminal
|
|
observation finish before `Next`. Do not wrap the whole coordinator in an
|
|
admission lease or an execution recorder. Nested calls remain independent.
|
|
|
|
### Group Collector Placement And Cost
|
|
|
|
Implement the collector in `internal/observation/group.go`, using domain events
|
|
and summary values. The root group is a thin adapter with private internal state;
|
|
root summary/member types are independent public declarations, not aliases.
|
|
Store a map keyed by run identity, a terminal-seen bit per member, and aggregate
|
|
terminal counters/usage under one mutex. Do not retain full event histories,
|
|
provider content, or per-attempt arrays. Event updates use map lookup rather than
|
|
rescanning all members. Snapshot copies the map's members and totals under the
|
|
lock without invoking callbacks. Observer composition is also internal logic
|
|
with thin root conversion; do not round-trip every event through both public and
|
|
domain representations simply to fan out callbacks.
|
|
|
|
## Stage 1 — Implement Immutable Exchange Storage
|
|
|
|
**Status:** Complete.
|
|
|
|
**Scope:** internal history value and ownership only.
|
|
|
|
1. Add the internal exchange representation and construction/flattening helpers
|
|
described above. Keep fields private and expose only the internal accessors
|
|
needed for run identities, message count, capture, and detached details.
|
|
2. Clone only new mutable suffix data when capturing; safely share immutable
|
|
parents. Preserve all role, content, empty-content, and cache metadata values.
|
|
3. Add focused internal domain tests for initial history, a second turn, two
|
|
independent branches, and mutation of source messages/cache control and
|
|
returned copies. Include a many-turn flattening case without asserting a
|
|
specific private layout or allocation count.
|
|
|
|
**Validation:** `go test ./internal/domain` when the package is buildable.
|
|
These tests own focused history behavior; the final suite verifies its assembly.
|
|
|
|
## Stage 2 — Add Public Handles And History-Aware Preparation
|
|
|
|
**Status:** Complete; capture state is carried through execution.
|
|
|
|
**Scope:** facade request conversion and the shared preparation path; generation
|
|
capture is the next stage.
|
|
|
|
1. Add `Exchange`, `ExchangeDetails`, their accessor and opaque formatting, the
|
|
request fields, and the prepared/result/observation ancestry fields selected
|
|
above. Add private domain request and prepared-operation capture state.
|
|
2. Extend `toDomainRunRequest` to translate a valid handle and capture choice.
|
|
Reject non-nil empty handles as invalid requests; do not impose engine binding
|
|
on exchanges. Preserve existing appended-message validation and copying.
|
|
3. Update completion to prepend captured messages before newly rendered messages
|
|
and caller suffixes. Compute hashes only after final assembly, leaving the
|
|
follow-up's settings, session, profile selection, and schema rules ordinary.
|
|
4. Update `PreparedRun` conversion and JSON behavior for optional `ParentRunID`.
|
|
Keep capture/private history references out of returned prepared details.
|
|
`ValidateRequest` inherits this through its existing preparation delegation.
|
|
5. Extend preparation contract coverage to assert exact order, roles, cache
|
|
metadata, hash sensitivity to prior history, and unchanged definition hashes.
|
|
Check nil/empty handle behavior and that preparation performs no model work.
|
|
|
|
**Validation:** focused root preparation/JSON tests and affected use-case tests.
|
|
Do not add a separate preparation or validation algorithm for continuation.
|
|
|
|
## Stage 3 — Capture Ordinary And Prepared Executions
|
|
|
|
**Status:** Complete.
|
|
|
|
**Scope:** completed-result creation and prepared payload lifetime.
|
|
|
|
1. Carry capture/parent state through `preparedOperation` and
|
|
`preparedExecutionPayload` to the shared `executePreparedRun` path. Keep it
|
|
out of the retained details object so discard can release execution-only
|
|
references. Preserve claim ownership, validation-plan freezing, direct-key
|
|
clearing, observer identity, and retry budgets.
|
|
2. Create the exchange from the request actually executed and exact final response
|
|
after the ordinary execution has a result. Initial capture is opt-in;
|
|
continuation always captures. Attach it and `ParentRunID` in result conversion.
|
|
3. Capture final failed validation/repair exhaustion as well as valid results.
|
|
On generation, validation-operation, or repair-generation errors returning no
|
|
result, create no exchange and leave the source exchange reusable.
|
|
4. Keep `RunResult.Exchange` excluded from ordinary JSON/YAML and preserve existing
|
|
result timing marshal/unmarshal behavior. Details are the explicit content
|
|
encoding path. The exchange's private state stores no execution target,
|
|
credential, result archive, callback, or lease.
|
|
5. Add focused tests for default no-capture, ordinary capture, prepared capture,
|
|
discarded/failed-claim silence, final repaired content without repair chatter,
|
|
failed validation capture, and operational-error absence of a new exchange.
|
|
Reuse existing prepared lifecycle and mutating-client fixtures.
|
|
|
|
**Validation:** targeted root capture, prepared-execution, and repair tests plus
|
|
`go test ./internal/usecase` as the touched package becomes coherent.
|
|
|
|
## Stage 4 — Propagate Ancestry Through Ordinary Observations
|
|
|
|
**Status:** Complete.
|
|
|
|
**Scope:** immediate-parent metadata without accounting changes.
|
|
|
|
1. Add parent metadata to the internal recorder's event construction and the root
|
|
event projection. Set it before ordinary start delivery and before a prepared
|
|
start after successful claim. Preserve it when selection identities update.
|
|
A small internal setter before `Start` is sufficient; do not add context-based
|
|
ancestry inheritance or new run IDs.
|
|
2. Propagate parent identity through start, attempt, retry-delay, and terminal
|
|
events. Initial runs and unrelated nested calls keep an empty parent. Preserve
|
|
terminal error classification and public error identities.
|
|
3. Cover observed ordinary/prepared continuation, preparation silence, operational
|
|
failure after continuation selection, and an independent nested call using
|
|
existing observation fixtures. Verify no history/content enters events.
|
|
|
|
**Validation:** focused observation and prepared-observation contract tests and
|
|
`go test ./internal/observation ./internal/generation`.
|
|
|
|
## Stage 5 — Verify Independent Continuation At The Public Boundary
|
|
|
|
**Status:** Complete.
|
|
|
|
**Scope:** assembled continuation behavior before adding coordination.
|
|
|
|
1. Add an external-package contract test that captures an initial exchange and
|
|
executes two branches, then continues one branch again. Assert exact outgoing
|
|
messages and immediate ancestry without changing the initial or sibling data.
|
|
2. Continue the exchange through another engine, including a scoped view. Select
|
|
different follow-up prompt/profile/output settings and session rules, and
|
|
verify that neither previous settings nor original direct credentials leak
|
|
into the new execution. Use synthetic credentials, not real environment data.
|
|
3. Remove or alter original artifact files after initial capture; continuation
|
|
still uses captured history. New follow-up inputs retain ordinary reads and
|
|
errors. Exercise prepared follow-up freezing separately from exchange reuse.
|
|
4. Mutate details and received result fields and use a mutating injected client
|
|
to prove ownership. Encode details generically and check exact final assistant
|
|
content, including empty/whitespace content when the output contract allows it.
|
|
5. Verify overlapping continuation runs can share the source exchange under the
|
|
race detector. Compare behavior, not a required scheduling order.
|
|
|
|
**Validation:** focused root continuation/capture tests under `-race`. This stage
|
|
must exercise ordinary shared preparation/execution rather than a mock history
|
|
reconstruction implemented by the test itself.
|
|
|
|
## Stage 6 — Implement The Internal Sequential Coordinator
|
|
|
|
**Status:** Complete.
|
|
|
|
**Scope:** the small generic loop and its deterministic state-machine tests.
|
|
|
|
1. Add the internal loop and turn/termination values described above, injecting
|
|
typed callback and run functions. Do not add a generic workflow framework or
|
|
a new generation collaborator.
|
|
2. Implement initial callback delivery, turn-start consumption, final-response
|
|
delivery, explicit stop versus exhaustion, and first-error termination.
|
|
3. Implement context checks and the single undelivered-response slot exactly as
|
|
specified. Do not prepare an exhausted request or replay delivered work.
|
|
4. Unit-test zero turns, stop on last response versus request for another turn,
|
|
execution and callback errors preserving `errors.Is`/`errors.As`, cancellation
|
|
before initial callback, cancellation withholding a completed result, and
|
|
cancellation/error after callback invocation. Use deterministic hooks, not
|
|
sleeps. Add one panic propagation check without terminal-delivery promises.
|
|
|
|
**Validation:** focused `go test ./internal/usecase -run Coordinate` (name tests
|
|
accordingly). The loop tests use cheap execution functions; provider behavior
|
|
remains covered by integration tests.
|
|
|
|
## Stage 7 — Expose Coordination Through The Facade
|
|
|
|
**Status:** Complete; public coordinator tests passed with observer composition.
|
|
|
|
**Scope:** public types, argument checks, and ordinary-execution adapters.
|
|
|
|
1. Add the selected coordinator types and `Engine.Coordinate`, with GoDoc for
|
|
callback state, budgets, cancellation, errors, and normal outcomes.
|
|
2. Validate coordinator arguments without looking up old resources. Project
|
|
callback state through fresh public handle wrappers. Preserve current private
|
|
exchange state independently of any callback mutation.
|
|
3. Supply the run adapter using `Engine.Run` with the receiving engine/view.
|
|
Set current history on a value copy; the coordinator does not mutate the
|
|
callback's `RunRequest`. Never add enclosing admission or an observer recorder.
|
|
4. Wire its observer selection to composition in Stage 9; do not build a second
|
|
temporary observer mechanism to make this stage independently complete.
|
|
5. Add basic public tests for invalid arguments, zero-budget initial stop,
|
|
ordinary continuation, callback error identity, and a callback modifying the
|
|
result/handle it received without changing subsequent retained history.
|
|
|
|
**Validation:** focused coordinator tests when wiring is buildable. Carry the
|
|
explicit observer-composition dependency forward, without an intermediate gate.
|
|
|
|
## Stage 8 — Verify Coordinator Capacity And Cancellation Boundaries
|
|
|
|
**Status:** Complete; focused race validation passed with observer composition.
|
|
|
|
**Scope:** the subtle assembled execution lifecycle.
|
|
|
|
1. Configure one backend with concurrency one and waiting capacity zero. Have the
|
|
callback make a same-engine/backend validation run after receiving a turn.
|
|
Assert it succeeds, proving the logical admission lease as well as the active
|
|
permit was released. Use ordinary admission, not test-only capacity bypasses.
|
|
2. Deliver a structurally invalid completed response normally, including one
|
|
whose structural repairs were exhausted. The application callback decides
|
|
whether to stop or select a new request; PromptKit adds no acceptance meaning.
|
|
3. Exercise operational failure stopping one coordinator while a sibling exchange
|
|
remains usable. Verify typed provider/capacity errors cross unchanged.
|
|
4. Trigger cancellation after a completed run but before callback delivery using
|
|
a terminal observer; retain its result/exchange once as undelivered. Separately
|
|
cancel inside the callback and assert no replay or undelivered duplicate.
|
|
5. Check that internal retry/repair attempts create only one delivery for that
|
|
continuation and do not consume extra turns. Reuse existing deterministic
|
|
HTTP/repair fixtures, not live providers.
|
|
|
|
**Validation:** focused root coordination tests under `-race`, with bounded test
|
|
contexts as failure guards, not timing-dependent synchronization.
|
|
|
|
## Stage 9 — Add Observer Composition
|
|
|
|
**Status:** Complete.
|
|
|
|
**Scope:** fan-out helper and final coordinator-observer wiring.
|
|
|
|
1. Implement internal synchronous fan-out over a copied function slice and the
|
|
root `ComposeObservers` wrapper. Keep public/domain adapters proportionate;
|
|
a small internal generic callback-composition helper can operate directly on
|
|
either event value type without repeated conversion.
|
|
2. Honor nil filtering, supplied order, no function deduplication, and ordinary
|
|
panic propagation. The helper holds no lock while invoking application code.
|
|
3. Complete coordinator observer composition, selecting each request observer
|
|
before the coordinator observer. Do not change original callback request
|
|
values. Callback-initiated calls remain explicitly selected independently.
|
|
4. Test nil/empty composition, caller mutation of the original observer slice,
|
|
ordered delivery, early panic, and simultaneous independent executions. Do
|
|
not promise stronger callback ordering across different runs.
|
|
|
|
**Validation:** focused composition and coordinator observer tests, including
|
|
the root observation-boundary tests.
|
|
|
|
## Stage 10 — Implement Terminal-Based Group Aggregation
|
|
|
|
**Status:** Complete.
|
|
|
|
**Scope:** internal collector, usage semantics, and concurrent snapshots.
|
|
|
|
1. Add domain summary/member values and the zero-ready internal group collector
|
|
under `internal/observation`. Implement map enrollment, terminal deduplication,
|
|
incremental terminal counters, and detached snapshots under one mutex.
|
|
2. Implement the selected known/completeness and coverage rules for all five
|
|
token fields. Do not sum `RunResult.Usage`, reconstruct usage from attempt
|
|
events, or convert missing terminals into zero evidence.
|
|
3. Cover empty groups, members without terminals, terminal-only enrollment,
|
|
duplicate terminal and repeated ordinary events, explicit zeros, wholly
|
|
unknown fields, partially known terminal usage, and terminal failures retaining
|
|
earlier completed usage. Use small event values rather than an HTTP test for
|
|
every arithmetic combination.
|
|
4. Concurrently observe independent runs and read snapshots, checking each
|
|
snapshot's counts, coverage, and member identities agree. Mutate a returned
|
|
snapshot and prove later snapshots are independent. Do not require snapshot
|
|
member ordering or introspect locks.
|
|
|
|
**Validation:** `go test -race ./internal/observation` with focused new group
|
|
tests. No background worker, callback invocation, or collector lifecycle is added.
|
|
|
|
## Stage 11 — Expose Groups And Verify Explicit Membership
|
|
|
|
**Status:** Complete.
|
|
|
|
**Scope:** thin public collector adapter and assembled accounting.
|
|
|
|
1. Add the public group/member/summary types and root event/summary conversions.
|
|
A private zero-ready internal group field keeps the public zero value usable.
|
|
Document synchronization, snapshot ownership, and no-copy-after-use semantics.
|
|
2. Exercise `group.Observe` alone and composed with a diagnostic observer and a
|
|
second group. Cover ordinary runs without capture, captured runs, prepared
|
|
execution, coordinator continuations, and separately selected callback calls.
|
|
3. Assert preparation/discard/failed claim creates no membership. The initial
|
|
exchange is not automatically enrolled by coordination; unselected nested
|
|
calls remain absent. Parent metadata and group membership remain independent.
|
|
4. In one assembled scenario, select initial A and branches B/C, plus a validator
|
|
into overlapping groups. Include a built-in transient retry, structural
|
|
repair, and a failure after completed usage. Check counters and known subtotals
|
|
once per identity, including replay usage reported by the provider for B/C.
|
|
5. A composed observer panic may leave an observed member without a terminal.
|
|
Verify snapshots remain usable, expose missing coverage, and do not manufacture
|
|
completion. Snapshotting inside an observer is safe because group locks are
|
|
not held while calling other observers.
|
|
|
|
**Validation:** focused root group/coordinator/observation tests under `-race`.
|
|
Reuse existing reliable-execution fixtures and avoid duplicating their complete
|
|
retry/repair matrices.
|
|
|
|
## Stage 12 — Demonstrate The Consumer Workflows
|
|
|
|
**Status:** Complete.
|
|
|
|
**Scope:** practical API use and one maintained offline example.
|
|
|
|
1. Extend `examples/go-library/observe` and its semantic assertions to demonstrate
|
|
captured proposal history, a coordinator follow-up, an application-owned
|
|
assessment call, and explicit group selection alongside diagnostic observation.
|
|
Keep its deterministic transport and existing retry/usage protections; no paid
|
|
calls or real credentials are needed.
|
|
2. The example's assessment remains application policy, not a library validator
|
|
hook. Use a closure to retain assessed state, and show stop versus exhausted
|
|
budget without returning an application verdict from PromptKit.
|
|
3. Add concise consumer-guide examples for direct branching and prepared capture,
|
|
plus both roadmap sketches translated to the selected callback shape. The
|
|
analysis sketch records a new result then asks another question or returns nil.
|
|
The Audita-shaped sketch uses existing findings initially, assesses each new
|
|
result, and either returns nil or an ordinary follow-up request. Domain-specific
|
|
functions remain illustrative caller code.
|
|
4. Show group use with ordinary/prepared requests and `CoordinateRequest.Observer`,
|
|
including explicit selection for a nested validator and an independent group
|
|
summary. Demonstrate checking structural validation before schema-specific
|
|
decoding; stop/exhaustion does not imply content acceptance.
|
|
|
|
**Validation:** `go test ./examples/go-library/...` and affected public example
|
|
tests. Review snippets against actual declarations; full copyable setup stays in
|
|
the maintained example rather than duplicated in prose.
|
|
|
|
## Stage 13 — Finalize Contracts, Documentation, And Sprint Validation
|
|
|
|
**Status:** Complete. Full offline race suite, vet, and diff checks passed with Go 1.27.3.
|
|
|
|
**Scope:** complete the target and remove sprint-only rough edges.
|
|
|
|
1. Review every new public declaration and GoDoc against this plan and the feature
|
|
roadmap. Confirm ordinary APIs still use existing error, timing, prepared-claim,
|
|
credential, namespace, usage, and cancellation behavior. Verify result/details
|
|
serialization and constant opaque formatting without introducing a restartable
|
|
exchange format or redacting caller-authored message content.
|
|
2. Update the canonical consumer guide, `doc.go` where orientation needs it, and
|
|
affected internal runner, observation, and component-overview documents.
|
|
Document capacity release for conversation callbacks separately from ordinary
|
|
per-attempt observer callback constraints. Configuration and integration docs
|
|
need only changes necessary to describe the implemented message composition;
|
|
ancestry is not an upstream wire field or configuration setting.
|
|
3. Remove temporary helpers, placeholder implementations, unused experimental
|
|
API choices, and intermediate compile/test failures. Do not remove unrelated
|
|
planned/deferred work or publish a release in this sprint. Release-note/version
|
|
selection and roadmap retirement can follow during release preparation.
|
|
4. With the newest locally available Go toolchain selected as the development
|
|
guide prescribes, format changed Go files, then run:
|
|
|
|
```sh
|
|
go test -race ./...
|
|
go vet ./...
|
|
git diff --check
|
|
```
|
|
|
|
The full suite includes the maintained examples; do not add a redundant
|
|
separate build or provider connectivity gate. Check added/changed local
|
|
documentation links, using the existing tooling where practical rather than
|
|
introducing another release validator.
|
|
5. Review the complete diff and Git status for unintended files or credentials.
|
|
Confirm no new dependency, workspace override, configuration parser, provider
|
|
parameter, or strengthened restriction/guarantee slipped into the work.
|
|
Commit completed units with plain-English messages under the agent policy.
|
|
|
|
**Completion evidence:** the implemented facade supports ordinary/prepared
|
|
capture, cross-engine independent continuations, coordinator callbacks after
|
|
admission release, cancellation's single undelivered response, and independent
|
|
observer-based groups. Public behavior tests cover those boundaries and terminal
|
|
usage completeness; the complete offline race suite and vet pass. No product or
|
|
API decisions are deferred to an implementation stage.
|