# SF Prompt Modularization

Human-readable contract for the manifest-driven prompt context system.

## Status

M004 — Phase 3 complete. All builders migrated to `composeUnitContext` v2.

## Overview

Every autonomous unit type (plan-milestone, execute-task, deploy, …) declares
what context it needs in a **manifest**. The composer reads the manifest and
orchestrates artifact resolution, producing the `## Inlined Context` block that
each prompt template receives.

This replaces the pre-Phase-3 approach where each builder hand-wired file reads
and DB queries inline.

## Two composer APIs

| API | Purpose | When to use |
|-----|---------|-------------|
| `composeInlinedContext(unitType, resolveArtifact)` | v1 — resolves `artifacts.inline` keys only | **Deprecated.** Retained in `unit-context-composer.js` for external consumers. Do not use in new builders. |
| `composeUnitContext(unitType, opts)` | v2 — resolves `prepend`, `inline`, `excerpt`, and `computed` artifacts | **Use for all new builders.** All in-tree builders now call this. |

### `composeUnitContext` contract

```ts
composeUnitContext(unitType: string, opts: {
  base: string;                          // repo root
  resolveArtifact?: (key: string) => Promise<string | null>;
  resolveExcerpt?: (key: string) => Promise<string | null>;
  computed?: Record<string, { build: (inputs, base) => string | null }>;
}) => Promise<{ prepend: string; inline: string }>
```

- **prepend** — computed blocks that should appear before the main inline content.
- **inline** — the joined context block suitable for `{{inlinedContext}}` substitution.
- Unknown `unitType` returns `{ prepend: "", inline: "" }` — callers fall through to default behavior.
- Missing resolvers or registry entries are skipped silently (no error).

## Manifest schema

```ts
interface UnitManifest {
  skills: { mode: "all" | "planning" | string[] };
  knowledge: "full" | "scoped" | "critical-only" | "none";
  memory: "prompt-relevant" | "critical-only" | "none";
  codebaseMap: boolean;
  preferences: "active-only" | "none";
  tools: string[];
  prepend?: string[];           // computed artifact ids (e.g. ["overrides"])
  artifacts: {
    inline: string[];           // static artifact keys, resolved in order
    excerpt: string[];          // excerpt artifact keys
    onDemand: string[];         // referenced but not auto-inlined
    computed?: string[];        // async computed artifact ids (e.g. ["knowledge", "graph"])
  };
  maxSystemPromptChars: number;
}
```

### Stable artifact keys (`ARTIFACT_KEYS`)

Keys are **stable identifiers**, not paths. The resolver maps a key to its
source (file, DB row, computed block).

Milestone-scoped: `roadmap`, `milestone-context`, `milestone-summary`,
`milestone-validation`, `milestone-research`, `milestone-plan`

Slice-scoped: `slice-context`, `slice-research`, `slice-plan`, `slice-summary`,
`slice-summaries`, `slice-uat`, `slice-assessment`

Task-scoped: `task-plan`, `task-summary`, `prior-task-summaries`,
`dependency-summaries`, `blocker-summaries`

Project-scoped: `requirements`, `decisions`, `project`, `templates`, `queue`

Validation-scoped: `verification-classes`, `outstanding-items`, `previous-validation`

History-scoped: `prior-milestone-summary`

## Migration decision matrix

| Builder complexity | Recommendation | Example |
|-------------------|------------------|---------|
| Simple (1–3 static artifacts, no knowledge splice) | Full v2 via `composeUnitContext` | deploy, release, rollback, challenge |
| Medium (4–6 static artifacts, optional knowledge/graph) | v2 inline + imperative knowledge splice | research-milestone, complete-slice, reassess-roadmap |
| Complex (many artifacts, computed prepend, conditional logic) | v2 with full computed registry | plan-milestone, research-slice, plan-slice, replan-slice |
| Fully imperative (no composer call) | Keep imperative; add manifest for documentation | execute-task, reactive-execute |

## Budget and caching

- `resolvePromptBudgets()` computes context-window-scaled budgets once per
dispatch tick (1-second TTL). Do not add additional caches.
- `capPreamble()` caps inlined context at `min(30K chars, scaled inline budget)`.
- Exceeding `maxSystemPromptChars` logs a telemetry event; the composer does not
truncate.

## Verification

- `auto-prompts-phase3.test.mjs` — v2 contracts for plan-milestone,
  replan-slice, validate-milestone, research-slice.
- `auto-prompts-complete-slice.test.mjs` — complete-slice closeout contract.
- `auto-prompts-v2-migration.test.mjs` — regression guard for all migrated
  builders (research-milestone, run-uat, deploy, smoke-production, release,
  rollback, challenge).
- `unit-context-manifest-computed.test.mjs` — manifest `computed` keys match
  builder behavior.

## Adding a new artifact key

1. Add the key to `ARTIFACT_KEYS` in `unit-context-manifest.js`.
2. Add the key to the `inline` / `excerpt` / `onDemand` / `computed` list of
   every manifest that needs it.
3. Implement the resolver in the builder's `resolveArtifact` function.
4. Add a regression test that asserts the artifact appears in the prompt.

## Adding a new computed artifact type

1. Define the builder function (e.g. `inlineKnowledgeBudgeted`).
2. Register it in the `computed` registry passed to `composeUnitContext`:
   ```js
   computed: {
     knowledge: { build: (inputs, base) => inlineKnowledgeBudgeted(base, inputs.keywords) }
   }
   ```
3. Add the id to `manifest.artifacts.computed`.
4. Add a test that asserts the computed block appears in the output.
