// SF — UnitContextManifest (#4782 phase 1).
//
// Declarative description of what context each autonomous mode unit type needs
// in its system prompt. Establishes the contract that later phases will
// use to drive a single composeSystemPromptForUnit() — replacing the
// per-unit-type branching currently spread across `auto-prompts.ts`.
//
// **Phase 1 ships the type + the data + a CI coverage guard.** It adds
// zero wiring — no caller reads a manifest yet. Every unit type gets a
// manifest that describes today's behavior as faithfully as possible, so
// when the composer lands in phase 2 the migration can proceed manifest-
// by-manifest without behavior change.
//
// Phased rollout tracking:
//   - Phase 1 (this PR): schema + manifests + coverage test.
//   - Phase 2: add composeSystemPromptForUnit(); migrate one low-risk
//     unit type (e.g. reassess-roadmap) as the pilot.
//   - Phase 3: migrate remaining unit types, tighten manifests per
//     empirical usage, introduce skipWhen predicates absorbing the
//     reassess opt-in gate from #4778.
//   - Phase 4: introduce pipeline variants as declared sequences,
//     absorbing the scope-classifier gates from #4781.
//
// Naming:
//   - Artifact keys are STABLE strings (not paths). Path resolution is
//     the composer's job; manifests describe intent, not disk layout.
//   - Char budgets are nominal — blown budgets log a telemetry event,
//     they do not truncate or error (the composer decides fallback).
import { validatePromptParts } from "./subagent/prompt-parts.js";

// ─── Artifact registry ────────────────────────────────────────────────────
/**
 * Stable identifiers for every artifact class a unit might inline, excerpt,
 * or reference on-demand. Adding a new artifact class requires (a) a key
 * here, (b) path/body resolution in the composer, and (c) updates to any
 * manifest that should surface it.
 */
export const ARTIFACT_KEYS = [
	// 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", // aggregated slice excerpts (complete/validate milestone)
	"slice-uat",
	"slice-assessment",
	// Task-scoped
	"task-plan",
	"task-summary",
	"prior-task-summaries",
	"dependency-summaries",
	"blocker-summaries", // tasks with blocker_discovered:true (replan-slice)
	// Project-scoped
	"requirements",
	"decisions",
	"project",
	"templates",
	"queue", // .sf/QUEUE file (plan-milestone)
	// Validation-scoped
	"verification-classes", // milestone.verification_* from DB (validate-milestone)
	"outstanding-items", // follow-ups aggregated from slice summaries (validate-milestone)
	"previous-validation", // prior VALIDATION file for re-validation rounds
	// History-scoped
	"prior-milestone-summary", // previous milestone's SUMMARY (plan-milestone)
	// Computed (async, budgeted) — these are not file paths but derived from DB/memory/graph
	"knowledge", // ranked knowledge entries scoped by keywords (knowledge-injector + DB memories)
	"graph", // code-graph subgraph scoped by milestone/slice/task identifiers
];
// ─── Manifests ────────────────────────────────────────────────────────────
// Phase 1 policy: every manifest encodes today's behavior. Skills = "all"
// unless the unit type was already narrowed via the existing skill-manifest
// resolver (#4779). Memory/knowledge policies reflect the defaults in
// `bootstrap/system-context.ts`. Artifact classifications follow what
// `auto-prompts.ts` inlines today for each unit type.
const COMMON_BUDGET_LARGE = 1_500_000; // ~400K tokens
const COMMON_BUDGET_MEDIUM = 750_000; // ~200K tokens
const COMMON_BUDGET_SMALL = 250_000; // ~65K tokens
// ─── Tool policy constants (#4934) ────────────────────────────────────────
// Reused across manifests so per-unit assignment stays declarative and the
// allowed-path set for the docs policy lives in one reviewable place.
const TOOLS_ALL = { mode: "all" };
const TOOLS_PLANNING = { mode: "planning" };
const TOOLS_DOCS = {
	mode: "docs",
	// Globs are resolved relative to project basePath. The set is intentionally
	// narrow: top-level docs/, README, CHANGELOG, and any markdown at the
	// project root. Projects with non-standard layouts (e.g. mintlify-docs/)
	// will need this list extended in a follow-up; landed conservative now,
	// expand on demand.
	allowedPathGlobs: [
		"docs/**",
		"README.md",
		"README.*.md",
		"CHANGELOG.md",
		"*.md",
	],
};
/**
 * Canonical unit types handled by autonomous mode dispatch. The coverage test
 * enumerates these against `UNIT_MANIFESTS` to catch manifest drift when
 * a new unit type lands.
 */
export const KNOWN_UNIT_TYPES = [
	"research-milestone",
	"roadmap-meeting",
	"plan-milestone",
	"discuss-milestone",
	"validate-milestone",
	"complete-milestone",
	"research-slice",
	"plan-slice",
	"refine-slice",
	"replan-slice",
	"complete-slice",
	"reassess-roadmap",
	"execute-task",
	"reactive-execute",
	"run-uat",
	"gate-evaluate",
	"rewrite-docs",
	// SF ADR-011 deep planning gate (project-scoped, before any milestone work)
	"discuss-project",
	"discuss-requirements",
	"research-project",
	"workflow-preferences",
	// ─── Ops / deployment pipeline ────────────────────────────────────────
	"deploy",
	"smoke-production",
	"release",
	"rollback",
	"challenge",
];
export const UNIT_MANIFESTS = {
	// ─── Milestone-scoped ────────────────────────────────────────────────
	"research-milestone": {
		skills: { mode: "all" },
		knowledge: "full",
		memory: "prompt-relevant",
		codebaseMap: true,
		preferences: "active-only",
		tools: TOOLS_PLANNING,
		artifacts: {
			// #M005-remediation: knowledge resolved as an inline artifact so its
			// position (between decisions and templates) is preserved by the
			// composer's declared-order traversal. Graph stays as a computed
			// artifact (always appended after templates), matching the prior
			// builder's behavior. Eliminates the manual splice that previously
			// existed in buildResearchMilestonePrompt.
			inline: [
				"milestone-context",
				"project",
				"requirements",
				"decisions",
				"knowledge",
				"templates",
			],
			excerpt: [],
			onDemand: [],
			computed: ["graph"],
		},
		maxSystemPromptChars: COMMON_BUDGET_MEDIUM,
	},
	"plan-milestone": {
		promptParts: ["aiSafety", "toolInstructions", "environmentContext"],
		skills: { mode: "all" },
		knowledge: "full",
		memory: "prompt-relevant",
		codebaseMap: true,
		preferences: "active-only",
		tools: TOOLS_PLANNING,
		// phase-anchor prepends the research-phase handoff block computed at call time.
		prepend: ["phase-anchor"],
		artifacts: {
			// Matches today's buildPlanMilestonePrompt inlining order.
			// Templates are level-dependent (resolved imperatively in resolveArtifact).
			// queue is conditional on .sf/QUEUE existence (null return = skipped).
			// knowledge/graph are true computed artifacts (async, budgeted).
			inline: [
				"milestone-context",
				"milestone-research",
				"prior-milestone-summary",
				"project",
				"requirements",
				"decisions",
				"queue",
				"templates",
			],
			excerpt: [],
			onDemand: [],
			computed: ["knowledge", "graph"],
		},
		maxSystemPromptChars: COMMON_BUDGET_LARGE,
	},
	"roadmap-meeting": {
		skills: { mode: "all" },
		knowledge: "full",
		memory: "prompt-relevant",
		codebaseMap: true,
		preferences: "active-only",
		tools: TOOLS_PLANNING,
		artifacts: {
			inline: [
				"project",
				"requirements",
				"decisions",
				"milestone-context",
				"templates",
			],
			excerpt: [],
			onDemand: [],
		},
		maxSystemPromptChars: COMMON_BUDGET_LARGE,
	},
	"discuss-milestone": {
		skills: { mode: "all" },
		knowledge: "full",
		memory: "prompt-relevant",
		codebaseMap: true,
		preferences: "active-only",
		tools: TOOLS_PLANNING,
		artifacts: {
			// #4782 phase 3: Q1 decision — added computed entries to match
			// the pattern of similar discuss-type builders. Knowledge/graph
			// are appended after templates (not prepended).
			inline: [
				"project",
				"requirements",
				"decisions",
				"milestone-context",
				"templates",
			],
			excerpt: [],
			onDemand: [],
			computed: ["knowledge", "graph"],
		},
		maxSystemPromptChars: COMMON_BUDGET_MEDIUM,
	},
	"validate-milestone": {
		skills: { mode: "all" },
		knowledge: "scoped",
		memory: "prompt-relevant",
		codebaseMap: false,
		preferences: "active-only",
		tools: TOOLS_PLANNING,
		artifacts: {
			// Matches today's buildValidateMilestonePrompt inlining order.
			// verification-classes, slice-summaries, outstanding-items, and
			// previous-validation are pre-computed blocks returned from
			// resolveArtifact — null when absent/empty.
			// knowledge/graph are true computed artifacts (async, budgeted).
			inline: [
				"roadmap",
				"verification-classes",
				"slice-summaries",
				"outstanding-items",
				"previous-validation",
				"requirements",
				"decisions",
				"project",
				"milestone-context",
			],
			excerpt: [],
			onDemand: [],
			computed: ["knowledge", "graph"],
		},
		maxSystemPromptChars: COMMON_BUDGET_LARGE,
	},
	"complete-milestone": {
		skills: { mode: "all" },
		knowledge: "scoped",
		memory: "prompt-relevant",
		codebaseMap: false,
		preferences: "active-only",
		tools: TOOLS_PLANNING,
		artifacts: {
			// Matches today's buildCompleteMilestonePrompt inlining order.
			// slice-summaries is a pre-computed block (parallel DB + file reads)
			// returned from resolveArtifact. knowledge/graph are true computed
			// artifacts (async, budgeted).
			inline: [
				"roadmap",
				"slice-summaries",
				"requirements",
				"decisions",
				"project",
				"milestone-context",
				"templates",
			],
			excerpt: [],
			onDemand: [],
			computed: ["knowledge", "graph"],
		},
		maxSystemPromptChars: COMMON_BUDGET_MEDIUM,
	},
	// ─── Slice-scoped ────────────────────────────────────────────────────
	"research-slice": {
		skills: { mode: "all" },
		knowledge: "full",
		memory: "prompt-relevant",
		codebaseMap: true,
		preferences: "active-only",
		tools: TOOLS_PLANNING,
		// overrides prepends the active-overrides block computed at call time.
		prepend: ["overrides"],
		artifacts: {
			// Matches today's buildResearchSlicePrompt inlining order.
			// dependency-summaries is a SEPARATE template variable (not inlined
			// into inlinedContext). knowledge/graph are true computed artifacts.
			inline: [
				"roadmap",
				"milestone-context",
				"slice-context",
				"milestone-research",
				"decisions",
				"requirements",
				"templates",
			],
			excerpt: [],
			onDemand: [],
			computed: ["knowledge", "graph"],
		},
		maxSystemPromptChars: COMMON_BUDGET_MEDIUM,
	},
	"plan-slice": {
		skills: { mode: "all" },
		knowledge: "full",
		memory: "prompt-relevant",
		codebaseMap: true,
		preferences: "active-only",
		tools: TOOLS_PLANNING,
		// overrides prepends the active-overrides block computed at call time.
		prepend: ["overrides"],
		artifacts: {
			// Matches today's renderSlicePrompt inlining order.
			// dependency-summaries is a SEPARATE template variable (not inlined).
			// prependBlocks (soft-scope hint / pre-exec failure) and the
			// research-slice phase anchor are injected between prepend and inline
			// by the builder (imperatively). knowledge/graph are true computed.
			// decisions and requirements are skipped when level === "minimal".
			// templates is level-dependent (plan always; task-plan if full).
			inline: [
				"roadmap",
				"slice-context",
				"slice-research",
				"decisions",
				"requirements",
				"templates",
			],
			excerpt: [],
			onDemand: [],
			computed: ["knowledge", "graph"],
		},
		maxSystemPromptChars: COMMON_BUDGET_LARGE,
	},
	"refine-slice": {
		skills: { mode: "all" },
		knowledge: "scoped",
		memory: "prompt-relevant",
		codebaseMap: true,
		preferences: "active-only",
		tools: TOOLS_PLANNING,
		// overrides prepends the active-overrides block computed at call time.
		prepend: ["overrides"],
		artifacts: {
			// Same inlining order as plan-slice (shares renderSlicePrompt).
			// The sketch-scope hard constraint is injected between prepend and
			// inline by the builder (imperatively, via prependBlocks).
			inline: [
				"roadmap",
				"slice-context",
				"slice-research",
				"decisions",
				"requirements",
				"templates",
			],
			excerpt: [],
			onDemand: [],
			computed: ["knowledge", "graph"],
		},
		maxSystemPromptChars: COMMON_BUDGET_MEDIUM,
	},
	"replan-slice": {
		skills: { mode: "all" },
		knowledge: "scoped",
		memory: "prompt-relevant",
		codebaseMap: true,
		preferences: "active-only",
		tools: TOOLS_PLANNING,
		// overrides prepends the active-overrides block computed at call time.
		prepend: ["overrides"],
		artifacts: {
			// Matches today's buildReplanSlicePrompt inlining order.
			// blocker-summaries is a pre-computed block (task file scan) returned
			// from resolveArtifact — null when no blocker task is found.
			inline: [
				"roadmap",
				"slice-context",
				"slice-plan",
				"blocker-summaries",
				"decisions",
			],
			excerpt: [],
			onDemand: [],
			computed: [],
		},
		maxSystemPromptChars: COMMON_BUDGET_MEDIUM,
	},
	"complete-slice": {
		skills: { mode: "all" },
		knowledge: "scoped",
		memory: "prompt-relevant",
		codebaseMap: false,
		preferences: "active-only",
		tools: TOOLS_PLANNING,
		artifacts: {
			// #4782 phase 3: knowledge/graph migrated to computed registry.
			// Overrides prepend stays in the builder imperatively (RFC #4924).
			inline: [
				"roadmap",
				"slice-context",
				"slice-plan",
				"requirements",
				"prior-task-summaries",
				"templates",
			],
			excerpt: [],
			onDemand: [],
			computed: ["knowledge", "graph"],
		},
		maxSystemPromptChars: COMMON_BUDGET_LARGE,
	},
	"reassess-roadmap": {
		skills: { mode: "all" },
		knowledge: "scoped",
		memory: "critical-only",
		codebaseMap: false,
		preferences: "none",
		tools: TOOLS_PLANNING,
		artifacts: {
			// #M006 S02 (AD04): knowledge and graph declared as computed
			// artifacts (migrated from manual fetch outside composer).
			// Phase 2 pilot comment retained — manifest now reflects actual builder.
			inline: [
				"roadmap",
				"slice-context",
				"slice-summary",
				"project",
				"requirements",
				"decisions",
			],
			excerpt: [],
			onDemand: [],
			computed: ["knowledge", "graph"],
		},
		maxSystemPromptChars: COMMON_BUDGET_MEDIUM,
	},
	// ─── Task-scoped ─────────────────────────────────────────────────────
	"execute-task": {
		skills: { mode: "all" },
		knowledge: "scoped",
		memory: "prompt-relevant",
		codebaseMap: true,
		preferences: "active-only",
		tools: TOOLS_ALL,
		artifacts: {
			inline: ["task-plan", "slice-plan", "prior-task-summaries", "templates"],
			excerpt: [],
			onDemand: ["slice-research"],
			computed: ["knowledge", "graph"],
		},
		maxSystemPromptChars: COMMON_BUDGET_LARGE,
	},
	"reactive-execute": {
		skills: { mode: "all" },
		knowledge: "scoped",
		memory: "prompt-relevant",
		codebaseMap: true,
		preferences: "active-only",
		tools: TOOLS_ALL,
		artifacts: {
			inline: ["slice-plan", "prior-task-summaries", "templates"],
			excerpt: [],
			onDemand: ["slice-research"],
			computed: ["knowledge", "graph"],
		},
		maxSystemPromptChars: COMMON_BUDGET_LARGE,
	},
	// ─── Ancillary units ─────────────────────────────────────────────────
	"run-uat": {
		skills: { mode: "all" },
		knowledge: "critical-only",
		memory: "critical-only",
		codebaseMap: false,
		preferences: "active-only",
		tools: TOOLS_PLANNING,
		artifacts: {
			// Phase 3 migration (#4782): manifest matches today's actual
			// buildRunUatPrompt inlining. Prior phase-1 entry listed
			// `slice-plan` aspirationally — the real builder inlines the UAT
			// file, the slice SUMMARY (optional), and the project row.
			inline: ["slice-uat", "slice-summary", "project"],
			excerpt: [],
			onDemand: [],
		},
		maxSystemPromptChars: COMMON_BUDGET_SMALL,
	},
	"gate-evaluate": {
		skills: { mode: "all" },
		knowledge: "critical-only",
		memory: "critical-only",
		codebaseMap: false,
		preferences: "active-only",
		tools: TOOLS_PLANNING,
		artifacts: {
			inline: ["slice-plan", "prior-task-summaries"],
			excerpt: [],
			onDemand: [],
		},
		maxSystemPromptChars: COMMON_BUDGET_SMALL,
	},
	"rewrite-docs": {
		skills: { mode: "all" },
		knowledge: "scoped",
		memory: "prompt-relevant",
		codebaseMap: true,
		preferences: "active-only",
		tools: TOOLS_DOCS,
		artifacts: {
			// #4782 phase 3: added computed entries for knowledge/graph.
			// Static inline: project, requirements, decisions, templates.
			// The document-list path accumulation (from the existing imperative
			// builder) is preserved as the primary onDemand path list in
			// the builder itself; the composer handles the structured inlines.
			inline: ["project", "requirements", "decisions", "templates"],
			excerpt: [],
			onDemand: [],
			computed: ["knowledge", "graph"],
		},
		maxSystemPromptChars: COMMON_BUDGET_MEDIUM,
	},
	// ─── Project-scoped (deep planning gate, SF ADR-011) ───────────────────
	"discuss-project": {
		skills: { mode: "all" },
		knowledge: "full",
		memory: "prompt-relevant",
		codebaseMap: true,
		preferences: "active-only",
		tools: TOOLS_PLANNING,
		artifacts: {
			inline: ["project", "templates"],
			excerpt: [],
			onDemand: [],
			computed: ["knowledge", "graph"],
		},
		maxSystemPromptChars: COMMON_BUDGET_MEDIUM,
	},
	"discuss-requirements": {
		skills: { mode: "all" },
		knowledge: "full",
		memory: "prompt-relevant",
		codebaseMap: true,
		preferences: "active-only",
		tools: TOOLS_PLANNING,
		artifacts: {
			inline: ["project", "requirements", "templates"],
			excerpt: [],
			onDemand: [],
			computed: ["knowledge", "graph"],
		},
		maxSystemPromptChars: COMMON_BUDGET_MEDIUM,
	},
	"research-project": {
		skills: { mode: "all" },
		knowledge: "full",
		memory: "prompt-relevant",
		codebaseMap: true,
		preferences: "active-only",
		tools: TOOLS_PLANNING,
		artifacts: {
			inline: ["project", "requirements", "decisions", "templates"],
			excerpt: [],
			onDemand: [],
			computed: ["knowledge", "graph"],
		},
		maxSystemPromptChars: COMMON_BUDGET_LARGE,
	},
	"workflow-preferences": {
		skills: { mode: "all" },
		knowledge: "scoped",
		memory: "prompt-relevant",
		codebaseMap: false,
		preferences: "active-only",
		tools: TOOLS_PLANNING,
		artifacts: {
			inline: ["templates"],
			excerpt: [],
			onDemand: [],
			computed: ["knowledge"],
		},
		maxSystemPromptChars: COMMON_BUDGET_MEDIUM,
	},
	// ─── Ops / deployment pipeline ────────────────────────────────────────
	deploy: {
		skills: { mode: "all" },
		knowledge: "critical-only",
		memory: "critical-only",
		codebaseMap: false,
		preferences: "active-only",
		tools: TOOLS_ALL,
		artifacts: {
			inline: ["project"],
			excerpt: [],
			onDemand: [],
		},
		maxSystemPromptChars: COMMON_BUDGET_SMALL,
	},
	"smoke-production": {
		skills: { mode: "all" },
		knowledge: "critical-only",
		memory: "critical-only",
		codebaseMap: false,
		preferences: "active-only",
		tools: TOOLS_ALL,
		artifacts: {
			inline: ["project"],
			excerpt: [],
			onDemand: [],
		},
		maxSystemPromptChars: COMMON_BUDGET_SMALL,
	},
	release: {
		skills: { mode: "all" },
		knowledge: "critical-only",
		memory: "critical-only",
		codebaseMap: false,
		preferences: "active-only",
		tools: TOOLS_ALL,
		artifacts: {
			inline: ["project"],
			excerpt: [],
			onDemand: [],
		},
		maxSystemPromptChars: COMMON_BUDGET_SMALL,
	},
	rollback: {
		skills: { mode: "all" },
		knowledge: "critical-only",
		memory: "critical-only",
		codebaseMap: false,
		preferences: "active-only",
		tools: TOOLS_ALL,
		artifacts: {
			inline: ["project"],
			excerpt: [],
			onDemand: [],
		},
		maxSystemPromptChars: COMMON_BUDGET_SMALL,
	},
	challenge: {
		skills: { mode: "all" },
		knowledge: "critical-only",
		memory: "critical-only",
		codebaseMap: false,
		preferences: "active-only",
		tools: TOOLS_ALL,
		artifacts: {
			inline: ["project"],
			excerpt: [],
			onDemand: [],
		},
		maxSystemPromptChars: COMMON_BUDGET_SMALL,
	},
};
// ─── Lookup helper ────────────────────────────────────────────────────────
/**
 * Return the manifest for a unit type, or null when the type is unknown.
 *
 * Callers MUST treat null as "fall through to today's default behavior"
 * rather than erroring — unknown unit types may be experimental and
 * should not crash the composer.
 */
export function resolveManifest(unitType) {
	return UNIT_MANIFESTS[unitType] ?? null;
}

export function validateManifestPromptParts(manifest) {
	return validatePromptParts(manifest?.promptParts);
}
