13 KiB
Captured Configuration Comparison Implementation Plan
Implement the feature roadmap in the numbered order below. Each stage is one bounded implementation prompt for a Luna coding agent. The feature roadmap owns the purpose and target contract; this document owns the steps.
Working Instructions
- Start with the development guide and follow the agent policy. Read the architecture, testing, documentation, and OSS policies for the stages they govern.
- Preserve ADR 0001: compare captured ConfigKit effective values for explicit inspection. Leave domain execution, configuration validation, and source processing unchanged.
- Use the released ConfigKit dependency, not a committed local replacement or workspace file. The sibling checkout is a reference, not a build prerequisite.
- Keep the upstream comparator and existing sensitivity classifier authoritative. Do not add local diff algorithms, masking rules, output limits, reloads, formatters, or execution-equivalence checks in this work.
- After each stage, review the complete relevant diff, run its checks, and commit the completed unit with a plain-English message. Record actual validation results in the handoff; do not bypass hooks or publish a release.
Current Baseline And Selected Decisions
At planning time, PromptKit uses ConfigKit v0.6.0. The public inspection methods
are in configuration_inspection.go; internal/configuration/inspection.go
defines their narrow Inspection interface and currently returns the retained
*configkit.Result[document] directly. The internal engine stores that interface.
Root inspection contract tests live in
configuration_inspection_contract_test.go.
Use ConfigKit v0.7.0, whose reviewed commit is
8f3396d6f0cbe03af4f92bb2c1a1c9798171dd52.
The public addition is exactly:
func (v ConfigurationInspection) Diff(other ConfigurationInspection) ([]configkit.Difference, error)
The receiver is left/earlier and the argument is right/later. Either zero view
returns ErrInvalidConfig and no records. Successful calls forward ConfigKit's
records; errors from ConfigKit remain inspectable through ordinary Go error
handling. Do not promise a particular nil-versus-empty slice on success or exact
error wording. No context parameter or PromptKit-specific difference type is
needed.
For the private bridge, retain the existing Inspection interface and add an
unexported accessor for the captured *configkit.Result[document]. Implement the
interface with a private wrapper around that result. Existing inspection methods
can be promoted from an embedded result; the accessor supplies the result to an
internal-package comparison function. This keeps the implementation small without
runtime type assertions or exposing the configuration type through the facade.
This wrapper is an implementation choice, not a new consumer contract.
Stage 1: Adopt ConfigKit v0.7.0
Status: Complete. The selected tag resolves to reviewed commit
8f3396d6f0cbe03af4f92bb2c1a1c9798171dd52; upstream COPYRIGHT and LICENSE
are unchanged from v0.6.0. GOTOOLCHAIN=local go test ./...,
GOTOOLCHAIN=local go vet ./..., GOTOOLCHAIN=local python3 scripts/check-examples.py, and git diff --check passed.
Scope: dependency and attribution changes only.
- Read the upstream v0.7.0 release notes and
diff.go, and verify the selected tag's commit and licensing notices against the baseline above. - Update the dependency with
go get gitea.maximumdirect.net/eric/ConfigKit@v0.7.0, thengo mod tidy. Reviewgo.modandgo.sum; avoid unrelated upgrades, Go-version changes, or local replacements. - Update the ConfigKit entry in
docs/oss/attribution.md: selected version and all upstream source, COPYRIGHT, and LICENSE links must use the exact imported commit. Preserve the house format and actual upstream notices. - Run
go test ./...,go vet ./..., andpython3 scripts/check-examples.py. Investigate regressions rather than weakening existing loading or execution contracts to accommodate them. - Review the dependency/notice diff and run
git diff --checkbefore committing.
Completion: PromptKit uses the published v0.7.0 module, current behavior passes existing validation, and attribution matches the selected source revision.
Stage 2: Add The Private Bridge And Public Method
Status: Complete. Added the private captured-result bridge and public
ConfigurationInspection.Diff method. External-package tests cover changed and
equal values, operand direction, self-comparison, and all zero-view pairs.
GOTOOLCHAIN=local go test . ./internal/configuration ./internal/engine and
git diff --check passed.
Scope: the small integration path and basic facade tests.
- In
internal/configuration/inspection.go, add the private wrapper and captured-result accessor described above. Updateloaded.Inspectionto return the wrapper for a captured result, keeping absent state represented by a nil inspection. Preserve all existing inspection operations. - Add an internal-package
Diff(left, right Inspection)function returning([]configkit.Difference, error). Obtain the captured results through the accessor and callconfigkit.Diff[document]once. If an internal operand is absent, pass a nil result to ConfigKit so its existing error behavior applies. Do not reload, reconstruct snapshots, or independently compare values. - Add
ConfigurationInspection.Diffto the root facade. Check both source interfaces for nil first and returnnil, ErrInvalidConfigfor a zero view; otherwise delegate to the bridge. Return upstream errors directly unless useful context is needed, in which case wrap with%w. - Add GoDoc for direction, zero-view errors, frozen effective-value scope, and ConfigKit-owned difference semantics. Note that reports can include ordinary application data and are not evidence of equivalent execution behavior.
- Add external-package root tests using independent engines with synthetic
mapping inputs and an explicitly defined local backend/profile. Verify a
model-value change at a known path, reversed operand direction, equal values,
self-comparison, and the three zero-view combinations. Use
errors.IsforErrInvalidConfigandlen(records) == 0for successful equality. - Run
gofmton changed Go files andgo test . ./internal/configuration ./internal/engine, thengit diff --check. Existing inspection tests must continue to pass.
Completion: the public signature works from package promptkit_test, the private
result remains inaccessible through the facade, and unchanged inspection methods
still behave as before. No runner or domain-package integration is needed.
Stage 3: Verify Result Semantics And Sensitive Changes
Status: Complete. Facade tests cover distinct source labels with equal
effective values, absent versus null, atomic sequence changes, paired secret
masking, sensitive shape replacement, and mutation of returned records.
GOTOOLCHAIN=local go test . ./internal/configuration and
git diff --check passed.
Scope: focused facade integration tests, not a second comparator test suite.
- Extend the comparison tests with two valid engine configurations whose effective values are equal but whose underlays/source labels differ. Verify an empty diff; history differences must not become value differences.
- Use valid JSON-compatible values in a synthetic provider
extra_paramsmap to exercise absence versus explicit null, and a changed sequence. Verify the forwardedPresentflags and one atomic sequence-region record. Compare semantic values rather than incidental YAML whitespace. - Add a focused sensitivity case using an existing credential-shaped key such
as
backends.local.extra_params.token. Change its synthetic secret value and a public sibling. Verify that neither operand's secret appears in either snapshot, that redaction information andHiddenChangedsurvive the bridge, and that the public change remains visible. - Include one valid shape-change case: an
extra_params.detailsmapping with a nestedtokenon one side and a scalar atdetailson the other. This places the sensitive descendant on only one side and exercises paired protection of the changed region. Use ConfigKit's documented masking and record granularity; do not require public descendants to remain visible in an incompatible shape change or altersecretProviderPath. - Mutate a returned record's snapshot data and any mutable path representation offered by the upstream public API, then compare again. Verify retained state is unaffected without asserting upstream private allocation details.
- Run
go test . ./internal/configurationandgit diff --check; format any changed Go files. Reuse existing fixture helpers where they fit naturally.
Completion: tests protect PromptKit's forwarding, operand pairing, sensitive-data handling, and result ownership. Upstream retains ownership of exhaustive mapping, sequence, sensitivity, and ordering cases.
Stage 4: Verify Frozen State And Concurrent Inspection
Scope: lifecycle and concurrency integration tests.
- Reuse or extend the existing captured-source inspection fixture and counting injected model client. Construct both engines while their sources are readable; afterward make those sources unreadable or change their contents. Obtain views and compare them repeatedly. Assert the captured differences remain unchanged and comparison causes no additional source reads or model calls. Set any environment values during setup, not concurrent operations.
- Exercise several concurrent comparisons on the same pair of views, together with an existing inspection operation. Validate returned semantic results and collect goroutine errors safely. Avoid sleeps, timing-based thresholds, or assertions about exact internal call sequences.
- Where useful, extend the existing concurrent inspection/execution fixture to include comparisons alongside offline runs. Reuse its coverage instead of building a duplicate lifecycle harness.
- Run
go test -race . ./internal/configuration ./internal/engineandgit diff --check; format changed Go files.
Completion: the comparison path demonstrably uses captured state and supports concurrent read-only use without involving prompt execution machinery.
Stage 5: Document The Feature And Validate The Integrated Change
Scope: canonical documentation and final verification.
- Add a small comparison example to the configuration-inspection section of
docs/consumers/pkg-promptkit.md. Show two captured views, error handling, and iteration over returned records. Link to GoDoc and ConfigKit's v0.7.0 comparison guidance for exact semantics rather than duplicating its API. - Explain the private inspection wrapper and comparison bridge in
docs/internal/sources.md. Refresh the existing ConfigKit v0.6.0-specific normalization discussion against v0.7.0 without changing message resource origins or execution normalization as part of this feature. - Keep the example and GoDoc explicit about captured configuration equality: external file contents, resolved credential contents, post-load execution preparation, and per-request overrides are not execution-equivalence inputs. Retain the existing caller responsibility for report disclosure.
- If the pending v0.13.0 release document is still unpublished and is the chosen release for this work, add a short feature/dependency note with a canonical documentation link. Otherwise leave published notes unchanged and record the feature during the next release preparation. Do not tag, push, or run a publication command in this stage.
- Run
go test -race ./...,go vet ./...,python3 scripts/check-examples.py, andgit diff --check. Check affected local links and upstream links against the selected release's files. Review exported declarations to confirm only the intended method was added. - Review the final diff against every work item in the feature roadmap, confirming comparison/masking are delegated upstream and runtime/domain components do not acquire ConfigKit inspection dependencies. Commit the completed documentation and any required integration corrections.
Completion: the dependency, API, tests, attribution, and canonical documentation are coherent and the full existing suite and maintained examples pass. Report stage commits and actual check results; release publication remains separate.
Open Questions
None. The accepted roadmap and the existing inspection error convention supply the decisions required for these stages.