Prepare documentation for the v0.7.0 release
This commit is contained in:
@@ -1,477 +0,0 @@
|
||||
# Profile Inheritance Implementation Plan
|
||||
|
||||
## Purpose
|
||||
|
||||
Implement the profile-inheritance feature defined by the
|
||||
[profile inheritance roadmap](profile-inheritance.md). This plan is the
|
||||
decision-complete execution sequence for a coding agent. Follow the repository
|
||||
[architecture](../policy/architecture.md), [testing](../policy/testing.md), and
|
||||
[documentation](../policy/documentation.md) policies and the task-specific
|
||||
reading guide in [`docs/development.md`](../development.md) throughout the
|
||||
work.
|
||||
|
||||
## Target Outcome
|
||||
|
||||
A downstream consumer can define a YAML or in-memory profile whose
|
||||
`base_profile`/`BaseProfileID` names another profile, including a Promptkit
|
||||
built-in. Promptkit resolves the complete one-parent chain through the assembled
|
||||
profile catalog, merges root-to-leaf profile settings, resolves the final
|
||||
backend, and then applies request overrides exactly as it does for a standalone
|
||||
profile.
|
||||
|
||||
The selected leaf ID remains the public profile identity. Ordinary operations
|
||||
resolve sources afresh, while prepared execution freezes the resolved target.
|
||||
Missing dependencies, cycles, excessive depth, and incomplete resolved profiles
|
||||
fail as profile-load errors without changing the distinct not-found contract for
|
||||
an absent directly selected profile.
|
||||
|
||||
## Fixed Decisions
|
||||
|
||||
- YAML uses `base_profile`; the public Go fields are
|
||||
`Profile.BaseProfileID` and
|
||||
`OpenAICompatibleProfileConfig.BaseProfileID`.
|
||||
- One profile may name one base. Bases may form a linear chain of at most 32
|
||||
profiles, including the selected leaf.
|
||||
- The resolver performs every lookup through the complete raw composite
|
||||
repository. Each base therefore observes the ordinary in-memory, configured,
|
||||
fallback, and built-in precedence.
|
||||
- The resolving repository is stateless and does not cache results across
|
||||
calls. It is applied once, outside the fully assembled raw overlay chain.
|
||||
- Profile definitions with a base may omit `model` and both connection fields.
|
||||
Standalone definitions retain their current completeness requirements.
|
||||
- Root-to-leaf merge behavior follows existing profile presence semantics:
|
||||
nonblank strings and nonzero numbers override; blank strings and zero numbers
|
||||
inherit; a nonempty `extra_params` map replaces the complete inherited map;
|
||||
and `APIKeyRequired` is a sticky logical OR.
|
||||
- `id` always comes from the selected leaf. `base_profile` is cleared from the
|
||||
resolved profile before it reaches the use-case layer.
|
||||
- Backend and endpoint remain independent fields. A child backend replaces the
|
||||
inherited backend ID without implicitly clearing an inherited endpoint; a
|
||||
child endpoint replaces only the endpoint.
|
||||
- This work introduces no profile-level clearing syntax, presence pointers,
|
||||
multiple inheritance, source-qualified references, public resolution graph,
|
||||
new public error sentinel, or provider-wire change.
|
||||
- A missing directly selected profile continues to expose
|
||||
`ErrProfileNotFound`. Once the leaf exists, a missing base is an invalid
|
||||
selected profile and must expose `ErrProfileLoad` without also matching
|
||||
`ErrProfileNotFound`.
|
||||
- Existing raw-source failures and context cancellation remain discoverable
|
||||
through `errors.Is` where they are currently preserved. The missing-base
|
||||
branch is the one deliberate exception: its diagnostic names the missing ID
|
||||
and chain but does not wrap the internal not-found sentinel.
|
||||
- No release notes, version tags, commits, or pushes are part of these stages.
|
||||
|
||||
## Execution Rules
|
||||
|
||||
- Complete the stages in numerical order. Each stage is sized for one
|
||||
gpt-5.6-terra implementation prompt and must finish its focused verification
|
||||
before the next begins.
|
||||
- At the start of every stage, inspect the working tree and preserve unrelated
|
||||
changes. The feature roadmap is intentional planning state and must remain.
|
||||
- Treat intermediate stages as an unreleased partial implementation. Do not
|
||||
describe the feature in durable current-state prose until Stage 4.
|
||||
- Keep inheritance logic in `internal/profile`. Do not add recursive profile
|
||||
lookup, chain merging, or source-precedence logic to `internal/usecase` or the
|
||||
root public facade.
|
||||
- Use real in-memory repositories and synthetic `fs.FS` sources where practical.
|
||||
Tests must remain deterministic, offline, parallel-safe, and independent of
|
||||
provider credentials.
|
||||
- Update existing test owners when their validation contract changes; do not
|
||||
retain contradictory expectations or duplicate the full inheritance matrix
|
||||
at higher layers.
|
||||
- Use `apply_patch` for edits, format every changed Go file, and review the
|
||||
complete stage diff before proceeding.
|
||||
|
||||
## Stage 1: Add Profile Definition Surfaces And Separate Local Validation
|
||||
|
||||
### Objective
|
||||
|
||||
Represent a base-profile reference in every supported profile input while
|
||||
preserving strict decoding, standalone-profile compatibility, and eager local
|
||||
validation. At the end of this stage repositories can publish locally valid
|
||||
derived definitions, but the engine does not yet resolve them.
|
||||
|
||||
### Implementation
|
||||
|
||||
1. Add `BaseProfileID string` to `internal/domain.ExecutionProfile` with the
|
||||
YAML tag `base_profile`. Keep it out of execution targets and provider
|
||||
request types.
|
||||
2. Add `BaseProfileID string` to the public `Profile` immediately after `ID`.
|
||||
Its GoDoc must state that:
|
||||
- `WithProfiles` trims the value;
|
||||
- a nonblank value permits required target fields to be inherited;
|
||||
- reference existence and resolved completeness are checked only when the
|
||||
profile is selected or inspected; and
|
||||
- blank means the profile remains standalone.
|
||||
3. Add the matching field to `OpenAICompatibleProfileConfig`, document that it
|
||||
maps to `Profile.BaseProfileID`, and copy it in
|
||||
`OpenAICompatibleProfile`. Preserve the constructor's shallow-copy and
|
||||
deferred deep-validation behavior for `ExtraParams`.
|
||||
4. Map and trim `Profile.BaseProfileID` in `toDomainProfile`. Ensure the memory
|
||||
repository continues to return caller-independent `ExtraParams` and does not
|
||||
expose shared mutable profile state.
|
||||
5. Centralize the duplicated file-profile and in-memory-profile normalization
|
||||
rules in `internal/profile` rather than adding a third validation path:
|
||||
- provide an internal-package exported helper named
|
||||
`NormalizeAndValidateDefinition` so the root facade can validate a
|
||||
converted in-memory `domain.ExecutionProfile` without owning profile
|
||||
parsing rules;
|
||||
- trim `ID`, `BaseProfileID`, `BackendID`, and `Endpoint` in that helper;
|
||||
- require a nonblank `ID` and validate a supplied endpoint and all execution
|
||||
setting bounds for every definition;
|
||||
- when `BaseProfileID` is blank, retain the existing requirements for a
|
||||
nonblank model and at least one nonblank backend or endpoint;
|
||||
- when `BaseProfileID` is nonblank, allow model, backend, and endpoint to be
|
||||
omitted so they can be inherited; and
|
||||
- do not add unrelated normalization, environment-name policy, clearing
|
||||
behavior, or backend registry lookup.
|
||||
6. Replace `normalizeAndValidateProfile` in
|
||||
`internal/profile/filesystem_repository.go` and
|
||||
`normalizeAndValidatePublicProfile` in `profiles.go` with the shared helper.
|
||||
Preserve each caller's current public/internal error wrapping, selected-file
|
||||
path diagnostic, raw-key prohibition, strict single-document rule, and
|
||||
duplicate-ID behavior. Remove imports and helpers made obsolete by the
|
||||
centralization.
|
||||
7. Keep the raw repositories raw: file, `fs.FS`, memory, overlay, and built-in
|
||||
repositories return `BaseProfileID` but do not recursively load it in this
|
||||
stage.
|
||||
|
||||
### Tests
|
||||
|
||||
Update the narrow existing owners:
|
||||
|
||||
1. In `internal/profile/repository_test.go`, extend the filesystem/`fs.FS`
|
||||
source-parity coverage to prove that:
|
||||
- strict YAML accepts `base_profile`, trims it, and publishes it;
|
||||
- an alias containing only `id` and `base_profile` is locally valid;
|
||||
- a derived profile still rejects an invalid supplied endpoint, negative or
|
||||
out-of-range setting, malformed `extra_params`, raw `api_key`, duplicate
|
||||
ID, and extra YAML document through their existing identities; and
|
||||
- a standalone profile missing connection fields or model remains invalid.
|
||||
Modify existing missing-field expectations rather than duplicating their
|
||||
entire matrices.
|
||||
2. In root package tests, extend the existing `WithProfiles` validation owner
|
||||
to show that an incomplete profile with a nonblank `BaseProfileID` is
|
||||
accepted by `NewEngine`, while the equivalent standalone profile retains its
|
||||
current `ErrInvalidConfig` result. Do not attempt to select the derived
|
||||
profile until the resolver is implemented.
|
||||
3. Extend `TestOpenAICompatibleProfileMapsEveryField` so
|
||||
`BaseProfileID` is included in the constructor's exact field mapping.
|
||||
4. Preserve existing tests for defensive copying, duplicates, standalone
|
||||
profiles, overlay precedence, built-ins, and strict decoding.
|
||||
|
||||
### Verification
|
||||
|
||||
```sh
|
||||
gofmt -w internal/domain/domain.go internal/profile/*.go profiles.go types.go
|
||||
go test ./internal/profile
|
||||
go test ./internal/profile/builtin
|
||||
go test ./... -run 'Profile|OpenAICompatibleProfile'
|
||||
go test ./...
|
||||
git diff --check
|
||||
```
|
||||
|
||||
Stage 1 is complete when all supported definition surfaces carry the normalized
|
||||
base ID, locally valid aliases can be stored, standalone validation is
|
||||
unchanged, and no repository performs inheritance resolution yet.
|
||||
|
||||
**Status:** Complete.
|
||||
|
||||
## Stage 2: Implement The Stateless Resolving Repository
|
||||
|
||||
### Objective
|
||||
|
||||
Add the internal component that resolves one complete profile chain, merges it
|
||||
deterministically, enforces safety bounds, and returns one ordinary resolved
|
||||
profile to its caller.
|
||||
|
||||
### Implementation
|
||||
|
||||
1. Add `internal/profile/resolving_repository.go` with:
|
||||
- an unexported `resolvingRepository` containing one underlying raw
|
||||
`Repository`;
|
||||
- `NewResolvingRepository(source Repository) Repository`; and
|
||||
- an unexported constant setting the maximum chain length to 32, counting
|
||||
the selected leaf.
|
||||
2. Make the wrapper safe for concurrent calls by keeping the source immutable
|
||||
and all chain, visited-ID, and merge state local to `GetProfile`. Do not add a
|
||||
cache, mutex, process-global registry, or source snapshot.
|
||||
3. Resolve one request using this exact flow:
|
||||
- reject a nil underlying repository or blank requested ID as
|
||||
`ErrInvalidProfile`;
|
||||
- trim and look up the directly requested ID through the underlying raw
|
||||
repository;
|
||||
- if that first lookup fails, return its error unchanged so direct
|
||||
`ErrProfileNotFound` behavior remains intact;
|
||||
- reject a nil profile returned without an error as `ErrInvalidProfile`;
|
||||
- follow each normalized nonblank `BaseProfileID` through the same underlying
|
||||
repository, recording IDs and profiles from leaf toward root;
|
||||
- check `ctx.Err()` between lookups;
|
||||
- detect a repeated ID before loading it again and return an
|
||||
`ErrInvalidProfile` cycle diagnostic containing the `a -> b -> a` chain;
|
||||
- reject a reference that would make the chain exceed 32 profiles with an
|
||||
`ErrInvalidProfile` diagnostic containing the traversed chain and next ID;
|
||||
- when a base lookup returns `ErrProfileNotFound`, return a new
|
||||
`ErrInvalidProfile` error naming the missing base and chain without
|
||||
wrapping `ErrProfileNotFound`; and
|
||||
- for any other base error, add `ErrInvalidProfile`, base ID, and chain
|
||||
context while preserving the source error and cancellation identity with
|
||||
wrapping.
|
||||
4. Merge the collected definitions from root to leaf into a new value. Use one
|
||||
focused helper with these rules:
|
||||
- retain the selected leaf `ID` and clear final `BaseProfileID`;
|
||||
- replace `BackendID`, `Endpoint`, `Model`, `ServiceTier`,
|
||||
`ReasoningEffort`, and `APIKeyEnv` only for a nonblank child value;
|
||||
- replace `Temperature`, `MaxTokens`, `TopP`, and `TimeoutSeconds` only for a
|
||||
nonzero child value;
|
||||
- set `APIKeyRequired` to the logical OR of every definition in the chain;
|
||||
- replace `ExtraParams` only when the child map is nonempty; otherwise
|
||||
inherit the current map; and
|
||||
- defensively deep-copy every retained or replacement `ExtraParams` map by
|
||||
using the existing bounded `internal/jsonvalue` owner. Never mutate a
|
||||
repository-returned profile or map. Treat an unexpected copy failure as
|
||||
`ErrInvalidProfile`, preserve the copy error, and include the selected
|
||||
chain in the diagnostic.
|
||||
5. Add an unexported final validator in `internal/profile` that requires a
|
||||
nonblank model and at least one backend or endpoint on the merged profile,
|
||||
and reuses the common endpoint and execution-setting validation. Wrap final
|
||||
failures with `ErrInvalidProfile` and the selected chain. The resolver must
|
||||
not perform backend registry membership lookup; that remains in
|
||||
`internal/usecase` after profile resolution.
|
||||
6. Return a caller-owned resolved profile on every successful call. A
|
||||
standalone profile passes through the same final validation and copy path,
|
||||
ensuring the wrapper does not create separate standalone and inherited
|
||||
execution contracts.
|
||||
|
||||
### Tests
|
||||
|
||||
Create `internal/profile/resolving_repository_test.go` and use small synthetic
|
||||
repositories plus the real overlay repository where it materially protects
|
||||
composition:
|
||||
|
||||
1. Prove a three-level root/middle/leaf chain merges representative values for
|
||||
every rule: inherited values, string and numeric replacements, independent
|
||||
backend/endpoint behavior, sticky `APIKeyRequired`, complete-map
|
||||
`ExtraParams` replacement, leaf identity, and cleared resolution metadata.
|
||||
Use one well-structured table or fixture rather than one test per field.
|
||||
2. Prove an empty child `ExtraParams` inherits the base map, a nonempty child
|
||||
map replaces rather than key-merges it, and mutating the returned nested
|
||||
values cannot mutate the raw repository or a later result.
|
||||
3. Prove the same composite repository resolves a base from a lower-precedence
|
||||
source and honors a higher-precedence shadow of that base ID. Do not add
|
||||
source-specific resolution to the wrapper.
|
||||
4. Cover the safety/error matrix through the resolving repository boundary:
|
||||
- directly selected missing ID preserves `ErrProfileNotFound`;
|
||||
- missing base matches `ErrInvalidProfile` and does not match
|
||||
`ErrProfileNotFound`;
|
||||
- direct and indirect cycles are rejected with useful chains;
|
||||
- exactly 32 profiles are accepted and a 33rd is rejected;
|
||||
- nil repository results and incomplete final profiles are invalid;
|
||||
- non-not-found base errors retain their original identity; and
|
||||
- cancellation before or during traversal remains discoverable and stops
|
||||
later lookups.
|
||||
5. Prove freshness and concurrency without timing sleeps: change a controlled
|
||||
raw repository between sequential calls and observe the new base value, then
|
||||
issue concurrent independent resolutions and verify results do not share
|
||||
mutable maps. Use synchronization owned by the fake; do not race on a plain
|
||||
map.
|
||||
6. Preserve the existing raw repository and overlay tests unchanged except
|
||||
where Stage 1 intentionally changed local completeness rules.
|
||||
|
||||
### Verification
|
||||
|
||||
```sh
|
||||
gofmt -w internal/profile/*.go
|
||||
go test ./internal/profile -run 'Resolving|Inheritance|Profile'
|
||||
go test -race ./internal/profile
|
||||
go test ./...
|
||||
git diff --check
|
||||
```
|
||||
|
||||
Stage 2 is complete when a standalone internal wrapper can safely and freshly
|
||||
resolve every accepted chain into one complete, copied profile with the fixed
|
||||
merge and error contracts.
|
||||
|
||||
**Status:** Complete.
|
||||
|
||||
## Stage 3: Assemble Inheritance And Prove Public Workflows
|
||||
|
||||
### Objective
|
||||
|
||||
Place the resolver at the correct engine boundary and prove that YAML,
|
||||
in-memory, built-in, inspection, ordinary preparation, runtime overrides, and
|
||||
prepared execution compose as intended through the public facade.
|
||||
|
||||
### Implementation
|
||||
|
||||
1. Update `newProfileRepository` in `engine.go` to:
|
||||
- assemble the raw repository in its current precedence order—built-ins,
|
||||
fallback, configured source or `Config.ProfileDir`, then in-memory;
|
||||
- wrap that complete raw repository exactly once with
|
||||
`profile.NewResolvingRepository`; and
|
||||
- return the wrapper to `usecase.NewRunner`.
|
||||
Do not wrap individual sources, because that would prevent a higher-level
|
||||
child from referencing a lower-level base and would change shadowing rules.
|
||||
2. Keep `internal/usecase.resolveProfileSelection` structurally unchanged. It
|
||||
should continue to request one profile, preserve the requested normalized ID
|
||||
as selection identity, resolve the final backend once, and then apply the
|
||||
ordinary backend/profile/request precedence. Do not pass `BaseProfileID`
|
||||
into `ExecutionTarget`, `PreparedRun`, `RunResult`, injected `LLMClient`
|
||||
requests, stable JSON, hashes, or provider payloads.
|
||||
3. Confirm `APIKeyRequired` behavior after inheritance without introducing a
|
||||
special credential path: a true value in any ancestor reaches the existing
|
||||
profile-to-target merge, clears inherited profile/backend `APIKeyEnv`, and
|
||||
still permits a direct request key or explicit request environment override.
|
||||
4. Preserve the current error mapper. The resolver's use of
|
||||
`ErrInvalidProfile` must make a missing/invalid base cross the use-case and
|
||||
root boundaries as `ErrProfileLoad`; no new root sentinel or special-case
|
||||
string matching is permitted.
|
||||
5. Do not modify built-in profile assets merely to demonstrate inheritance.
|
||||
Built-ins serve as valid bases without changing their own definitions.
|
||||
|
||||
### Tests
|
||||
|
||||
Use the smallest public tests that protect assembled behavior without repeating
|
||||
the resolver's matrix:
|
||||
|
||||
1. Add one external-package contract workflow that defines `weather-light` as
|
||||
an in-memory alias/refinement of the built-in `deepseek-4-flash`. Compare
|
||||
its inspected target with an inspection of the base rather than duplicating
|
||||
every built-in literal. Assert:
|
||||
- the child inspection reports `weather-light`;
|
||||
- backend and model are inherited;
|
||||
- explicit child reasoning and timeout values win;
|
||||
- `Prepare` reports the child as `SelectedProfileID`; and
|
||||
- a per-request reasoning or timeout override still wins over the child.
|
||||
2. Exercise YAML through `WithProfileFS` with a minimal alias of a built-in and
|
||||
prove it resolves through inspection or preparation. Parser tests own YAML
|
||||
edge cases, so do not repeat them here.
|
||||
3. Add a focused public error-identity table:
|
||||
- a directly selected absent profile still matches `ErrProfileNotFound` and
|
||||
not `ErrProfileLoad`;
|
||||
- an existing child with an absent base matches `ErrProfileLoad` and not
|
||||
`ErrProfileNotFound`; and
|
||||
- one representative cycle matches `ErrProfileLoad` and includes its IDs.
|
||||
Reuse or extend the existing profile-inspection public error owner where
|
||||
that keeps the suite lean.
|
||||
4. Prove fresh ordinary resolution and frozen prepared execution with one
|
||||
controlled mutable `fs.FS` workflow:
|
||||
- prepare an execution while its base supplies model A;
|
||||
- change the base source to model B after preparation;
|
||||
- run the prepared handle through an injected deterministic client and
|
||||
observe model A; and
|
||||
- perform a new inspection or ordinary preparation and observe model B.
|
||||
Keep all mutation sequential or synchronized and make no provider call.
|
||||
5. Add only a narrow credential assertion if existing use-case tests do not
|
||||
already prove the final inherited `APIKeyRequired` target behavior. The
|
||||
resolver test owns sticky merging; current credential tests own availability,
|
||||
precedence, redaction, and prepared rechecking.
|
||||
6. Run all existing source-precedence, fallback, malformed-profile, built-in,
|
||||
public JSON, runtime-override, credential, and prepared-execution tests. Fix
|
||||
production behavior rather than weakening those contracts.
|
||||
|
||||
### Verification
|
||||
|
||||
```sh
|
||||
gofmt -w engine.go engine_test.go public_contract_test.go prepared_execution_contract_test.go profiles.go types.go
|
||||
go test ./... -run 'Profile|Inheritance|PreparedExecution|RuntimeOverride'
|
||||
go test ./internal/profile
|
||||
go test ./internal/usecase
|
||||
go test -race ./...
|
||||
go vet ./...
|
||||
go build ./...
|
||||
git diff --check
|
||||
```
|
||||
|
||||
Stage 3 is complete when the engine resolves aliases and refinements across all
|
||||
supported sources, public identities and errors match the roadmap, ordinary
|
||||
operations remain fresh, and prepared execution remains frozen.
|
||||
|
||||
**Status:** Complete.
|
||||
|
||||
## Stage 4: Publish Canonical Documentation And Complete Validation
|
||||
|
||||
### Objective
|
||||
|
||||
Align every canonical contract owner with the implemented feature, remove
|
||||
temporary ambiguity, and validate the release candidate comprehensively.
|
||||
|
||||
### Documentation
|
||||
|
||||
1. Finalize exact GoDoc for `Profile.BaseProfileID` and
|
||||
`OpenAICompatibleProfileConfig.BaseProfileID`, plus nearby `Profile`,
|
||||
`OpenAICompatibleProfile`, and `WithProfiles` text whose standalone
|
||||
completeness or validation-timing statements changed. GoDoc owns the public
|
||||
fields and construction contract; do not reproduce internal traversal.
|
||||
2. Update `docs/formats.md` as the exact YAML contract owner:
|
||||
- add `base_profile` to the field table and example;
|
||||
- distinguish locally complete standalone profiles from derived aliases;
|
||||
- document root-to-leaf override rules, complete-map `extra_params`
|
||||
replacement, sticky `APIKeyRequired`, independent backend/endpoint
|
||||
behavior, the 32-profile limit, and lack of clearing syntax;
|
||||
- document cross-source lookup precedence and shadowing; and
|
||||
- distinguish a missing selected profile from an invalid selected chain.
|
||||
3. Update `docs/internal/sources.md` to describe the raw composite catalog,
|
||||
the one outer resolving repository, fresh per-operation traversal,
|
||||
cycle/depth protection, final validation, and prepared-state freezing. Link
|
||||
to the format reference for exact consumer syntax and merge rules.
|
||||
4. Update the `internal/profile` entry in `docs/internal/overview.md` because
|
||||
the concrete package now resolves inherited definitions in addition to
|
||||
loading, validating, and overlaying raw sources. Do not change architecture
|
||||
policy because package ownership and dependency direction are unchanged.
|
||||
5. Add one concise task-oriented example to
|
||||
`docs/consumers/pkg-promptkit.md` showing an application-owned alias of a
|
||||
built-in and a selected child override. Link to the format reference and
|
||||
public GoDoc for details rather than duplicating the complete contract.
|
||||
6. Do not update the README, examples, release documents, release procedure,
|
||||
future/deferred catalogs, or built-in catalog unless the implementation has
|
||||
made a concrete current-state statement there inaccurate. Do not copy the
|
||||
roadmap into durable documentation.
|
||||
|
||||
### Final Validation
|
||||
|
||||
Run the complete maintainer workflow from
|
||||
[`docs/development.md#maintainer-validation`](../development.md#maintainer-validation):
|
||||
|
||||
```sh
|
||||
go test ./...
|
||||
go test -race ./...
|
||||
go vet ./...
|
||||
go build ./...
|
||||
go run ./examples/go-library/prepare
|
||||
go run ./examples/go-library/run
|
||||
```
|
||||
|
||||
Then run the documented tracked-Go formatting check, repository-relative
|
||||
Markdown link checker, workspace/vendor/module-replacement checks, unstaged and
|
||||
staged whitespace checks, ignored-file check, credential scan, and
|
||||
`git diff --check`. Inspect the complete diff and confirm:
|
||||
|
||||
- only `base_profile`/`BaseProfileID` were added to consumer configuration;
|
||||
- raw repositories remain source-local and the resolver wraps the full catalog
|
||||
once;
|
||||
- chain resolution is bounded, cycle-safe, fresh, and concurrency-safe;
|
||||
- merge behavior covers every profile field without mutating source values;
|
||||
- direct missing and dependent missing profiles preserve distinct public error
|
||||
identities;
|
||||
- selected IDs, backend resolution, runtime overrides, credentials, and
|
||||
prepared freezing retain their stated ownership;
|
||||
- resolution metadata never reaches stable execution JSON, hashes, injected
|
||||
generation targets, or provider payloads;
|
||||
- tests remain lean, deterministic, offline, and owned at the narrowest stable
|
||||
boundary; and
|
||||
- durable documentation describes only implemented behavior with one canonical
|
||||
owner per exact contract.
|
||||
|
||||
Stage 4 is complete when all focused and repository-wide checks pass and the
|
||||
implementation, GoDoc, format reference, internal documentation, and consumer
|
||||
guidance agree.
|
||||
|
||||
**Status:** Complete.
|
||||
|
||||
## Open Questions
|
||||
|
||||
None. Naming, supported sources, lookup precedence, merge behavior, connection
|
||||
semantics, credential inheritance, validation timing, chain bounds, error
|
||||
identity, freshness, prepared freezing, package ownership, test boundaries, and
|
||||
documentation ownership are fixed by the feature roadmap and this plan.
|
||||
Reference in New Issue
Block a user