spec(prompt): add project map prompt injection change set

This commit is contained in:
2026-06-11 22:56:20 +02:00
parent 58e8bd31d3
commit c11d49d015
4 changed files with 639 additions and 0 deletions
@@ -0,0 +1,235 @@
# Design: Project Map Prompt Injection
## Status
| Field | Value |
|---|---|
| Phase | **Design** |
| Based on | [Spec](spec.md) |
| Next | Tasks |
## Design summary
This change adds a runtime guidance layer on top of the paired map/index artifact system. The implementation should not redesign map generation or retrieval. Instead, it should add a well-scoped injection pipeline that:
- emits honest startup hints before init,
- preloads the root pair after init,
- expands under a hybrid budget,
- avoids redundant reinjection by scanning real outgoing context,
- varies guidance strength through explicit modes,
- proves correctness with integration-heavy validation.
## Affected areas
### Source files likely to change
- `pi-extension.ts`
- shared config handling (`src/config.ts` or equivalent)
- runtime helpers for map/index discovery and injection selection
- tests covering extension lifecycle and per-turn behavior
- docs/runtime guidance surfaces if needed
### New modules likely to appear
- `src/prompt-injection.ts` or equivalent runtime helper
- optional helper for canonical marker construction / detection
- optional helper for token-budget estimation across paired artifacts
## Architecture changes
### 1. Injection policy helper
Centralize prompt-injection logic in one helper rather than scattering it across hooks.
Suggested responsibilities:
- determine current mode (`off` / `advisory` / `strong` / `strict`),
- detect whether artifacts exist,
- build pre-init startup hint,
- build post-init root-pair payload,
- estimate expansion budget,
- choose additional artifacts under the budget,
- construct a canonical injected marker/block,
- scan outgoing context or payload for that marker,
- decide whether reinjection is required.
This helper is the main guard against drift between startup hints, context hooks, and strict-mode checks.
### 2. Injection surfaces
Use different extension surfaces for different responsibilities.
#### Pre-init / startup hint
Use `before_agent_start` for lightweight startup guidance before real artifacts exist.
Required behavior:
- inject only a hint,
- tell the agent to run `project_map_init`,
- keep the hint visible and inspectable.
#### Post-init root-pair preload
Use `before_agent_start` to guarantee the initial post-init root-pair preload for a prompt.
Required behavior:
- inject root `.pi-map.index.md`,
- inject root `.pi-map.md`,
- optionally append brief protocol text only if needed by the selected mode.
#### Relevant-turn reinjection
Use `context` for relevant-turn checks.
Required behavior:
- inspect `event.messages`,
- decide whether the canonical block is already present,
- only add the root pair / budgeted expansion if absent,
- avoid rescanning on every trivial turn in `strong` mode.
#### Payload fallback
Use `before_provider_request` only as a fallback or debugging surface when message-layer detection is insufficient.
### 3. Canonical marker design
Deduplication depends on stable canonical detection.
The implementation should stamp injected content with a canonical marker block.
Recommended v1 shape:
- a deterministic wrapper such as `<!-- PI_MAP_ROOT_PAIR_START -->` / `<!-- PI_MAP_ROOT_PAIR_END -->`,
- normalized artifact identity lines for root `.pi-map.index.md` and root `.pi-map.md`,
- trust-boundary text within the same wrapped block when the active mode requires it.
Marker rules:
- stable across turns,
- independent of provider formatting quirks,
- easy to scan in both `event.messages` and final provider payload,
- robust enough that root-pair presence can be detected without brittle full-text matching.
### 4. Budgeting
Budgeting should be deterministic and layered.
#### Context-window discovery and fallback
- Prefer active-model context-window metadata exposed by Pi runtime/model selection.
- If context-window metadata is unavailable, fall back to the configured absolute cap.
- The fallback path should be explicit in logs/debug behavior so budget decisions remain auditable.
#### Required order
1. compute effective budget from relative percentage + optional absolute cap,
2. reserve the root pair first,
3. expand outward using a deterministic traversal order,
4. stop when the budget would be exceeded.
#### Traversal strategy
The spec does not force exact traversal heuristics, but the design should prefer:
- root pair first,
- shallow structural coverage before deep leaves,
- predictable order over opaque scoring.
This keeps automatic injection understandable and auditable.
### 5. Mode control surface
Expose a config/runtime setting for the four modes.
Because the current project config is loaded from flat JSON in `.pi-project-map.json`, v1 should prefer a flat compatible shape rather than forcing an immediate nested/YAML migration.
Suggested v1 config shape:
```json
{
"promptInjectionMode": "strong",
"contextBudgetPercent": 15,
"contextBudgetMaxTokens": 100000
}
```
Migration note:
- keep existing flat JSON loading in `src/config.ts`,
- treat these as new additive keys,
- decide explicitly whether existing `contextBudget` is deprecated, ignored for injection, or retained only for older LLM-analysis paths.
Optional runtime UX may later mirror thinking-level controls, but v1 implementation can start with config-driven mode selection as long as the semantics are the same.
### 6. Mode semantics
#### `off`
- no automatic artifact injection,
- no startup/init hint beyond existing tool/docs discovery.
#### `advisory`
- startup/init hints enabled,
- optional root-pair preload,
- light reminders,
- weaker reinjection behavior.
#### `strong`
- root-pair preload required,
- budgeted expansion required,
- relevant-turn reinjection checks required,
- reminders before edits and architecture-sensitive reasoning.
#### `strict`
- everything in `strong`, plus:
- when protocol path is missing during sensitive actions, require explicit bypass justification.
### 7. Relevant-turn detection
`strong` mode should not rescan on every turn.
Relevant-turn triggers should be mapped to concrete runtime signals where possible:
- agent start,
- edit intent / edit tool preparation,
- architecture-sensitive planning prompts,
- compaction completion,
- root-pair artifact change detection.
This will likely require some combination of:
- hook-local heuristics,
- observed tool calls,
- file timestamp/hash checks for root artifacts,
- compaction event handling.
### 8. Visibility model
The design should preserve mixed visibility.
- startup hints: visible and inspectable,
- raw injected artifact blocks: agent-visible by default,
- the fact that automatic injection exists should remain discoverable.
The implementation may use hidden custom message types for artifact payloads, but should not make startup/init behavior opaque.
### 9. Validation strategy
This change is validation-heavy.
Unit tests alone are not enough, because the main risk is runtime interaction between hooks, message mutation, compaction, and reinjection.
#### Integration focus areas
- startup before init,
- startup after init,
- root-pair marker insertion,
- reinjection suppression when marker already exists,
- reinjection after compaction,
- reinjection after artifact mutation,
- mode differences,
- strict-mode bypass path,
- mixed visibility expectations,
- synthetic event-sequence coverage for relevant-turn heuristics such as edit-intent, architecture-sensitive reasoning, compaction, and artifact invalidation.
### 10. Documentation impact
Runtime guidance docs must align with the spec, but retrieval docs remain separate.
Docs should teach:
- startup hint before init,
- root-pair automatic preload after init,
- trust boundary,
- mode ladder,
- relevant-turn reinjection behavior,
- integration-test importance.
## Risks
| Risk | Mitigation |
|---|---|
| Message-level scanning misses provider serialization quirks | Add payload fallback via `before_provider_request` |
| Root-pair marker becomes brittle | Use deterministic boundaries and normalized artifact identity lines |
| 15% + 100k is too aggressive in some fleets | Keep both knobs configurable and document the opinionated default |
| Relevant-turn detection becomes fuzzy | Centralize detection heuristics and prove them with integration tests |
| `strict` mode causes friction | Keep `strong` as default and isolate strict-only bypass behavior |
## Settled defaults
- Root pair is always guaranteed after init.
- Reinjection avoidance is based on canonical outgoing-context scanning.
- Retrieval remains out of scope.
- Mixed visibility is the intended baseline.
- Four modes exist, with `strong` as default.
- Default budget is 15% of active context window, capped at 100k tokens.