Skip to content

feat(prompts): overridable Prompts interface (Layer 0) - #62

Merged
ranxianglei merged 4 commits into
masterfrom
2026-08-12_prompts-interface
Aug 13, 2026
Merged

ranxianglei merged 4 commits into
masterfrom
2026-08-12_prompts-interface

Conversation

@ranxianglei

@ranxianglei ranxianglei commented Aug 12, 2026 •

Copy link
Copy Markdown
Owner

What

Opens the kernel's 4 load-bearing compression rules for host override, behind an acknowledgeRisk gate — the foundation for customizable prompts across acp-kernel and its downstreams (billion-context-pi, opencode-acp, …). Tracks dog/billion-context-pi#27 (design discussion → approved → reviewed).

Pure additive: default wording is byte-identical, and renderNudgeText stays one-arg backward-compatible. No downstream change required to keep current behavior.

Why staged this way

Prompt text is split into load-bearing (the compression rules — tuned over months, quality-critical for retrieval) vs surface (headers, chrome, descriptions). Layer 0 opens only the load-bearing rules, gated so overrides can't happen by accident. Full rationale + the 4-layer roadmap (override interface → prompt-set format → adapter plumbing → third-party packs) is in DESIGN-prompts.md.

Changes (5 files, +451/−11)

  • src/prompts.ts (new) — the public override surface:
    • interface Prompts { compressPhilosophy; howToCompressRules; tier2DistillRules; tier3CondenseRules: string }
    • defaultPrompts — Object.freezed singleton (review R2: callers can't corrupt the shared default).
    • resolvePrompts(overrides?, { acknowledgeRisk? }) — non-string values are dropped (review R1: {compressPhilosophy: undefined} no longer clobbers the good default); throws on any override without acknowledgement, naming the offending keys. Mirrors the existing compress-tool dangerous/acknowledgeRisk pattern.
  • src/nudge-text.ts — renderNudgeText(decision, prompts = defaultPrompts); an override now flows into every nudge tier (gentle / emergency / tier-2 / tier-3). EFFICIENCY_NOTE/EMERGENCY_HEADER became helper functions of prompts.
  • src/index.ts — export defaultPrompts, resolvePrompts, Prompts, ResolvePromptsOptions.
  • tests/prompts.test.ts (new, 20 tests) — defaults mirror the verbatim constants; empty override no-op; override without ack throws + lists keys; override with ack merges; renderNudgeText reflects custom text across all four modes; non-clobber (undefined/null/number dropped); no-aliasing/mutation; frozen assertion; one-arg vs two-arg byte-identity.
  • DESIGN-prompts.md (new) — load-bearing/surface classification, host usage, non-goals, Layers 1–3 design intent, risk table.

Verification

  • npm run typecheck ✅
  • npm test ✅ — 244 pass / 0 fail (224 baseline + 20 new)
  • npm run build ✅ — dist/index.js + .d.ts emit the new exports

Dual-agent review

Two oracle agents reviewed for regressions (per issue #27). Verdict: ship-with-fixes. Two real issues found and fixed (R1 non-string clobber, R2 mutable singleton — both above). Non-blocking observations documented in DESIGN-prompts.md.

Publishing

Rebased onto current master (v0.0.20, 13a345e). Note: 0.0.21 already shipped for an unrelated nudge fix (#64) and does NOT include Layer 0. When this PR merges, Layer 0 lands in the next publish (0.0.22). Downstream billion-context-pi#128 is blocked on that.

Non-goals (deliberately deferred)

Surface text (summary header, status-report headers, tool descriptions) and inline validation errors stay fixed for now — see DESIGN-prompts.md §Non-goals. No Config/defaultConfig change; the host holds the resolved Prompts object and passes it explicitly.

🤖 Merge is human-only — I will not merge this PR.

…r 0)

Open the 4 load-bearing compression rules for host override behind an
acknowledgeRisk gate, so prompts can be customized without forking the
kernel. Pure additive: defaults are byte-identical and renderNudgeText
stays one-arg backward-compatible.

- src/prompts.ts: Prompts interface, defaultPrompts, resolvePrompts
- src/nudge-text.ts: renderNudgeText(decision, prompts=defaultPrompts)
- src/index.ts: export defaultPrompts/resolvePrompts/Prompts
- DESIGN-prompts.md: layer 0-3 roadmap + load-bearing/surface split
- tests/prompts.test.ts: 11 tests (defaults, gate, override flow)
…ew R1/R2)

Regression review (PR #62) found two correctness gaps:

R1: resolvePrompts spread overrides verbatim, so an explicit
{field: undefined} or {field: null} clobbered a good default with
undefined/null (then embedded "undefined"/"null" into the prompt).
Now only string-valued overrides take effect; undefined/null/wrong-type
are dropped before the gate and the merge, so a malformed partial never
degrades the canonical rules and never triggers a false throw.

R2: defaultPrompts was a mutable singleton. A buggy caller mutating one
field would corrupt every other consumer. Now Object.freeze'd.

Tests: +9 (tier-3 custom text, non-clobber for undefined/null/number,
no-false-throw, no aliasing/mutation of defaults, frozen assertion,
two-arg-vs-one-arg byte-identity across all 4 modes, full-rule
verbatim embedding). 235 -> 244 pass.
@ranxianglei
ranxianglei merged commit e146690 into master Aug 13, 2026
3 checks passed
@ranxianglei ranxianglei mentioned this pull request Aug 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant