Files
promptkit/docs/roadmap/implementation.md

255 lines
14 KiB
Markdown

# Profile And Prompt Listing Implementation Plan
Implement the [listing roadmap](listing.md) in the numbered order below.
Each stage is scoped for one Luna coding-agent prompt and should leave a
compiling, tested unit. All four stages are pending.
This replaces the completed performance plan, preserved in
[Git history](https://gitea.maximumdirect.net/eric/promptkit/src/commit/7325a34c070c77c4784ab76c916fc8a4d79b5c46/docs/roadmap/implementation.md).
For older inspection-roadmap references to this pathname, the completed
[inspection plan](https://gitea.maximumdirect.net/eric/promptkit/src/commit/481e2d494c5a4e8b7f3a2a6f1cceaeb3f7c71d05/docs/roadmap/implementation.md)
is also preserved. These historical plans are not work for this sprint.
## Baseline And Working Rules
Planning baseline: `7325a34c070c77c4784ab76c916fc8a4d79b5c46`.
There are no listing methods yet. Configuration already captures profiles in
`loaded.indexedProfiles` and prompts in `loaded.prompts`, after the final
ConfigKit handoff. `internal/engine.Engine` currently retains the runner and
the operator inspection view.
Start with [development](../development.md), then read the applicable
[agent](../policy/agents.md), [architecture](../policy/architecture.md),
[testing](../policy/testing.md), and
[documentation](../policy/documentation.md) policies. Reuse unchanged documents
already read. Consult [internal sources](../internal/sources.md), the
[component overview](../internal/overview.md), the
[consumer guide](../consumers/pkg-promptkit.md), existing inspection GoDoc, and
the [configuration ADR](../adr/0001-configuration-boundary.md).
For each stage: inspect the current worktree, preserve unrelated edits, implement
only its scope, format changed Go files, run focused verification, review the
diff, update this plan's stage status with brief evidence, and commit the
nonempty completed unit with a plain-English message. Report any blocked
validation honestly. Publication and release preparation are separate tasks.
Use the existing module versions. No dependency upgrade, configuration-format
change, execution refactor, new public package, or release-tool change is needed.
The storage and wiring choices below are implementation decisions, not permanent
consumer invariants. Tests protect outcomes, not private helper names, clone
counts, allocation counts, or timing thresholds.
## Selected Public Contract
Use these root-package declarations:
```go
type PromptIdentity struct {
ID string
Version string
}
func (e *Engine) ListProfiles(ctx context.Context) ([]string, error)
func (e *Engine) ListPrompts(ctx context.Context) ([]PromptIdentity, error)
```
- List final effective captured identities, including maintained and consumer
profiles. Inherited profiles keep their own keys. Return each profile ID and
each prompt ID/version pair once; omit historical-only contributions.
- Preserve literal spelling. Sort profiles lexically by ID; sort prompts
lexically by ID, then version. Do not interpret versions as SemVer or select
a preferred version. Existing omitted-version inspection remains unchanged.
- Results are caller-owned. Concurrent listing and other supported engine
operations cannot mutate the captured inventory through these results.
- Empty inventories succeed. Nil and zero-value public engines return an error
matching `ErrInvalidConfig`, consistent with existing facade checks.
- For a valid engine, context cancellation/deadline errors retain their standard
identity. Return no partial inventory on error. Follow existing inspection
precedence: validate the public engine before delegating context handling.
Check context before and after the synchronous copy/conversion work; do not
promise immediate interruption or deterministic precedence in a cancel/return
race. No new nil-context behavior is selected.
- `PromptIdentity` follows other inspection values: no stable JSON
representation is promised. Do not add custom serialization or pin nil versus
nonnil empty slices in GoDoc or tests.
- Membership is not execution readiness. Listing performs no per-item full
inspection, target validation, credential resolution, resource reads,
rendering, admission, generation, or ConfigKit provenance traversal.
The roadmap owns scope and intent; implemented GoDoc will own the exact API.
Do not add backend listing, filters, pagination, reloads, runtime mutation,
provider discovery, new caller limits, or stronger catalog availability promises.
## Stage 1 — Capture And Read Identity Inventories
**Status: Pending.**
**Scope:** `internal/domain`, `internal/configuration`, and focused adapter tests.
1. Add an internal `domain.PromptIdentity` with only `ID` and `Version` strings.
Keep it separate from the eventual public declaration; no public type alias.
2. Add private profile-ID and prompt-identity slices to `loaded`. Populate them
once at the end of `load`, after both `materializePrompts` and
`buildExecutionProfiles` have succeeded. Enumerate the finished
`indexedProfiles` keys and the finished `prompts` ID/version keys, not
ConfigKit metadata or raw contributions.
3. Build the ordered slices with existing `sortedKeys` or standard-library
sorting. Flatten actual prompt versions; an ID with no versions contributes
no prompt identity. Store only strings, not targets, definitions, or histories.
4. Add `ListProfiles(context.Context) ([]string, error)` and
`ListPrompts(context.Context) ([]domain.PromptIdentity, error)` on `loaded`,
in a focused `internal/configuration/listing.go`. Check context, copy the
captured slice, check context again, and return the independent result.
Ordinary slice copying suffices because elements contain only strings.
Reads are linear in returned inventory size and require no repeated sorting.
5. Add adapter tests using the real `Load` and existing mapping helpers. Cover
a small unsorted profile/prompt fixture, multiple versions such as `"10"`
and `"2"`, literal whitespace/slash keys, a partial profile, and an empty
load. Mutate returned entries and verify subsequent lists are unchanged.
Cover an already-canceled context for each method without timing assertions.
Do not change existing lookup methods or add inventory mutation. Keep
construction complexity proportionate; there is no benchmark gate.
**Verify:** format changed files; run
`go test ./internal/configuration ./internal/domain` and
`go vet ./internal/configuration ./internal/domain`.
**Done when:** successful loads retain sorted identity-only inventories and
return owned lists without rereading ConfigKit or requiring runnable targets.
## Stage 2 — Wire The Public Facade And Its Basic Contract
**Status: Pending.** Depends on Stage 1.
**Scope:** `internal/engine`, root API/conversion, and focused facade tests.
1. Add a private, consumer-owned inventory interface in `internal/engine`
containing just the two listing methods from Stage 1. Store the loaded
snapshot through this interface during `New`, alongside the existing
operator inspection view. Delegate new internal engine methods to it.
There is no need to extend execution repository interfaces or the use-case
runner, add optional capabilities, or provide a full-inspection fallback.
2. Add `listing.go` at the module root, containing public `PromptIdentity` and
the two methods. Apply the same `e == nil || e.internal == nil` check as
existing inspection methods, returning `ErrInvalidConfig` with useful
context. Map delegated errors through `mapPublicError`.
3. Return the already-owned profile slice. Convert internal prompt identities
into a separate public slice by copying their two strings. Check context
after conversion so a detected cancellation yields no partial result.
Do not export an internal type in any public signature.
4. Write concise GoDoc covering effective membership, exact lexical ordering,
all prompt versions, captured-state behavior, caller ownership, concurrent
use, empty success, nil-engine and context errors, and absence of readiness
checks or external work. Identifiers may be sensitive application data;
do not describe these lists as automatically safe for disclosure.
5. Add external-package basic tests in `listing_contract_test.go`: a usable
public engine lists a configured profile and two exact prompt versions;
returned identities can be inspected; nil/zero engines match
`ErrInvalidConfig`; canceled and expired contexts preserve context identity
with no partial inventory. Use real constructors and synthetic configuration.
**Verify:** format changed files; run
`go test ./internal/engine .` and `go vet ./internal/engine .`.
Existing root boundary tests must continue to pass; do not weaken them.
**Done when:** consumers can call both methods through the root facade and
receive independent public values with the selected error behavior.
## Stage 3 — Verify Effective Membership, Ownership, And Concurrent Use
**Status: Pending.** Depends on Stage 2.
**Scope:** focused additions to the external-package listing contract tests.
Production edits are limited to defects those tests reveal.
1. Build one representative layered engine with a consumer base profile, an
inherited child, an overlay of the same profile ID, and multiple prompt
versions. Verify effective identities, no duplicate overlay entry, and
child/base identities remaining distinct. Inspect one listed identity to
confirm it resolves to the effective captured definition.
2. Verify maintained profiles are included without asserting a complete catalog
inventory or a specific upstream model. The two PromptKit-owned standard
profile names may serve as stable membership examples under the existing
release policy; other expectations should use fixture-controlled identities.
3. Exercise empty effective inventories through a public engine: a final main
mapping with `profiles: null` and `prompts: null` clears the sections above
the catalog underlays. Assert successful zero-length lists, not a particular
nil/empty representation. This also checks that lower-layer historical
profile paths are not returned. Use existing ConfigKit semantics; add no
source-validation pass or special listing-only removal rule.
4. Mutate elements in both public results and relist to verify independent
storage across calls. Add one bounded concurrent test that lists and mutates
independent results while other goroutines list/inspect the same captured
engine. Coordinate deterministically; avoid sleeps or shared test-owned
mutable result slices.
5. Cover snapshot/no-execution behavior with a compact fixture: construct from
an application filesystem, then remove/change its content after construction
and verify listing still reports the captured identities. Inject existing
model-client and artifact-reader stubs that fail if invoked. Include a
partial profile or a named but unavailable API-key environment variable;
listing succeeds without provider readiness. Do not add new extension points
or exact private-call-count assertions solely for these tests.
Place effective-loading/ordering mechanics in Stage 1's adapter tests and
public assembled outcomes here. Avoid duplicating every loader, inheritance,
or existing inspection case. No execution/network call is required to prove
identity discovery.
**Verify:** `go test -race ./internal/configuration ./internal/engine .`.
**Done when:** public tests demonstrate effective—not historical—membership,
independent results, concurrent safety, and listing without execution work.
## Stage 4 — Document And Verify The Completed Feature
**Status: Pending.** Depends on Stage 3.
**Scope:** owning documentation, final review, and complete offline verification.
1. Update `doc.go` to include listing in its operation and concurrent-use
summaries, and `PromptIdentity` among values without stable JSON contracts.
Keep exact behavior in the method/type GoDoc from Stage 2.
2. Add a short consumer-guide discovery example listing identities and then
inspecting a selected entry. Distinguish listing from configuration history
and from execution readiness; link to the exact GoDoc. A new runnable example
project or CLI is unnecessary.
3. Update `docs/internal/sources.md` and `docs/internal/overview.md` for captured
inventories and direct internal-engine delegation. The execution runner has
not acquired a listing responsibility. Add a brief format-reference link to
the listing guidance where it currently introduces inspection, without
changing configuration semantics.
4. Repair old `implementation.md` links in the completed `performance.md`,
`inspection.md`, and historical sections of `remediation.md` to their
respective history links at the top of this plan. Preserve finding statuses
and recorded evidence; do not retire unrelated roadmaps or imply their
work remains pending. This is navigation repair, not another feature.
5. Review the entire feature diff against the listing roadmap and selected
contract. Confirm only effective indexes are used; no runtime imports of
ConfigKit, provenance traversal, readiness checks, resource reads, new
limits, or stable serialization promises have been introduced.
6. Run the complete offline race suite and full vet, plus maintained examples
if changed or affected. Validate changed documentation links and
`git diff --check`. Reuse successful focused checks for unchanged content.
7. Mark the listing roadmap and this plan complete with concise verification
evidence. Commit the completed unit. Leave release notes, roadmap retirement,
tags, and publication for the later release-preparation task.
Use the existing local Go installation and prepared module cache. Suggested
final commands (not new release gates):
```sh
GOTOOLCHAIN=local GOWORK=off GOPROXY=off GONOPROXY=none GOSUMDB=off \
go test -race -vet=off -mod=readonly -count=1 ./...
GOTOOLCHAIN=local GOWORK=off GOPROXY=off go vet -mod=readonly ./...
git diff --check
```
**Done when:** all four stages are verified, listing is documented by its
canonical owners, and the feature is ready for ordinary release preparation.
## Open Questions
None. The selected signatures, result shape, ordering, ownership, error
behavior, implementation boundary, and verification scope are specified above.