Files
gstack/scripts/resolvers/preamble.ts
T
Garry Tan cb3713fbf1 fix(preamble): reorder AskUserQuestion Format above model overlay + rewrite Opus 4.7 pacing directive
Root cause of plan-review regression (v1.6.4.0): model overlays rendered
ABOVE the pacing rule in every SKILL.md, so Opus 4.7 read "Batch your
questions" first and absorbed it as the ambient default. The overlay's
claimed subordination ("skill wins on pacing, always") didn't stick —
literal-interpretation mode reads physical order, not claimed hierarchy.

Part 1 of 4 (plan: ~/.claude/plans/system-instruction-you-are-working-polymorphic-twilight.md):

scripts/resolvers/preamble.ts
- Move generateAskUserFormat above generateModelOverlay in section array
- Comment explains why — prevents future refactors from silently reverting

model-overlays/opus-4-7.md
- Replace "Batch your questions" block with "Pace questions to the skill"
- New wording makes one-question-per-turn the default when the skill
  contains STOP directives; batching becomes the explicit exception

Regenerated 30 SKILL.md files via bun run gen:skill-docs.

Verified:
- With --model opus-4-7: Format renders at line 359, Model-Specific
  Patch at 373, "Pace questions" at 419 (Format comes first, overlay
  second, pacing directive intact).
- bun test passes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 16:35:26 -07:00

108 lines
5.1 KiB
TypeScript

/**
* Preamble composition root.
*
* Each generator lives in its own file under ./preamble/*.ts. This file only
* wires them together via generatePreamble(). Keep composition declarative —
* no inline logic beyond tier gating.
*
* Each skill runs independently via `claude -p` (or the host's equivalent).
* There is no shared loader. The preamble provides: update checks, session
* tracking, user preferences, repo mode detection, model overlays, and
* telemetry.
*
* Telemetry data flow:
* 1. Always: local JSONL append to ~/.gstack/analytics/ (inline, inspectable)
* 2. If _TEL != "off" AND binary exists: gstack-telemetry-log for remote reporting
*/
import type { TemplateContext } from './types';
import { generateModelOverlay } from './model-overlay';
import { generateQuestionTuning } from './question-tuning';
// Core bootstrap
import { generatePreambleBash } from './preamble/generate-preamble-bash';
import { generateUpgradeCheck } from './preamble/generate-upgrade-check';
import { generateCompletionStatus } from './preamble/generate-completion-status';
// One-time onboarding prompts
import { generateLakeIntro } from './preamble/generate-lake-intro';
import { generateTelemetryPrompt } from './preamble/generate-telemetry-prompt';
import { generateProactivePrompt } from './preamble/generate-proactive-prompt';
import { generateRoutingInjection } from './preamble/generate-routing-injection';
import { generateVendoringDeprecation } from './preamble/generate-vendoring-deprecation';
import { generateSpawnedSessionCheck } from './preamble/generate-spawned-session-check';
import { generateWritingStyleMigration } from './preamble/generate-writing-style-migration';
// Host-specific instructions
import { generateBrainHealthInstruction } from './preamble/generate-brain-health-instruction';
// Behavioral / voice
import { generateVoiceDirective } from './preamble/generate-voice-directive';
// Tier 2+ context and interaction framework
import { generateContextRecovery } from './preamble/generate-context-recovery';
import { generateAskUserFormat } from './preamble/generate-ask-user-format';
import { generateWritingStyle } from './preamble/generate-writing-style';
import { generateCompletenessSection } from './preamble/generate-completeness-section';
import { generateConfusionProtocol } from './preamble/generate-confusion-protocol';
import { generateContinuousCheckpoint } from './preamble/generate-continuous-checkpoint';
import { generateContextHealth } from './preamble/generate-context-health';
// Tier 3+ repo mode + search
import { generateRepoModeSection } from './preamble/generate-repo-mode-section';
import { generateSearchBeforeBuildingSection } from './preamble/generate-search-before-building';
// Standalone export used directly by the resolver registry
export { generateTestFailureTriage } from './preamble/generate-test-failure-triage';
// Preamble Composition (tier → sections)
// ─────────────────────────────────────────────
// T1: core + upgrade + lake + telemetry + voice(trimmed) + completion
// T2: T1 + voice(full) + ask + completeness + context-recovery + confusion + checkpoint + context-health
// T3: T2 + repo-mode + search
// T4: (same as T3 — TEST_FAILURE_TRIAGE is a separate {{}} placeholder, not preamble)
//
// Skills by tier:
// T1: browse, setup-cookies, benchmark
// T2: investigate, cso, retro, doc-release, setup-deploy, canary, context-save, context-restore, health
// T3: autoplan, codex, design-consult, office-hours, ceo/design/eng-review
// T4: ship, review, qa, qa-only, design-review, land-deploy
export function generatePreamble(ctx: TemplateContext): string {
const tier = ctx.preambleTier ?? 4;
if (tier < 1 || tier > 4) {
throw new Error(`Invalid preamble-tier: ${tier} in ${ctx.tmplPath}. Must be 1-4.`);
}
const sections = [
generatePreambleBash(ctx),
generateUpgradeCheck(ctx),
generateWritingStyleMigration(ctx),
generateLakeIntro(),
generateTelemetryPrompt(ctx),
generateProactivePrompt(ctx),
generateRoutingInjection(ctx),
generateVendoringDeprecation(ctx),
generateSpawnedSessionCheck(),
generateBrainHealthInstruction(ctx),
// AskUserQuestion Format renders BEFORE the model overlay so the pacing rule
// is the ambient default; the overlay's behavioral nudges land as subordinate
// patches. Opus 4.7 reads top-to-bottom and absorbs the first pacing directive
// it hits; reversing this order regresses plan-review cadence (v1.6.4.0 bug).
...(tier >= 2 ? [generateAskUserFormat(ctx)] : []),
generateModelOverlay(ctx),
generateVoiceDirective(tier),
...(tier >= 2 ? [
generateContextRecovery(ctx),
generateWritingStyle(ctx),
generateCompletenessSection(),
generateConfusionProtocol(),
generateContinuousCheckpoint(),
generateContextHealth(),
generateQuestionTuning(ctx),
] : []),
...(tier >= 3 ? [generateRepoModeSection(), generateSearchBeforeBuildingSection(ctx)] : []),
generateCompletionStatus(ctx),
];
return sections.filter(s => s && s.trim().length > 0).join('\n\n');
}