236 lines
8.5 KiB
Markdown
236 lines
8.5 KiB
Markdown
# 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.
|