557 lines
22 KiB
Markdown
557 lines
22 KiB
Markdown
# Explicit Per-Run Profile Overrides Implementation Plan
|
|
|
|
**Status:** Complete.
|
|
|
|
## Purpose
|
|
|
|
This document is the decision-complete implementation plan for the
|
|
[explicit per-run profile overrides](profile-overrides.md). It is written for
|
|
a coding agent that will implement each stage in order.
|
|
|
|
The feature roadmap owns the intended capability, user intent, policy choices,
|
|
compatibility decision, and target end state. This document owns the concrete
|
|
API changes, internal representation, implementation sequence, test ownership,
|
|
documentation updates, and completion gates.
|
|
|
|
## Implementation Rules
|
|
|
|
- Complete the stages in order. Keep the repository compiling and the focused
|
|
tests passing at every stage boundary.
|
|
- Preserve unrelated working-tree changes. The selected feature roadmap and
|
|
the removal of its source idea from `future.md` may already be uncommitted
|
|
when implementation begins; retain both.
|
|
- Follow every policy under `docs/policy/`, the task-specific reading guide in
|
|
`docs/development.md`, and the target behavior in
|
|
`profile-overrides.md`.
|
|
- Keep the public API in the root `promptkit` package and implementation
|
|
details under `internal/`. Do not create another public package.
|
|
- Do not broaden the feature into conversation storage, automatic session
|
|
generation, provider capability discovery, backend-specific headers,
|
|
concurrency control, retries, queues, or file-format changes.
|
|
- Preserve synchronous `Prepare` and `Run`, engine-local invocation state,
|
|
endpoint-only profiles, backend-selected profiles, and injected
|
|
`LLMClient` behavior.
|
|
- Keep all tests deterministic and offline. Use existing fakes or local
|
|
`httptest` servers; never contact OpenRouter or another live provider.
|
|
- Update exact GoDoc with each exported declaration change. Update durable
|
|
current-state documents only after the corresponding behavior is
|
|
implemented.
|
|
- Add tests at the narrowest stable owner described below. Do not reproduce
|
|
the same precedence table at the domain, runner, facade, and transport
|
|
layers.
|
|
- Do not create a release, change a module version, or tag a commit. The final
|
|
implementation handoff must call out the planned pre-`v1` source
|
|
compatibility change so it can be included in the next minor release note.
|
|
|
|
## Fixed Design
|
|
|
|
### Public API
|
|
|
|
Add a direct session field to `RunRequest`:
|
|
|
|
```go
|
|
type RunRequest struct {
|
|
// Insert after ProfileID and before APIKey.
|
|
SessionID string
|
|
}
|
|
```
|
|
|
|
Its exact GoDoc must state that:
|
|
|
|
- a nonblank value is trimmed and overrides the prompt definition's
|
|
`session_id` template;
|
|
- a blank value supplies no direct override;
|
|
- the maximum is 256 Unicode code points after trimming;
|
|
- a direct value is opaque consumer metadata, is not a credential, and may be
|
|
exposed in prepared values, results, collaborator requests, provider
|
|
requests, and provider observability; and
|
|
- callers should use stable, non-sensitive identifiers.
|
|
|
|
Change the existing reasoning override field:
|
|
|
|
```go
|
|
type ExecutionTargetOverride struct {
|
|
// Existing fields remain otherwise unchanged.
|
|
ReasoningEffort *string
|
|
}
|
|
```
|
|
|
|
The field has these exact states:
|
|
|
|
| Public value | Effective behavior |
|
|
| --- | --- |
|
|
| `nil` | Inherit the profile's reasoning effort. |
|
|
| Pointer to a nonblank string | Trim and replace the profile value. |
|
|
| Pointer to an empty or whitespace-only string | Clear the inherited value and disable reasoning for this run. |
|
|
|
|
Do not add `DisableReasoning`, a parallel reasoning override, a public optional
|
|
string wrapper, or a fixed reasoning enum. The pointer is the one supported
|
|
runtime reasoning surface.
|
|
|
|
Add completed-run session metadata:
|
|
|
|
```go
|
|
type RunResult struct {
|
|
// Insert after PromptHash and before RenderedPromptHash.
|
|
SessionID string `json:"session_id,omitempty"`
|
|
}
|
|
```
|
|
|
|
`PreparedRun.SessionID` remains the preparation metadata field. Update its
|
|
GoDoc from "rendered" to "effective direct or rendered" session identity.
|
|
Likewise, update `RenderedPrompt.SessionID` GoDoc because injected clients may
|
|
receive a direct request value rather than one produced by the prompt
|
|
renderer.
|
|
|
|
Do not add session identity to `ExecutionTarget`,
|
|
`ExecutionTargetOverride`, `Profile`, or the profile file format. Do not add a
|
|
second result field that records whether the session came from the request or
|
|
prompt.
|
|
|
|
### Internal Values And Copy Ownership
|
|
|
|
Mirror the public request changes in `internal/domain`:
|
|
|
|
```go
|
|
type RunRequest struct {
|
|
// Insert after ProfileID and before APIKey.
|
|
SessionID string
|
|
}
|
|
|
|
type ExecutionTargetOverride struct {
|
|
// Existing fields...
|
|
ReasoningEffort *string
|
|
}
|
|
|
|
type RunResult struct {
|
|
// Insert after PromptHash and before RenderedPromptHash.
|
|
SessionID string
|
|
}
|
|
```
|
|
|
|
`toDomainRunRequest` copies `RunRequest.SessionID`. The public-to-domain
|
|
execution override conversion must allocate a new string for a non-nil
|
|
reasoning pointer rather than retain the caller's pointer. Add a small
|
|
`copyStringPtr` helper beside the existing numeric pointer-copy helpers in
|
|
`convert.go`.
|
|
|
|
The internal and public profile and effective-target fields remain ordinary
|
|
strings:
|
|
|
|
- `domain.ExecutionProfile.ReasoningEffort`
|
|
- `Profile.ReasoningEffort`
|
|
- `domain.ExecutionTarget.ReasoningEffort`
|
|
- `ExecutionTarget.ReasoningEffort`
|
|
|
|
Do not extend `ExecutionTargetPresence`. That value exists to preserve
|
|
explicit numeric zero serialization. After reasoning resolution, an empty
|
|
effective reasoning string is sufficient to instruct built-in and injected
|
|
clients to omit reasoning.
|
|
|
|
### Shared Session Normalization
|
|
|
|
Add `internal/domain/session.go` with one shared internal domain rule:
|
|
|
|
```go
|
|
func NormalizeSessionID(raw string) (string, error)
|
|
```
|
|
|
|
The function:
|
|
|
|
- trims surrounding Unicode whitespace with `strings.TrimSpace`;
|
|
- returns `""`, `nil` for a blank value;
|
|
- counts Unicode code points with `utf8.RuneCountInString`;
|
|
- rejects a normalized value longer than `SessionIDMaxLength`; and
|
|
- returns an ordinary internal error containing the actual and maximum
|
|
lengths, without attaching a prompt, request, or transport error sentinel.
|
|
|
|
Keep `SessionIDMaxLength` in `internal/domain` at 256. The renderer, runner,
|
|
and model client wrap normalization failures into their own error boundaries:
|
|
|
|
- prompt session template: prompt-render failure;
|
|
- direct request session: invalid request; and
|
|
- defensive model-client validation: invalid generate request.
|
|
|
|
Refactor `internal/prompt.renderSessionID` and
|
|
`internal/llm.openAIChatRequestFromGenerateRequest` to use this helper. Remove
|
|
their duplicated trimming and rune-count logic and any now-unused imports.
|
|
|
|
### Direct Session Resolution
|
|
|
|
In `Runner.Prepare`, preserve required prompt-ID validation as the first
|
|
request check. Immediately afterward, normalize `req.SessionID`. A
|
|
normalization failure must return an error matching use-case
|
|
`ErrInvalidRequest` before prompt or profile loading begins.
|
|
|
|
Continue loading and hashing the original prompt definition. When the
|
|
normalized direct session is nonempty:
|
|
|
|
1. make a value copy of the loaded prompt definition;
|
|
2. clear only the copy's `SessionID` template;
|
|
3. pass the copied definition to the existing renderer; and
|
|
4. set the renderer's returned `RenderedPrompt.SessionID` to the normalized
|
|
direct value before hashing or constructing `PreparedRun`.
|
|
|
|
Do not mutate the repository-owned prompt definition. Do not change the
|
|
renderer interface, inject the direct value into `Vars`, or expose it to
|
|
message templates. Message rendering continues to use the request's existing
|
|
variables and inputs.
|
|
|
|
When the direct session is blank, pass the original definition to the renderer
|
|
and retain current prompt-template behavior. This includes current missing
|
|
variable, invalid template, blank result, and maximum-length behavior.
|
|
|
|
The prompt-definition hash must always be computed from the original
|
|
definition. The rendered-prompt hash must use the effective session ID after
|
|
the direct override is applied.
|
|
|
|
`Runner.Run` must copy `PreparedRun.SessionID` into the internal `RunResult`.
|
|
The existing generation request must continue receiving the same effective
|
|
value through `RenderedPrompt.SessionID`.
|
|
|
|
### Reasoning Resolution
|
|
|
|
Leave `mergeExecutionTarget`, which overlays framework/backend/profile
|
|
`ExecutionTarget` values, unchanged: blank profile-level reasoning strings
|
|
continue to mean "no replacement."
|
|
|
|
Change only `mergeExecutionTargetOverride`, which applies the request's
|
|
pointer-based override:
|
|
|
|
```go
|
|
if override.ReasoningEffort != nil {
|
|
out.ReasoningEffort = strings.TrimSpace(*override.ReasoningEffort)
|
|
}
|
|
```
|
|
|
|
Assign the trimmed value even when it is empty. This is the operation that
|
|
clears inherited reasoning. Do not validate against a fixed vocabulary and do
|
|
not translate an empty value to `none`, `null`, or any other provider value.
|
|
|
|
The built-in client already omits an empty effective `ReasoningEffort` and
|
|
serializes a nonempty value. Preserve that transport behavior. A consumer may
|
|
still explicitly provide a provider-supported nonblank value such as `none`;
|
|
Promptkit treats it as an opaque override rather than its own disable
|
|
mechanism.
|
|
|
|
### Stable JSON And Result Conversion
|
|
|
|
Add `session_id,omitempty` to the private `runResultJSON` representation in
|
|
`json.go`. Carry it in both `RunResult.MarshalJSON` and
|
|
`RunResult.UnmarshalJSON`.
|
|
|
|
The stable JSON contract is:
|
|
|
|
- a nonempty `RunResult.SessionID` serializes as top-level `session_id`;
|
|
- an empty value is omitted; and
|
|
- JSON produced before this field existed remains valid and decodes with an
|
|
empty session ID.
|
|
|
|
Carry the internal result value through `fromDomainRunResult`. No custom JSON
|
|
work is needed for `PreparedRun`, whose existing `SessionID` field already has
|
|
the required name and omission behavior.
|
|
|
|
### OpenAI-Compatible Wire Contract
|
|
|
|
Do not add `x-session-id`. The built-in client continues sending the effective
|
|
nonempty value only as the top-level `session_id` body field. Keep
|
|
`session_id` in the reserved extra-parameter field set.
|
|
|
|
The existing transport tests that assert body serialization, blank omission,
|
|
length rejection, and absence of the session header remain the canonical wire
|
|
tests. Refactoring normalization must not weaken those assertions.
|
|
|
|
### Error Identities
|
|
|
|
Do not add public or internal error sentinels.
|
|
|
|
| Failure | Required identity |
|
|
| --- | --- |
|
|
| Direct session exceeds the maximum | Public `ErrInvalidRequest` through use-case `ErrInvalidRequest` |
|
|
| Prompt session template is invalid or cannot render | Existing `ErrPromptRender` behavior |
|
|
| Prompt-rendered session exceeds the maximum | Existing `ErrPromptRender` behavior |
|
|
| Model client defensively receives an overlong session | Existing internal LLM `ErrInvalidRequest` |
|
|
| Blank reasoning pointer | No error; clears inherited reasoning |
|
|
| Nonblank unfamiliar reasoning value | No Promptkit validation error; pass through after trimming |
|
|
|
|
Do not make exact diagnostic prose a public contract. Tests should use
|
|
`errors.Is` and inspect only a useful semantic fragment when necessary.
|
|
|
|
## Test Ownership
|
|
|
|
Use the following ownership split:
|
|
|
|
- `internal/domain/session_test.go` owns shared trimming, blank handling, and
|
|
the 256/257 Unicode-code-point boundary.
|
|
- `internal/prompt/renderer_test.go` retains ownership of session-template
|
|
parsing, rendering, and prompt-render error mapping. Update existing tests
|
|
only as required by the shared normalizer refactor.
|
|
- `internal/usecase/runner_test.go` owns request/profile reasoning precedence,
|
|
direct-session bypass of the template, invalid direct-session
|
|
classification, and application of the effective session before hashing.
|
|
- Root external-package tests own public conversion, an assembled
|
|
direct-session workflow, completed-result propagation, and stable public
|
|
JSON.
|
|
- `internal/llm/openai_compatible_client_test.go` remains the sole owner of
|
|
body-versus-header delivery and wire omission. Do not duplicate those exact
|
|
HTTP assertions in a root workflow test.
|
|
|
|
Prefer extending coherent existing table tests over creating parallel
|
|
end-to-end suites. Keep literal-limit testing centralized around
|
|
`domain.SessionIDMaxLength`; use relative boundary cases rather than copying
|
|
the number into multiple tests.
|
|
|
|
## Stage 1 — Tri-State Reasoning Override
|
|
|
|
**Status:** Complete.
|
|
|
|
### Goal
|
|
|
|
Replace the ambiguous string request override with an owned pointer and
|
|
implement inherit, replace, and clear semantics without changing profiles or
|
|
the provider payload model.
|
|
|
|
### Work
|
|
|
|
1. In `types.go`, change
|
|
`ExecutionTargetOverride.ReasoningEffort` to `*string` and write exact
|
|
tri-state GoDoc. Update the surrounding override summary so it no longer
|
|
says every empty string inherits.
|
|
2. In `internal/domain/domain.go`, make the corresponding override field a
|
|
`*string`. Leave profile and effective-target fields unchanged.
|
|
3. In `convert.go`, add `copyStringPtr` beside `copyFloat64Ptr` and
|
|
`copyIntPtr`, and use it when converting the override. Never retain the
|
|
caller's pointer.
|
|
4. In `internal/usecase/runner.go`, change only the request-override merge to
|
|
assign `strings.TrimSpace(*override.ReasoningEffort)` whenever the pointer
|
|
is non-nil. Preserve profile-level blank-string behavior.
|
|
5. Update all in-repository public and internal
|
|
`ExecutionTargetOverride` composite literals to use string pointers where
|
|
they intend an override. Do not change `Profile`, `ExecutionProfile`, or
|
|
effective `ExecutionTarget` literals.
|
|
6. Keep `ExecutionTargetPresence`, the model-client request type, and the
|
|
OpenAI-compatible wire structure unchanged.
|
|
|
|
### Tests
|
|
|
|
1. Update the existing root execution-precedence test so its ordinary
|
|
nonblank reasoning override uses a pointer.
|
|
2. Extend that coherent public precedence coverage with a profile that has a
|
|
nonempty reasoning default and a pointer to a blank request value; assert
|
|
that `PreparedRun.EffectiveModelParams.ReasoningEffort` is empty.
|
|
3. In the runner resolution tests, cover all three states:
|
|
- nil inherits the profile value;
|
|
- a pointer to `" high "` produces `"high"`; and
|
|
- a pointer to whitespace produces `""`.
|
|
4. Retain the existing model-client tests for serializing nonempty reasoning
|
|
and omitting empty reasoning. Do not add another HTTP test for the same
|
|
transport branches.
|
|
|
|
### Focused Validation
|
|
|
|
Run:
|
|
|
|
```sh
|
|
gofmt -w types.go convert.go internal/domain/domain.go \
|
|
internal/usecase/runner.go engine_test.go \
|
|
internal/usecase/runner_test.go
|
|
go test . ./internal/usecase ./internal/llm
|
|
go vet . ./internal/usecase ./internal/llm
|
|
git diff --check
|
|
```
|
|
|
|
### Completion Gate
|
|
|
|
This stage is complete when the repository builds with the pointer-based
|
|
public API, all three reasoning states resolve correctly, caller pointer
|
|
ownership is not retained, and the provider client still receives only the
|
|
resolved string.
|
|
|
|
## Stage 2 — Direct Session Resolution And Result Metadata
|
|
|
|
**Status:** Complete.
|
|
|
|
### Goal
|
|
|
|
Add direct per-run session identity, bypass the prompt session template when
|
|
selected, and carry the effective value consistently through hashes,
|
|
generation, results, and stable JSON.
|
|
|
|
### Work
|
|
|
|
1. Add `internal/domain/session.go` with the shared normalization function and
|
|
refactor the prompt renderer and model client to consume it.
|
|
2. Add `SessionID` to public and internal `RunRequest` and copy it in
|
|
`toDomainRunRequest`.
|
|
3. Implement direct normalization at the start of `Runner.Prepare` after
|
|
required prompt-ID validation. Wrap an overlong value with use-case
|
|
`ErrInvalidRequest`.
|
|
4. Preserve the original definition for its hash. For a nonempty direct
|
|
session, render with a copied definition whose session template is cleared,
|
|
then apply the direct value before computing the rendered-prompt hash.
|
|
5. Leave message variables and templates unchanged. Confirm that a direct
|
|
session succeeds even if the unused prompt session template would fail
|
|
because of a missing variable.
|
|
6. Add `SessionID` to internal and public `RunResult`, populate it from
|
|
`PreparedRun` in `Runner.Run`, and carry it through `fromDomainRunResult`.
|
|
7. Add the field to `runResultJSON`, `MarshalJSON`, and `UnmarshalJSON` with
|
|
`omitempty`.
|
|
8. Update GoDoc for `RunRequest.SessionID`, `PreparedRun.SessionID`,
|
|
`RunResult.SessionID`, and `RenderedPrompt.SessionID`. Ensure none describes
|
|
the value as necessarily renderer-produced.
|
|
9. Keep the built-in HTTP request body and reserved-field behavior unchanged;
|
|
no session header is added.
|
|
|
|
### Tests
|
|
|
|
1. Add focused domain normalization cases for:
|
|
- surrounding whitespace;
|
|
- blank input;
|
|
- exactly `SessionIDMaxLength` Unicode code points; and
|
|
- one code point over the limit.
|
|
2. Add runner tests showing:
|
|
- a direct value is trimmed and wins over a prompt session template;
|
|
- a direct value bypasses an otherwise failing session template while
|
|
message templates still render normally;
|
|
- blank direct input retains prompt-template behavior;
|
|
- changing only the direct session changes the rendered-prompt hash but not
|
|
the prompt-definition hash; and
|
|
- an overlong direct value matches `ErrInvalidRequest` and does not invoke
|
|
generation.
|
|
3. Add one assembled root-package workflow using an injected fake model
|
|
client. Assert that the same direct session appears in `PreparedRun`,
|
|
`GenerateRequest.Prompt`, and `RunResult`. This test owns the public adapter
|
|
path; it must not repeat body/header assertions.
|
|
4. Extend the public `RunResult` JSON round-trip test to cover a nonempty
|
|
session, and add or extend omission coverage for an empty session.
|
|
5. Keep the existing prompt-renderer and model-client boundary tests passing
|
|
after normalization is centralized.
|
|
|
|
### Focused Validation
|
|
|
|
Run:
|
|
|
|
```sh
|
|
gofmt -w types.go convert.go json.go internal/domain/domain.go \
|
|
internal/domain/session.go internal/domain/session_test.go \
|
|
internal/prompt/go_renderer.go internal/prompt/renderer_test.go \
|
|
internal/usecase/runner.go internal/usecase/runner_test.go \
|
|
internal/llm/openai_compatible_client.go \
|
|
internal/llm/openai_compatible_client_test.go \
|
|
engine_test.go public_contract_test.go
|
|
go test . ./internal/domain ./internal/prompt ./internal/usecase ./internal/llm
|
|
go vet . ./internal/domain ./internal/prompt ./internal/usecase ./internal/llm
|
|
git diff --check
|
|
```
|
|
|
|
If an existing test file did not require an edit, omit it from the `gofmt`
|
|
arguments rather than touching it mechanically.
|
|
|
|
### Completion Gate
|
|
|
|
This stage is complete when one normalized effective session flows from the
|
|
public request through preparation, hashing, generation, completed results,
|
|
and stable JSON; direct values bypass the prompt session template; and the
|
|
existing body-only transport contract remains intact.
|
|
|
|
## Stage 3 — Durable Documentation And Final Validation
|
|
|
|
**Status:** Complete.
|
|
|
|
### Goal
|
|
|
|
Make current-state contracts accurately describe the implemented feature,
|
|
record the compatibility impact for release handoff, and validate the complete
|
|
repository.
|
|
|
|
### Work
|
|
|
|
1. Review all changed exported declarations in `types.go` and `doc.go`.
|
|
Ensure GoDoc is the canonical owner of exact field types, pointer states,
|
|
copying, normalization, privacy exposure, JSON, and error behavior.
|
|
2. Update `docs/consumers/pkg-promptkit.md` with concise task-oriented guidance
|
|
for:
|
|
- supplying a direct per-run session;
|
|
- inheriting, replacing, and disabling profile reasoning with a string
|
|
pointer; and
|
|
- treating session IDs as stable, non-secret correlation values.
|
|
Keep snippets illustrative and link to GoDoc for the exact contract.
|
|
3. Update `docs/formats.md` only where the file format interacts with runtime
|
|
precedence:
|
|
- a direct request session bypasses the prompt `session_id` template; and
|
|
- reasoning is the exception to the general nonempty request-string rule
|
|
because its pointer can explicitly clear the profile value.
|
|
Do not add or change YAML fields.
|
|
4. Update `docs/integrations/openai-compatible-chat.md` to describe the
|
|
effective direct-or-rendered session, body-only delivery, the 256-code-point
|
|
check, and omission of an explicitly disabled effective reasoning setting.
|
|
Do not duplicate public Go field declarations.
|
|
5. Update `docs/internal/runner.md` to describe direct-session normalization
|
|
and template bypass, effective hashing, tri-state reasoning precedence, and
|
|
propagation into completed results.
|
|
6. Do not update `docs/internal/llm.md` or the internal component inventory.
|
|
Shared normalization is an incidental mechanism and does not change the
|
|
model client's documented flow or any package responsibility.
|
|
7. Do not change architecture, documentation, or testing policy unless
|
|
implementation reveals a genuine policy change. This feature is expected
|
|
to fit the current policies.
|
|
8. After every implementation and documentation check passes, set the feature
|
|
roadmap and this implementation plan status to `Complete`. Do not remove
|
|
either roadmap in the implementation change; lifecycle retirement can
|
|
follow after review.
|
|
9. In the implementation handoff, explicitly identify
|
|
`ExecutionTargetOverride.ReasoningEffort` changing from `string` to
|
|
`*string` as a pre-`v1` minor-release source compatibility change. Do not
|
|
edit release procedure or create a tag.
|
|
|
|
### Full Validation
|
|
|
|
Run the complete sequence from `docs/development.md`:
|
|
|
|
```sh
|
|
go test ./...
|
|
go test -race ./...
|
|
go vet ./...
|
|
go build ./...
|
|
go run ./examples/go-library/prepare
|
|
gofmt -l $(git ls-files '*.go')
|
|
git diff --check
|
|
```
|
|
|
|
The formatting command must produce no paths. Follow every added or changed
|
|
Markdown link and confirm its target and heading exist. Also inspect:
|
|
|
|
```sh
|
|
git status --short
|
|
git diff --stat
|
|
git diff
|
|
```
|
|
|
|
Confirm that:
|
|
|
|
- only intended feature, test, documentation, and roadmap files changed;
|
|
- no `go.work`, `go.work.sum`, local module replacement, credential, generated
|
|
binary, or unrelated change was introduced;
|
|
- all public and internal override literals use the correct pointer or string
|
|
type for their layer;
|
|
- `session_id` remains reserved in extra parameters;
|
|
- the built-in client does not set `x-session-id`;
|
|
- current-state documents describe implemented behavior rather than referring
|
|
readers to the roadmaps; and
|
|
- the feature and implementation roadmaps contain no unresolved work marked
|
|
complete.
|
|
|
|
### Completion Gate
|
|
|
|
The implementation is complete only when every target-end-state item in
|
|
`profile-overrides.md` is implemented, the full validation sequence passes,
|
|
durable contracts no longer depend on roadmap prose, and the compatibility
|
|
change is clearly reported for the next minor release.
|
|
|
|
## Open Questions
|
|
|
|
None. The feature roadmap and this plan fix the public representation,
|
|
precedence, normalization, error identities, body-versus-header choice,
|
|
metadata, hashing, test ownership, compatibility treatment, and non-goals
|
|
needed for implementation.
|