markform 0.1.21 → 0.1.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/README.md +32 -4
  2. package/dist/ai-sdk.d.mts +1 -1
  3. package/dist/ai-sdk.mjs +1 -1
  4. package/dist/{apply-CD-t7ovb.mjs → apply-C7mO7VkZ.mjs} +100 -74
  5. package/dist/apply-C7mO7VkZ.mjs.map +1 -0
  6. package/dist/bin.mjs +1 -1
  7. package/dist/{cli-ChdIy1a7.mjs → cli-C8F9yDsv.mjs} +17 -1213
  8. package/dist/cli-C8F9yDsv.mjs.map +1 -0
  9. package/dist/cli.mjs +1 -1
  10. package/dist/{coreTypes-BQrWf_Wt.d.mts → coreTypes-BlsJkU1w.d.mts} +1 -1
  11. package/dist/fillRecord-DTl5lnK0.d.mts +345 -0
  12. package/dist/fillRecordRenderer-CruJrLkj.mjs +1256 -0
  13. package/dist/fillRecordRenderer-CruJrLkj.mjs.map +1 -0
  14. package/dist/index.d.mts +5 -342
  15. package/dist/index.mjs +3 -3
  16. package/dist/render.d.mts +74 -0
  17. package/dist/render.mjs +4 -0
  18. package/dist/{session-ZgegwtkT.mjs → session-BCcltrLA.mjs} +1 -1
  19. package/dist/{session-ZgegwtkT.mjs.map → session-BCcltrLA.mjs.map} +1 -1
  20. package/dist/{session-BPuQ-ok0.mjs → session-VeSkVrck.mjs} +1 -1
  21. package/dist/{shared-DwdyWmvE.mjs → shared-CsdT2T7k.mjs} +1 -1
  22. package/dist/{shared-DwdyWmvE.mjs.map → shared-CsdT2T7k.mjs.map} +1 -1
  23. package/dist/{shared-BTR35aMz.mjs → shared-fb0nkzQi.mjs} +1 -1
  24. package/dist/{src-DOPe4tmu.mjs → src-CbRnGzMK.mjs} +16 -11
  25. package/dist/{src-DOPe4tmu.mjs.map → src-CbRnGzMK.mjs.map} +1 -1
  26. package/dist/urlFormat-lls7CsEP.mjs +71 -0
  27. package/dist/urlFormat-lls7CsEP.mjs.map +1 -0
  28. package/docs/markform-apis.md +53 -0
  29. package/examples/simple/simple-skipped-filled.report.md +8 -8
  30. package/examples/twitter-thread/twitter-thread.form.md +373 -0
  31. package/package.json +5 -1
  32. package/dist/apply-CD-t7ovb.mjs.map +0 -1
  33. package/dist/cli-ChdIy1a7.mjs.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"apply-CD-t7ovb.mjs","names":["isAtLineStart","matchFenceOpening","matchFenceClosing"],"sources":["../src/errors.ts","../src/llms.ts","../src/settings.ts","../src/engine/parseHelpers.ts","../src/engine/parseSentinels.ts","../src/engine/preprocess.ts","../src/utils/keySort.ts","../src/utils/urlFormat.ts","../src/engine/serialize.ts","../src/engine/summaries.ts","../src/engine/validate.ts","../src/engine/inspect.ts","../src/engine/apply.ts"],"sourcesContent":["/**\n * Structured error types for markform.\n *\n * Provides a typed error hierarchy with context-rich information for debugging\n * and error handling by consumers.\n */\n\n// Build-time injected version\ndeclare const __MARKFORM_VERSION__: string;\nconst VERSION: string =\n typeof __MARKFORM_VERSION__ !== 'undefined' ? __MARKFORM_VERSION__ : 'development';\n\n// =============================================================================\n// Base Error Class\n// =============================================================================\n\n/**\n * Base error class for all markform errors.\n * Consumers can catch this to handle any markform error.\n */\nexport class MarkformError extends Error {\n override readonly name: string = 'MarkformError';\n readonly version: string = VERSION;\n\n constructor(message: string, options?: { cause?: Error }) {\n super(message, options);\n // Maintains proper prototype chain for instanceof checks\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n// =============================================================================\n// Parse Errors\n// =============================================================================\n\n/**\n * Form definition/parsing errors.\n * Thrown when the form markdown is invalid.\n */\nexport class MarkformParseError extends MarkformError {\n override readonly name = 'MarkformParseError';\n /** File path or form identifier */\n readonly source?: string;\n /** Line number in source (1-indexed) */\n readonly line?: number;\n /** Column number in source (1-indexed) */\n readonly column?: number;\n\n constructor(\n message: string,\n context?: { source?: string; line?: number; column?: number; cause?: Error },\n ) {\n super(message, { cause: context?.cause });\n this.source = context?.source;\n this.line = context?.line;\n this.column = context?.column;\n }\n}\n\n// =============================================================================\n// Patch Errors\n// =============================================================================\n\n/**\n * Single patch validation error.\n * Thrown when an LLM generates an invalid patch value.\n */\nexport class MarkformPatchError extends MarkformError {\n override readonly name = 'MarkformPatchError';\n /** The field ID that was targeted */\n readonly fieldId: string;\n /** The patch operation that failed */\n readonly patchOperation: string;\n /** The expected type description */\n readonly expectedType: string;\n /** The actual value that was received */\n readonly receivedValue: unknown;\n /** The type of the received value */\n readonly receivedType: string;\n /** Patch index in the batch (for batch operations) */\n readonly patchIndex?: number;\n\n constructor(\n message: string,\n context: {\n fieldId: string;\n patchOperation: string;\n expectedType: string;\n receivedValue: unknown;\n patchIndex?: number;\n cause?: Error;\n },\n ) {\n super(message, { cause: context.cause });\n this.fieldId = context.fieldId;\n this.patchOperation = context.patchOperation;\n this.expectedType = context.expectedType;\n this.receivedValue = context.receivedValue;\n this.patchIndex = context.patchIndex;\n this.receivedType =\n context.receivedValue === null\n ? 'null'\n : Array.isArray(context.receivedValue)\n ? 'array'\n : typeof context.receivedValue;\n }\n}\n\n/**\n * Multiple validation errors in a single operation.\n * Thrown when multiple patches fail validation.\n */\nexport class MarkformValidationError extends MarkformError {\n override readonly name = 'MarkformValidationError';\n /** Individual patch errors */\n readonly issues: MarkformPatchError[];\n\n constructor(issues: MarkformPatchError[]) {\n const summary =\n issues.length === 1\n ? issues[0]!.message\n : `${issues.length} validation errors: ${issues.map((i) => i.fieldId).join(', ')}`;\n super(summary);\n this.issues = issues;\n }\n\n /** Get all affected field IDs */\n get fieldIds(): string[] {\n return this.issues.map((i) => i.fieldId);\n }\n}\n\n// =============================================================================\n// LLM Errors\n// =============================================================================\n\n/**\n * LLM/API errors.\n * Thrown for rate limits, timeouts, invalid responses, etc.\n */\nexport class MarkformLlmError extends MarkformError {\n override readonly name = 'MarkformLlmError';\n /** LLM provider (e.g., 'anthropic', 'openai') */\n readonly provider?: string;\n /** Model identifier */\n readonly model?: string;\n /** HTTP status code if applicable */\n readonly statusCode?: number;\n /** Whether this error is retryable */\n readonly retryable: boolean;\n\n constructor(\n message: string,\n context: {\n provider?: string;\n model?: string;\n statusCode?: number;\n retryable?: boolean;\n cause?: Error;\n },\n ) {\n super(message, { cause: context.cause });\n this.provider = context.provider;\n this.model = context.model;\n this.statusCode = context.statusCode;\n this.retryable = context.retryable ?? false;\n }\n}\n\n// =============================================================================\n// Configuration Errors\n// =============================================================================\n\n/**\n * Configuration errors.\n * Thrown when invalid options are passed to fillForm, etc.\n */\nexport class MarkformConfigError extends MarkformError {\n override readonly name = 'MarkformConfigError';\n /** The configuration option that was invalid */\n readonly option: string;\n /** Expected type or value description */\n readonly expectedType: string;\n /** The actual value that was received */\n readonly receivedValue: unknown;\n\n constructor(\n message: string,\n context: { option: string; expectedType: string; receivedValue: unknown },\n ) {\n super(message);\n this.option = context.option;\n this.expectedType = context.expectedType;\n this.receivedValue = context.receivedValue;\n }\n}\n\n// =============================================================================\n// Form Abort Error\n// =============================================================================\n\n/**\n * Form abort error.\n * Thrown when a form is explicitly aborted via abort_form patch.\n */\nexport class MarkformAbortError extends MarkformError {\n override readonly name = 'MarkformAbortError';\n /** The reason for aborting */\n readonly reason: string;\n /** Field ID that triggered the abort, if applicable */\n readonly fieldId?: string;\n\n constructor(reason: string, fieldId?: string) {\n super(`Form aborted: ${reason}`);\n this.reason = reason;\n this.fieldId = fieldId;\n }\n}\n\n// =============================================================================\n// Type Guards\n// =============================================================================\n\n/**\n * Check if an error is any markform error.\n * Works in environments where instanceof might not be reliable (bundlers, etc.).\n */\nexport function isMarkformError(error: unknown): error is MarkformError {\n return error instanceof Error && 'version' in error && error.name.startsWith('Markform');\n}\n\n/** Check if an error is a parse error */\nexport function isParseError(error: unknown): error is MarkformParseError {\n return isMarkformError(error) && error.name === 'MarkformParseError';\n}\n\n/** Check if an error is a single patch error */\nexport function isPatchError(error: unknown): error is MarkformPatchError {\n return isMarkformError(error) && error.name === 'MarkformPatchError';\n}\n\n/** Check if an error is a validation error (multiple patches) */\nexport function isValidationError(error: unknown): error is MarkformValidationError {\n return isMarkformError(error) && error.name === 'MarkformValidationError';\n}\n\n/** Check if an error is an LLM/API error */\nexport function isLlmError(error: unknown): error is MarkformLlmError {\n return isMarkformError(error) && error.name === 'MarkformLlmError';\n}\n\n/** Check if an error is a configuration error */\nexport function isConfigError(error: unknown): error is MarkformConfigError {\n return isMarkformError(error) && error.name === 'MarkformConfigError';\n}\n\n/** Check if an error is a form abort error */\nexport function isAbortError(error: unknown): error is MarkformAbortError {\n return isMarkformError(error) && error.name === 'MarkformAbortError';\n}\n\n/** Check if an error is retryable (currently only LLM errors) */\nexport function isRetryableError(error: unknown): boolean {\n return isLlmError(error) && error.retryable;\n}\n\n// =============================================================================\n// Backward Compatibility Aliases\n// =============================================================================\n\n/**\n * Alias for MarkformParseError.\n * @deprecated Use MarkformParseError instead. ParseError will be removed in a future version.\n */\nexport const ParseError = MarkformParseError;\n","/**\n * LLM-related settings and configuration.\n *\n * This module centralizes LLM provider and model configuration,\n * including suggested models and web search support.\n */\n\n// =============================================================================\n// Model ID Parsing\n// =============================================================================\n\n/**\n * Parsed model ID components for display purposes.\n */\nexport interface DisplayModelId {\n /** Provider name (e.g., 'anthropic', 'openai') or 'unknown' */\n provider: string;\n /** Model name (e.g., 'claude-sonnet-4', 'gpt-4o') */\n model: string;\n}\n\n/**\n * Parse a model ID string into provider and model components for display.\n *\n * This is a non-throwing utility for extracting display-friendly components\n * from a model ID. For validation and resolution, use `parseModelId` from\n * `modelResolver.ts` instead.\n *\n * @param modelId - Model ID in format `provider/model-id`\n * @returns Parsed components with 'unknown' provider if format is invalid\n *\n * @example\n * parseModelIdForDisplay('anthropic/claude-sonnet-4')\n * // => { provider: 'anthropic', model: 'claude-sonnet-4' }\n *\n * parseModelIdForDisplay('claude-sonnet-4')\n * // => { provider: 'unknown', model: 'claude-sonnet-4' }\n */\nexport function parseModelIdForDisplay(modelId: string): DisplayModelId {\n const slashIndex = modelId.indexOf('/');\n if (slashIndex === -1 || slashIndex === 0 || slashIndex === modelId.length - 1) {\n return { provider: 'unknown', model: modelId };\n }\n return {\n provider: modelId.slice(0, slashIndex),\n model: modelId.slice(slashIndex + 1),\n };\n}\n\n// =============================================================================\n// Suggested LLMs\n// =============================================================================\n\n/**\n * Suggested LLM models for the fill command, organized by provider.\n * These are shown in help/error messages and model selection prompts.\n */\nexport const SUGGESTED_LLMS: Record<string, string[]> = {\n openai: ['gpt-5-mini', 'gpt-5-nano', 'gpt-5.2', 'gpt-5.2-pro', 'o3', 'o3-mini'],\n anthropic: ['claude-opus-4-5', 'claude-sonnet-4-5', 'claude-haiku-4-5'],\n google: ['gemini-3-flash', 'gemini-3-pro-preview', 'gemini-2.5-flash'],\n xai: ['grok-4', 'grok-4.1-fast'],\n deepseek: ['deepseek-chat', 'deepseek-reasoner'],\n};\n\n/**\n * Format suggested LLMs for display in help/error messages.\n */\nexport function formatSuggestedLlms(): string {\n const lines: string[] = ['Available providers and example models:'];\n for (const [provider, models] of Object.entries(SUGGESTED_LLMS)) {\n lines.push(` ${provider}/`);\n for (const model of models) {\n lines.push(` - ${provider}/${model}`);\n }\n }\n return lines.join('\\n');\n}\n\n// =============================================================================\n// Web Search Configuration\n// =============================================================================\n\n/**\n * Web search support configuration by provider.\n */\nexport interface WebSearchConfig {\n /** Whether the provider has native web search */\n supported: boolean;\n /** Tool name on providerSdk.tools (e.g., 'webSearch', 'googleSearch') */\n toolName?: string;\n}\n\n/**\n * Web search configuration per provider.\n *\n * Tool names are from Vercel AI SDK provider documentation:\n * - openai: https://ai-sdk.dev/providers/ai-sdk-providers/openai\n * - anthropic: https://ai-sdk.dev/providers/ai-sdk-providers/anthropic\n * - google: https://ai-sdk.dev/providers/ai-sdk-providers/google-generative-ai\n * - xai: https://ai-sdk.dev/providers/ai-sdk-providers/xai\n * - deepseek: https://ai-sdk.dev/providers/ai-sdk-providers/deepseek (no tools)\n */\nexport const WEB_SEARCH_CONFIG: Record<string, WebSearchConfig> = {\n // openai.tools.webSearch - https://ai-sdk.dev/providers/ai-sdk-providers/openai#web-search\n openai: {\n supported: true,\n toolName: 'webSearch',\n },\n // anthropic.tools.webSearch_20250305 - https://ai-sdk.dev/providers/ai-sdk-providers/anthropic#web-search\n anthropic: {\n supported: true,\n toolName: 'webSearch_20250305',\n },\n // google.tools.googleSearch - https://ai-sdk.dev/providers/ai-sdk-providers/google-generative-ai#google-search-grounding\n google: {\n supported: true,\n toolName: 'googleSearch',\n },\n // xai.tools.webSearch - https://ai-sdk.dev/providers/ai-sdk-providers/xai#web-search-tool\n xai: {\n supported: true,\n toolName: 'webSearch',\n },\n // deepseek has no tools - https://ai-sdk.dev/providers/ai-sdk-providers/deepseek\n deepseek: {\n supported: false,\n },\n};\n\n/**\n * Check if a provider supports native web search.\n */\nexport function hasWebSearchSupport(provider: string): boolean {\n return WEB_SEARCH_CONFIG[provider]?.supported ?? false;\n}\n\n/**\n * Get web search tool configuration for a provider.\n * Returns undefined if provider doesn't support web search.\n */\nexport function getWebSearchConfig(provider: string): WebSearchConfig | undefined {\n const config = WEB_SEARCH_CONFIG[provider];\n return config?.supported ? config : undefined;\n}\n","/**\n * Global settings and constants for Markform.\n *\n * This file consolidates non-changing default values that were previously\n * scattered across the codebase. These are NOT runtime configurable - they\n * are compile-time constants.\n */\n\nimport type { FieldPriorityLevel } from './engine/coreTypes.js';\nimport { MarkformConfigError } from './errors.js';\n\n// =============================================================================\n// Spec Version Constants\n// =============================================================================\n\n/**\n * The current Markform spec version in full notation (e.g., \"MF/0.1\").\n * This is distinct from npm package version and tracks the format that\n * .form.md files conform to.\n */\nexport const MF_SPEC_VERSION = 'MF/0.1';\n\n/**\n * The numeric portion of the spec version (e.g., \"0.1\").\n * Used when only the version number is needed.\n */\nexport const MF_SPEC_VERSION_NUMBER = '0.1';\n\n// =============================================================================\n// Role System Constants\n// =============================================================================\n\n/** Default role for fields without explicit role attribute */\nexport const AGENT_ROLE = 'agent' as const;\n\n/** Role for human-filled fields in interactive mode */\nexport const USER_ROLE = 'user' as const;\n\n/** Default roles list for forms without explicit roles in frontmatter */\nexport const DEFAULT_ROLES: readonly [typeof USER_ROLE, typeof AGENT_ROLE] = [\n USER_ROLE,\n AGENT_ROLE,\n] as const;\n\n/** Default instructions per role (used when form doesn't specify role_instructions) */\nexport const DEFAULT_ROLE_INSTRUCTIONS: Record<string, string> = {\n [USER_ROLE]: 'Fill in the fields you have direct knowledge of.',\n [AGENT_ROLE]: 'Complete the remaining fields based on the provided context.',\n};\n\n/** Pattern for valid role names: starts with letter, alphanumeric with underscores/hyphens */\nexport const ROLE_NAME_PATTERN = /^[a-z][a-z0-9_-]*$/;\n\n/** Reserved role identifiers (not allowed as role names in forms) */\nexport const RESERVED_ROLE_NAMES = ['*'] as const;\n\n/**\n * Normalize a role name: trim whitespace, lowercase.\n * Throws if invalid pattern or reserved name.\n */\nexport function normalizeRole(role: string): string {\n const normalized = role.trim().toLowerCase();\n if (!ROLE_NAME_PATTERN.test(normalized)) {\n throw new MarkformConfigError(\n `Invalid role name: \"${role}\" (must match pattern: start with letter, alphanumeric with underscores/hyphens)`,\n { option: 'role', expectedType: 'valid role name pattern', receivedValue: role },\n );\n }\n if ((RESERVED_ROLE_NAMES as readonly string[]).includes(normalized)) {\n throw new MarkformConfigError(`Reserved role name: \"${role}\"`, {\n option: 'role',\n expectedType: 'non-reserved role name',\n receivedValue: role,\n });\n }\n return normalized;\n}\n\n/**\n * Parse --roles CLI flag value into normalized role array.\n * Handles comma-separated values and '*' wildcard.\n */\nexport function parseRolesFlag(raw: string): string[] {\n if (raw === '*') {\n return ['*'];\n }\n return raw.split(',').map((r) => normalizeRole(r));\n}\n\n// =============================================================================\n// Field Defaults\n// =============================================================================\n\n/**\n * The default priority level for fields when not explicitly specified.\n * Used by the parser to set default values and by the serializer to\n * determine whether to emit the priority attribute.\n */\nexport const DEFAULT_PRIORITY: FieldPriorityLevel = 'medium';\n\n// =============================================================================\n// CLI Defaults\n// =============================================================================\n\n/**\n * Default forms directory for CLI output (relative to cwd).\n * Commands write form outputs here to avoid cluttering the workspace.\n */\nexport const DEFAULT_FORMS_DIR = './forms';\n\n/**\n * Maximum forms to display in 'markform run' menu.\n * Additional forms are not shown but can be run directly by path.\n */\nexport const MAX_FORMS_IN_MENU = 30;\n\n/**\n * The default port for the serve command.\n */\nexport const DEFAULT_PORT = 3344;\n\n// =============================================================================\n// YAML Formatting\n// =============================================================================\n\n/**\n * Default line width for YAML output.\n * Long strings will wrap at this column for readability.\n * 88 is a common line width that works well with most editors.\n */\nexport const DEFAULT_YAML_LINE_WIDTH = 88;\n\n/**\n * YAML stringify options for readable output.\n * - No forced quoting (YAML only quotes when necessary)\n * - lineWidth provides reasonable wrapping for long strings\n * - Plain keys without quotes\n */\nexport const YAML_STRINGIFY_OPTIONS = {\n lineWidth: DEFAULT_YAML_LINE_WIDTH,\n defaultKeyType: 'PLAIN',\n} as const;\n\n// =============================================================================\n// Harness Config Mapping (snake_case ↔ camelCase)\n// =============================================================================\n\nimport type { FrontmatterHarnessConfig } from './engine/coreTypes.js';\n\n/**\n * Mapping between YAML snake_case keys and TypeScript camelCase keys\n * for harness configuration. Single source of truth for all transformations.\n */\nexport const HARNESS_CONFIG_MAPPING = {\n max_turns: 'maxTurns',\n max_patches_per_turn: 'maxPatchesPerTurn',\n max_issues_per_turn: 'maxIssuesPerTurn',\n max_parallel_agents: 'maxParallelAgents',\n} as const;\n\n/** Type for snake_case harness config (used in YAML frontmatter) */\nexport interface HarnessConfigYaml {\n max_turns?: number;\n max_patches_per_turn?: number;\n max_issues_per_turn?: number;\n max_parallel_agents?: number;\n}\n\n/**\n * Transform harness config from YAML snake_case to TypeScript camelCase.\n * Used by the parser when reading frontmatter.\n */\nexport function transformHarnessConfigToTs(yaml: HarnessConfigYaml): FrontmatterHarnessConfig {\n const result: FrontmatterHarnessConfig = {};\n for (const [snakeKey, camelKey] of Object.entries(HARNESS_CONFIG_MAPPING)) {\n const value = yaml[snakeKey as keyof HarnessConfigYaml];\n if (value !== undefined) {\n result[camelKey as keyof FrontmatterHarnessConfig] = value;\n }\n }\n return result;\n}\n\n/**\n * Transform harness config from TypeScript camelCase to YAML snake_case.\n * Used by the serializer when writing frontmatter.\n */\nexport function transformHarnessConfigToYaml(ts: FrontmatterHarnessConfig): HarnessConfigYaml {\n const result: HarnessConfigYaml = {};\n for (const [snakeKey, camelKey] of Object.entries(HARNESS_CONFIG_MAPPING)) {\n const value = ts[camelKey as keyof FrontmatterHarnessConfig];\n if (value !== undefined) {\n result[snakeKey as keyof HarnessConfigYaml] = value;\n }\n }\n return result;\n}\n\n// =============================================================================\n// Harness Defaults\n// =============================================================================\n\n/**\n * Default maximum turns for the fill harness.\n * Prevents runaway loops during agent execution.\n */\nexport const DEFAULT_MAX_TURNS = 100;\n\n/**\n * Default maximum patches per turn.\n */\nexport const DEFAULT_MAX_PATCHES_PER_TURN = 20;\n\n/**\n * Default maximum issues to show per turn.\n * Note: Renamed from DEFAULT_MAX_ISSUES for naming consistency with other per-turn limits.\n */\nexport const DEFAULT_MAX_ISSUES_PER_TURN = 10;\n\n/**\n * Default maximum concurrent agents for parallel batches.\n * When parallel batches are executed, this limits how many agents run simultaneously.\n */\nexport const DEFAULT_MAX_PARALLEL_AGENTS = 4;\n\n/**\n * Default maximum AI SDK steps (tool call rounds) per harness turn.\n * Matches AI SDK's ToolLoopAgent default of 20.\n * @see https://ai-sdk.dev/docs/agents/loop-control\n */\nexport const DEFAULT_MAX_STEPS_PER_TURN = 20;\n\n// =============================================================================\n// Research Defaults\n// =============================================================================\n\n/**\n * Default maximum issues to show per turn in research mode.\n * Lower than general fill to keep research responses focused.\n */\nexport const DEFAULT_RESEARCH_MAX_ISSUES_PER_TURN = 5;\n\n/**\n * Default maximum patches per turn in research mode.\n */\nexport const DEFAULT_RESEARCH_MAX_PATCHES_PER_TURN = 10;\n\n// =============================================================================\n// File Extension Constants\n// =============================================================================\n\n/**\n * Export format extensions used by the export command and exportMultiFormat.\n * These are the primary output formats when exporting forms.\n */\nexport const EXPORT_EXTENSIONS = {\n /** Canonical markform files with markdoc directives */\n form: '.form.md',\n /** Raw markdown export (no directives) */\n raw: '.raw.md',\n /** YAML values export */\n yaml: '.yml',\n /** JSON values export */\n json: '.json',\n} as const;\n\n/**\n * Report extension - generated by the report command.\n * Separate from exports as it's a filtered human-readable output.\n */\nexport const REPORT_EXTENSION = '.report.md' as const;\n\n/**\n * Schema extension - generated JSON Schema for form structure.\n * Used for validation, code generation, and LLM tooling.\n */\nexport const SCHEMA_EXTENSION = '.schema.json' as const;\n\n/**\n * Fill record extension - sidecar file containing execution metadata.\n * Generated by `markform fill --record-fill` and read by `markform serve`.\n *\n * **Recommended naming convention:**\n * - Form file: `document.form.md` → Fill record: `document.fill.json`\n * - Form file: `document.md` → Fill record: `document.fill.json`\n *\n * Using `.form.md` for forms and `.fill.json` for fill records helps tools\n * discover related files automatically. The CLI enforces this convention.\n *\n * The fill record contains:\n * - Session timing and duration\n * - LLM token usage (input/output)\n * - Tool call timeline with results\n * - Form progress at completion\n */\nexport const FILL_RECORD_EXTENSION = '.fill.json' as const;\n\n/**\n * All recognized markform file extensions.\n * Combines export formats with report and schema formats.\n */\nexport const ALL_EXTENSIONS = {\n ...EXPORT_EXTENSIONS,\n report: REPORT_EXTENSION,\n schema: SCHEMA_EXTENSION,\n} as const;\n\n/** Union type of recognized file types for routing and rendering */\nexport type FileType = 'form' | 'raw' | 'report' | 'yaml' | 'json' | 'schema' | 'unknown';\n\n/**\n * Detect file type from path based on extension.\n * Used by serve command to dispatch to appropriate renderer.\n */\nexport function detectFileType(filePath: string): FileType {\n if (filePath.endsWith(ALL_EXTENSIONS.form)) return 'form';\n if (filePath.endsWith(ALL_EXTENSIONS.raw)) return 'raw';\n if (filePath.endsWith(ALL_EXTENSIONS.report)) return 'report';\n if (filePath.endsWith(ALL_EXTENSIONS.yaml)) return 'yaml';\n if (filePath.endsWith(ALL_EXTENSIONS.schema)) return 'schema';\n if (filePath.endsWith(ALL_EXTENSIONS.json)) return 'json';\n // Generic .md files are treated as raw markdown\n if (filePath.endsWith('.md')) return 'raw';\n return 'unknown';\n}\n\n/**\n * Derive export path by replacing any known extension with the target format.\n * Only works with export formats (form, raw, yaml, json), not report.\n */\nexport function deriveExportPath(basePath: string, format: keyof typeof EXPORT_EXTENSIONS): string {\n let base = basePath;\n // Remove any known extension first\n for (const ext of Object.values(ALL_EXTENSIONS)) {\n if (base.endsWith(ext)) {\n base = base.slice(0, -ext.length);\n break;\n }\n }\n return base + EXPORT_EXTENSIONS[format];\n}\n\n/**\n * Derive report path from any markform file path.\n * Strips known extensions and appends .report.md.\n */\nexport function deriveReportPath(basePath: string): string {\n let base = basePath;\n for (const ext of Object.values(ALL_EXTENSIONS)) {\n if (base.endsWith(ext)) {\n base = base.slice(0, -ext.length);\n break;\n }\n }\n return base + REPORT_EXTENSION;\n}\n\n/**\n * Derive schema path from any markform file path.\n * Strips known extensions and appends .schema.json.\n */\nexport function deriveSchemaPath(basePath: string): string {\n let base = basePath;\n for (const ext of Object.values(ALL_EXTENSIONS)) {\n if (base.endsWith(ext)) {\n base = base.slice(0, -ext.length);\n break;\n }\n }\n return base + SCHEMA_EXTENSION;\n}\n\n/**\n * Derive fill record sidecar path from a form file path.\n *\n * **Convention:**\n * - `document.form.md` → `document.fill.json`\n * - `document.md` → `document.fill.json`\n *\n * Priority: strips `.form.md` first, then falls back to `.md`.\n * This ensures the recommended `.form.md` extension is handled correctly\n * while also supporting plain `.md` files.\n */\nexport function deriveFillRecordPath(formPath: string): string {\n // Priority order: try .form.md first (recommended), then .md (fallback)\n if (formPath.endsWith(EXPORT_EXTENSIONS.form)) {\n return formPath.slice(0, -EXPORT_EXTENSIONS.form.length) + FILL_RECORD_EXTENSION;\n }\n if (formPath.endsWith('.md')) {\n return formPath.slice(0, -'.md'.length) + FILL_RECORD_EXTENSION;\n }\n // For non-markdown files, just append the extension\n return formPath + FILL_RECORD_EXTENSION;\n}\n\n// =============================================================================\n// LLM Settings (re-exported from llms.ts for backwards compatibility)\n// =============================================================================\n\nexport {\n SUGGESTED_LLMS,\n formatSuggestedLlms,\n WEB_SEARCH_CONFIG,\n hasWebSearchSupport,\n getWebSearchConfig,\n type WebSearchConfig,\n} from './llms.js';\n","/**\n * Low-level parsing utilities for Markdoc AST processing.\n *\n * This module provides helper functions for extracting attributes and content\n * from Markdoc AST nodes. These are used by the field parsers and form parser.\n */\n\nimport type { Node } from '@markdoc/markdoc';\n\nimport type { CheckboxValue, ValidatorRef } from './coreTypes.js';\n\n// =============================================================================\n// Checkbox Marker Parsing\n// =============================================================================\n\n/** Map checkbox marker to state value */\nexport const CHECKBOX_MARKERS: Record<string, CheckboxValue> = {\n '[ ]': 'todo',\n '[x]': 'done',\n '[X]': 'done',\n '[/]': 'incomplete',\n '[*]': 'active',\n '[-]': 'na',\n '[y]': 'yes',\n '[Y]': 'yes',\n '[n]': 'no',\n '[N]': 'no',\n};\n\n// Regex to extract checkbox marker and label from text content\n// Text is like \"[ ] Label\" or \"[x] Label\"\nconst OPTION_TEXT_PATTERN = /^(\\[[^\\]]\\])\\s*(.*?)\\s*$/;\n\nexport interface ParsedMarkerText {\n marker: string;\n label: string;\n}\n\n/**\n * Parse option text to extract marker and label.\n * Text is like \"[ ] Label\" or \"[x] Label\".\n */\nexport function parseOptionText(text: string): ParsedMarkerText | null {\n const match = OPTION_TEXT_PATTERN.exec(text);\n if (!match) {\n return null;\n }\n\n const marker = match[1] ?? '';\n const label = (match[2] ?? '').trim();\n\n return { marker, label };\n}\n\n// =============================================================================\n// Markdoc Tag Processing\n// =============================================================================\n\n/**\n * Check if a node is a tag node with specific name.\n * Works with raw AST nodes (not transformed Tags).\n */\nexport function isTagNode(node: Node, name?: string): boolean {\n if (typeof node !== 'object' || node === null) {\n return false;\n }\n if (node.type === 'tag' && node.tag) {\n return name === undefined || node.tag === name;\n }\n return false;\n}\n\n/**\n * Get string attribute value or undefined.\n */\nexport function getStringAttr(node: Node, name: string): string | undefined {\n const value: unknown = node.attributes?.[name];\n return typeof value === 'string' ? value : undefined;\n}\n\n/**\n * Get number attribute value or undefined.\n */\nexport function getNumberAttr(node: Node, name: string): number | undefined {\n const value: unknown = node.attributes?.[name];\n return typeof value === 'number' ? value : undefined;\n}\n\n/**\n * Get boolean attribute value or undefined.\n */\nexport function getBooleanAttr(node: Node, name: string): boolean | undefined {\n const value: unknown = node.attributes?.[name];\n return typeof value === 'boolean' ? value : undefined;\n}\n\n/**\n * Get validator references from validate attribute.\n * Handles both single string and array formats.\n */\nexport function getValidateAttr(node: Node): ValidatorRef[] | undefined {\n const value: unknown = node.attributes?.validate;\n if (value === undefined || value === null) {\n return undefined;\n }\n if (Array.isArray(value)) {\n return value as ValidatorRef[];\n }\n if (typeof value === 'string') {\n return [value];\n }\n if (typeof value === 'object') {\n // Single object validator ref like { id: \"foo\", param: 1 }\n return [value as ValidatorRef];\n }\n return undefined;\n}\n\n/**\n * Get string array attribute value or undefined.\n * Handles both single string (converts to array) and array formats.\n */\nexport function getStringArrayAttr(node: Node, name: string): string[] | undefined {\n const value: unknown = node.attributes?.[name];\n if (value === undefined || value === null) {\n return undefined;\n }\n if (Array.isArray(value)) {\n // Filter to only strings\n const strings = value.filter((v): v is string => typeof v === 'string');\n return strings.length > 0 ? strings : undefined;\n }\n if (typeof value === 'string') {\n return [value];\n }\n return undefined;\n}\n\n/**\n * Parsed option item from AST.\n */\nexport interface ParsedOptionItem {\n /** ID from {% #id %} annotation */\n id: string | null;\n /** Text content (e.g., \"[ ] Label\" or \"[x] Label\") */\n text: string;\n}\n\n/**\n * Extract option items from node children (for option lists).\n * Works with raw AST nodes. Collects text and ID from list items.\n */\nexport function extractOptionItems(node: Node): ParsedOptionItem[] {\n const items: ParsedOptionItem[] = [];\n\n /**\n * Collect all text content from a node tree into a single string.\n */\n function collectText(n: Node): string {\n let text = '';\n\n // Text nodes have content in attributes\n if (n.type === 'text' && typeof n.attributes?.content === 'string') {\n text += n.attributes.content;\n }\n\n // Softbreak is a newline\n if (n.type === 'softbreak') {\n text += '\\n';\n }\n\n // Recurse into children\n if (n.children && Array.isArray(n.children)) {\n for (const c of n.children) {\n text += collectText(c);\n }\n }\n\n return text;\n }\n\n /**\n * Traverse to find list items and extract their content.\n */\n function traverse(child: Node): void {\n if (!child || typeof child !== 'object') {\n return;\n }\n\n // List items contain the option text and ID\n if (child.type === 'item') {\n const text = collectText(child);\n // Markdoc parses {% #id %} as an id attribute on the item (tight list)\n // or on the first child paragraph (loose list with blank lines between items)\n let id: string | null = null;\n if (typeof child.attributes?.id === 'string') {\n // Tight list: ID is directly on the item\n id = child.attributes.id;\n } else if (\n child.children &&\n Array.isArray(child.children) &&\n child.children.length > 0 &&\n typeof child.children[0]?.attributes?.id === 'string'\n ) {\n // Loose list: ID is on the first child (paragraph)\n id = child.children[0].attributes.id;\n }\n if (text.trim()) {\n items.push({ id, text: text.trim() });\n }\n return; // Don't recurse further into item children\n }\n\n // Recurse into children\n if (child.children && Array.isArray(child.children)) {\n for (const c of child.children) {\n traverse(c);\n }\n }\n }\n\n if (node.children && Array.isArray(node.children)) {\n for (const child of node.children) {\n traverse(child);\n }\n }\n\n return items;\n}\n\n/**\n * Extract fence value from node children.\n * Looks for ```value code blocks.\n */\nexport function extractFenceValue(node: Node): string | null {\n function traverse(child: Node): string | null {\n if (!child || typeof child !== 'object') {\n return null;\n }\n\n // Check if this is a fence node with language=\"value\"\n if (child.type === 'fence') {\n const lang = child.attributes?.language as string | undefined;\n if (lang === 'value') {\n return typeof child.attributes?.content === 'string' ? child.attributes.content : null;\n }\n }\n\n // Traverse children\n if (child.children && Array.isArray(child.children)) {\n for (const c of child.children) {\n const result = traverse(c);\n if (result !== null) {\n return result;\n }\n }\n }\n\n return null;\n }\n\n if (node.children && Array.isArray(node.children)) {\n for (const child of node.children) {\n const result = traverse(child);\n if (result !== null) {\n return result;\n }\n }\n }\n\n return null;\n}\n\n/**\n * Extract table content from node children.\n * Handles both raw text and Markdoc-parsed table nodes.\n * Reconstructs markdown table format from the AST.\n */\nexport function extractTableContent(node: Node): string | null {\n const lines: string[] = [];\n\n function extractTextFromNode(n: Node): string {\n if (!n || typeof n !== 'object') return '';\n if (n.type === 'text' && typeof n.attributes?.content === 'string') {\n return n.attributes.content;\n }\n if (n.children && Array.isArray(n.children)) {\n return n.children.map(extractTextFromNode).join('');\n }\n return '';\n }\n\n function extractTableRow(trNode: Node): string {\n if (!trNode.children || !Array.isArray(trNode.children)) return '';\n const cells = trNode.children\n .filter((c: Node) => c.type === 'th' || c.type === 'td')\n .map((c: Node) => extractTextFromNode(c).trim());\n return `| ${cells.join(' | ')} |`;\n }\n\n function processNode(child: Node): void {\n if (!child || typeof child !== 'object') return;\n\n // Paragraph containing text (like \"| Name | Role |\")\n if (child.type === 'paragraph' || child.type === 'inline') {\n const text = extractTextFromNode(child).trim();\n if (text) {\n lines.push(text);\n }\n return;\n }\n\n // Text node directly\n if (child.type === 'text' && typeof child.attributes?.content === 'string') {\n const text = child.attributes.content.trim();\n if (text) {\n lines.push(text);\n }\n return;\n }\n\n // Markdoc table node - reconstruct as markdown\n if (child.type === 'table') {\n // Process thead\n const thead = child.children?.find((c: Node) => c.type === 'thead');\n if (thead?.children) {\n for (const tr of thead.children.filter((c: Node) => c.type === 'tr')) {\n lines.push(extractTableRow(tr));\n }\n }\n\n // If we have header rows, add separator\n if (thead?.children?.length) {\n const firstTr = thead.children.find((c: Node) => c.type === 'tr');\n if (firstTr?.children) {\n const colCount = firstTr.children.filter(\n (c: Node) => c.type === 'th' || c.type === 'td',\n ).length;\n const separatorCells = Array(colCount).fill('----');\n lines.push(`| ${separatorCells.join(' | ')} |`);\n }\n }\n\n // Process tbody\n const tbody = child.children?.find((c: Node) => c.type === 'tbody');\n if (tbody?.children) {\n for (const tr of tbody.children.filter((c: Node) => c.type === 'tr')) {\n lines.push(extractTableRow(tr));\n }\n }\n return;\n }\n\n // Traverse children for other node types\n if (child.children && Array.isArray(child.children)) {\n for (const c of child.children) {\n processNode(c);\n }\n }\n }\n\n if (node.children && Array.isArray(node.children)) {\n for (const child of node.children) {\n processNode(child);\n }\n }\n\n const result = lines.join('\\n').trim();\n return result || null;\n}\n","/**\n * Sentinel value parsing for Markform fields.\n *\n * Sentinel values (%SKIP% and %ABORT%) are special markers that indicate\n * a field was intentionally skipped or aborted rather than filled in.\n */\n\nimport type { Node } from '@markdoc/markdoc';\n\nimport type { FieldResponse } from './coreTypes.js';\nimport { extractFenceValue, getStringAttr } from './parseHelpers.js';\nimport { MarkformParseError } from '../errors.js';\n\n// =============================================================================\n// Sentinel Constants\n// =============================================================================\n\n/** Sentinel values for text fields */\nexport const SENTINEL_SKIP = '%SKIP%';\nexport const SENTINEL_ABORT = '%ABORT%';\n\n// =============================================================================\n// Sentinel Parsing\n// =============================================================================\n\n/**\n * Parsed sentinel value.\n */\nexport interface ParsedSentinel {\n type: 'skip' | 'abort';\n reason?: string;\n}\n\n/**\n * Detect if a value contains a sentinel pattern (%SKIP% or %ABORT%).\n *\n * This is the shared low-level detection function used for:\n * - Form parsing validation (strict format)\n * - Patch value validation (reject embedded sentinels)\n * - Table cell parsing (convert to skipped/aborted state)\n *\n * Supports multiple formats that LLMs might generate:\n * - `%SKIP%` or `%skip%` (case-insensitive)\n * - `%SKIP% (reason)` - canonical format\n * - `%SKIP:reason%` or `%SKIP(reason)%` - compact formats\n *\n * @param value - The value to check (returns null for non-strings)\n * @returns Detected sentinel type and optional reason, or null if no sentinel\n */\nexport function detectSentinel(value: unknown): ParsedSentinel | null {\n if (value == null || typeof value !== 'string') return null;\n\n const trimmed = value.trim();\n\n // Pattern for compact formats: %SKIP:reason% or %SKIP(reason)%\n // Case-insensitive, supports both : and ( as separators\n // Use non-greedy (.*?) to avoid capturing the closing ) or trailing %\n const compactSkipMatch = /^%SKIP(?:[:(](.*?))?[)]?%$/i.exec(trimmed);\n if (compactSkipMatch) {\n const reason = compactSkipMatch[1]?.trim();\n return { type: 'skip', ...(reason && { reason }) };\n }\n\n const compactAbortMatch = /^%ABORT(?:[:(](.*?))?[)]?%$/i.exec(trimmed);\n if (compactAbortMatch) {\n const reason = compactAbortMatch[1]?.trim();\n return { type: 'abort', ...(reason && { reason }) };\n }\n\n // Pattern for canonical format: %SKIP% (reason) - case-insensitive\n const upper = trimmed.toUpperCase();\n if (upper.startsWith('%SKIP%')) {\n const rest = trimmed.slice(6).trim(); // 6 = '%SKIP%'.length\n if (rest === '') {\n return { type: 'skip' };\n }\n // Extract reason from parentheses\n const reasonMatch = /^\\((.+)\\)$/s.exec(rest);\n if (reasonMatch?.[1]) {\n return { type: 'skip', reason: reasonMatch[1].trim() };\n }\n // Has trailing content but not in parentheses - still a sentinel\n return { type: 'skip' };\n }\n\n if (upper.startsWith('%ABORT%')) {\n const rest = trimmed.slice(7).trim(); // 7 = '%ABORT%'.length\n if (rest === '') {\n return { type: 'abort' };\n }\n // Extract reason from parentheses\n const reasonMatch = /^\\((.+)\\)$/s.exec(rest);\n if (reasonMatch?.[1]) {\n return { type: 'abort', reason: reasonMatch[1].trim() };\n }\n // Has trailing content but not in parentheses - still a sentinel\n return { type: 'abort' };\n }\n\n return null;\n}\n\n/**\n * Parse a sentinel value with optional parenthesized reason (strict format).\n *\n * This is the strict parser used during form parsing, where we want to\n * validate the exact format. For patch validation, use detectSentinel().\n *\n * Formats: `%SKIP%`, `%SKIP% (reason text)`, `%ABORT%`, `%ABORT% (reason text)`\n * Returns null if the content is not a valid sentinel format.\n */\nexport function parseSentinel(content: string | null): ParsedSentinel | null {\n if (!content) {\n return null;\n }\n\n const trimmed = content.trim();\n const reasonPattern = /^\\((.+)\\)$/s;\n\n // Check for %SKIP% with optional reason (case-sensitive for strict parsing)\n if (trimmed.startsWith(SENTINEL_SKIP)) {\n const rest = trimmed.slice(SENTINEL_SKIP.length).trim();\n if (rest === '') {\n return { type: 'skip' };\n }\n // Extract reason from parentheses\n const match = reasonPattern.exec(rest);\n if (match?.[1]) {\n return { type: 'skip', reason: match[1].trim() };\n }\n // Invalid format - just %SKIP% with non-parenthesized content\n return null;\n }\n\n // Check for %ABORT% with optional reason (case-sensitive for strict parsing)\n if (trimmed.startsWith(SENTINEL_ABORT)) {\n const rest = trimmed.slice(SENTINEL_ABORT.length).trim();\n if (rest === '') {\n return { type: 'abort' };\n }\n // Extract reason from parentheses\n const match = reasonPattern.exec(rest);\n if (match?.[1]) {\n return { type: 'abort', reason: match[1].trim() };\n }\n // Invalid format - just %ABORT% with non-parenthesized content\n return null;\n }\n\n return null;\n}\n\n/**\n * Check for sentinel values in fence content and validate against state attribute.\n * Handles the common pattern of checking for %SKIP% and %ABORT% sentinels in field values.\n *\n * @param node - The field node to check\n * @param fieldId - The field ID for error messages\n * @param required - Whether the field is required (skip not allowed on required fields)\n * @returns A FieldResponse if a sentinel is found, null otherwise\n */\nexport function tryParseSentinelResponse(\n node: Node,\n fieldId: string,\n required: boolean,\n): FieldResponse | null {\n const fenceContent = extractFenceValue(node);\n const stateAttr = getStringAttr(node, 'state');\n\n const sentinel = parseSentinel(fenceContent);\n if (!sentinel) {\n return null;\n }\n\n if (sentinel.type === 'skip') {\n if (stateAttr !== undefined && stateAttr !== 'skipped') {\n throw new MarkformParseError(\n `Field '${fieldId}' has conflicting state='${stateAttr}' with %SKIP% sentinel`,\n );\n }\n if (required) {\n throw new MarkformParseError(\n `Field '${fieldId}' is required but has %SKIP% sentinel. Cannot skip required fields.`,\n );\n }\n return { state: 'skipped', ...(sentinel.reason && { reason: sentinel.reason }) };\n }\n\n if (sentinel.type === 'abort') {\n if (stateAttr !== undefined && stateAttr !== 'aborted') {\n throw new MarkformParseError(\n `Field '${fieldId}' has conflicting state='${stateAttr}' with %ABORT% sentinel`,\n );\n }\n return { state: 'aborted', ...(sentinel.reason && { reason: sentinel.reason }) };\n }\n\n return null;\n}\n","/**\n * Preprocessor for HTML comment syntax in Markform files.\n *\n * Transforms HTML comment-style directives (`<!-- tag -->`) to Markdoc syntax (`{% tag %}`)\n * before parsing. This enables forms to render cleanly on GitHub and standard Markdown editors.\n *\n * Key rules:\n * - A document is identified as Markform by a form tag with `id=` attribute\n * - Tag transformation only occurs WITHIN the form tag boundaries\n * - Comments outside the form pass through unchanged (prevents collisions)\n */\n\nimport type { SyntaxStyle } from './coreTypes.js';\n\n// Re-export for convenience\nexport type { SyntaxStyle } from './coreTypes.js';\n\n// Known Markform tag names that trigger comment-to-tag transformation (when inside form)\nconst MARKFORM_TAGS = new Set(['form', 'field', 'group', 'note', 'instructions', 'description']);\n\n/**\n * Check if a string starts with a known Markform tag name.\n * Returns the tag name if found, null otherwise.\n */\nfunction startsWithMarkformTag(content: string): string | null {\n const trimmed = content.trim();\n for (const tag of MARKFORM_TAGS) {\n // Check for exact tag name followed by space, end, or attribute\n if (trimmed === tag || trimmed.startsWith(tag + ' ') || trimmed.startsWith(tag + '/')) {\n return tag;\n }\n }\n return null;\n}\n\n/**\n * Check if content represents a valid form tag with id= attribute.\n * A valid form tag must:\n * - Start with \"form \" (the tag name followed by space for attributes)\n * - Include an `id=` attribute in any valid Markdoc syntax:\n * - Double quotes: id=\"value\"\n * - Single quotes: id='value'\n * - Unquoted: id=value\n * - Expression: id={variable}\n * - With spaces: id = \"value\"\n */\nfunction isValidFormTag(content: string): boolean {\n const trimmed = content.trim();\n // Must start with \"form\" followed by space (to have attributes)\n if (!trimmed.startsWith('form ')) {\n return false;\n }\n // Must contain = (has attributes) and id= specifically\n // \\b ensures word boundary so \"valid=\" doesn't match \"id\" in \"valid\"\n return trimmed.includes('=') && /\\bid\\s*=/.test(trimmed);\n}\n\n// =============================================================================\n// State Machine for Code Block Detection\n// =============================================================================\n\n/** Parser state for tracking code blocks */\nconst enum State {\n NORMAL = 0,\n FENCED_CODE = 1,\n}\n\n/**\n * Check if position is at the start of a line (or at position 0).\n */\nfunction isAtLineStart(input: string, pos: number): boolean {\n return pos === 0 || input[pos - 1] === '\\n';\n}\n\n/**\n * Check if position is in leading whitespace of a line.\n * Returns true if all characters between the last newline (or start) and pos are spaces.\n */\nexport function isInLeadingWhitespace(input: string, pos: number): boolean {\n let j = pos - 1;\n while (j >= 0 && input[j] !== '\\n') {\n if (input[j] !== ' ' && input[j] !== '\\t') {\n return false;\n }\n j--;\n }\n return true;\n}\n\n/**\n * Match a fenced code block opening at the given position.\n * Returns fence info if found, null otherwise.\n * Handles 0-3 leading spaces per CommonMark spec.\n */\nfunction matchFenceOpening(\n input: string,\n pos: number,\n): { char: string; length: number; fullMatch: string } | null {\n // Check for 0-3 leading spaces (4+ spaces = indented code block, not fence)\n let indent = 0;\n let i = pos;\n while (i < input.length && input[i] === ' ') {\n indent++;\n i++;\n }\n\n // 4+ spaces means this is not a fenced code block per CommonMark\n if (indent >= 4) {\n return null;\n }\n\n // Check for fence character (` or ~)\n const fenceChar = input[i];\n if (fenceChar !== '`' && fenceChar !== '~') {\n return null;\n }\n\n // Count consecutive fence characters (need at least 3)\n let fenceLength = 0;\n while (i + fenceLength < input.length && input[i + fenceLength] === fenceChar) {\n fenceLength++;\n }\n\n if (fenceLength < 3) {\n return null;\n }\n\n // Find end of line to get full match\n let endOfLine = i + fenceLength;\n while (endOfLine < input.length && input[endOfLine] !== '\\n') {\n endOfLine++;\n }\n // Include the newline if present\n if (endOfLine < input.length) {\n endOfLine++;\n }\n\n return {\n char: fenceChar,\n length: fenceLength,\n fullMatch: input.slice(pos, endOfLine),\n };\n}\n\n/**\n * Check if position matches a closing fence for the given opening fence.\n */\nfunction matchFenceClosing(\n input: string,\n pos: number,\n fenceChar: string,\n fenceLength: number,\n): boolean {\n // Check for 0-3 leading spaces\n let indent = 0;\n let i = pos;\n while (indent < 4 && i < input.length && input[i] === ' ') {\n indent++;\n i++;\n }\n\n // Check for matching fence character\n if (input[i] !== fenceChar) {\n return false;\n }\n\n // Count consecutive fence characters (need at least fenceLength)\n let closingLength = 0;\n while (i + closingLength < input.length && input[i + closingLength] === fenceChar) {\n closingLength++;\n }\n\n if (closingLength < fenceLength) {\n return false;\n }\n\n // Rest of line must be whitespace only (or end of string)\n let afterFence = i + closingLength;\n while (afterFence < input.length && input[afterFence] !== '\\n') {\n if (input[afterFence] !== ' ' && input[afterFence] !== '\\t') {\n return false;\n }\n afterFence++;\n }\n\n return true;\n}\n\n/**\n * Find the end of an inline code span starting at the given position.\n * Returns the position after the closing backticks, or -1 if not a valid span.\n */\nexport function findInlineCodeEnd(input: string, pos: number): number {\n // Count opening backticks\n let openCount = 0;\n let i = pos;\n while (i < input.length && input[i] === '`') {\n openCount++;\n i++;\n }\n\n if (openCount === 0) {\n return -1;\n }\n\n // Look for matching closing backticks\n while (i < input.length) {\n if (input[i] === '`') {\n let closeCount = 0;\n while (i < input.length && input[i] === '`') {\n closeCount++;\n i++;\n }\n if (closeCount === openCount) {\n return i;\n }\n // Not a match, continue searching\n } else if (input[i] === '\\n') {\n // Inline code can span lines but not across blank lines\n i++;\n } else {\n i++;\n }\n }\n\n return -1;\n}\n\n// =============================================================================\n// Preprocessor\n// =============================================================================\n\n/**\n * Transform HTML comment syntax to Markdoc syntax.\n *\n * Transformation rules:\n * - The form tag with `id=` is always transformed (establishes Markform document)\n * - Other tags are ONLY transformed when inside the form tag boundaries\n * - Comments outside the form pass through unchanged (prevents collisions)\n *\n * Patterns transformed (when inside form):\n * - `<!-- tagname ... -->` → `{% tagname ... %}`\n * - `<!-- /tagname -->` → `{% /tagname %}`\n * - `<!-- tagname ... /-->` → `{% tagname ... /%}`\n * - `<!-- #id -->` → `{% #id %}`\n * - `<!-- .class -->` → `{% .class %}`\n *\n * Code blocks (fenced and inline) are preserved unchanged.\n *\n * @param input - The markdown content\n * @returns The preprocessed content with Markdoc syntax\n */\nexport function preprocessCommentSyntax(input: string): string {\n let output = '';\n let state: State = State.NORMAL;\n let fenceChar = '';\n let fenceLength = 0;\n let i = 0;\n // Track whether we're inside a form tag (for scoped transformation)\n let insideForm = false;\n\n while (i < input.length) {\n switch (state) {\n case State.NORMAL: {\n // Check for fenced code block at line start\n if (isAtLineStart(input, i)) {\n const fence = matchFenceOpening(input, i);\n if (fence) {\n state = State.FENCED_CODE;\n fenceChar = fence.char;\n fenceLength = fence.length;\n output += fence.fullMatch;\n i += fence.fullMatch.length;\n continue;\n }\n }\n\n // Check for inline code (skip it entirely to preserve content)\n // For 3+ backticks at line start, skip - they might be fence-like patterns\n // For 1-2 backticks, always treat as inline code regardless of position\n if (input[i] === '`') {\n let backtickCount = 0;\n let j = i;\n while (j < input.length && input[j] === '`') {\n backtickCount++;\n j++;\n }\n const skipInlineCode = backtickCount >= 3 && isInLeadingWhitespace(input, i);\n if (!skipInlineCode) {\n const end = findInlineCodeEnd(input, i);\n if (end !== -1) {\n output += input.slice(i, end);\n i = end;\n continue;\n }\n }\n }\n\n // Check for HTML comment directive\n if (input.slice(i, i + 4) === '<!--') {\n const endComment = input.indexOf('-->', i + 4);\n if (endComment !== -1) {\n const interior = input.slice(i + 4, endComment).trim();\n\n // Check for form tag with id= (always transform - establishes document)\n if (isValidFormTag(interior)) {\n if (interior.endsWith('/')) {\n // Self-closing form (unusual but valid)\n output += '{% ' + interior.slice(0, -1).trim() + ' /%}';\n } else {\n output += '{% ' + interior + ' %}';\n insideForm = true; // Now inside form, enable other transformations\n }\n i = endComment + 3;\n continue;\n }\n\n // Check for closing form tag\n if (interior === '/form' || interior.startsWith('/form ')) {\n output += '{% ' + interior + ' %}';\n insideForm = false; // Leaving form\n i = endComment + 3;\n continue;\n }\n\n // All other transformations only happen inside the form\n if (insideForm) {\n // Check for known Markform tag (opening tags, excluding form)\n const tagName = startsWithMarkformTag(interior);\n if (tagName && tagName !== 'form') {\n if (interior.endsWith('/')) {\n // Self-closing: <!-- tag /--> → {% tag /%}\n output += '{% ' + interior.slice(0, -1).trim() + ' /%}';\n } else {\n output += '{% ' + interior + ' %}';\n }\n i = endComment + 3;\n continue;\n }\n\n // Check for closing tag: <!-- /tagname -->\n if (interior.startsWith('/')) {\n const closingTagName = startsWithMarkformTag(interior.slice(1));\n if (closingTagName) {\n output += '{% ' + interior + ' %}';\n i = endComment + 3;\n continue;\n }\n }\n\n // Check for #id or .class annotations\n if (interior.startsWith('#') || interior.startsWith('.')) {\n output += '{% ' + interior + ' %}';\n i = endComment + 3;\n continue;\n }\n }\n\n // Not a Markform directive (or outside form) - pass through unchanged\n }\n }\n\n output += input[i];\n i++;\n break;\n }\n\n case State.FENCED_CODE: {\n // Check for fence close at line start\n if (isAtLineStart(input, i) && matchFenceClosing(input, i, fenceChar, fenceLength)) {\n // Find end of closing fence line\n let endLine = i;\n while (endLine < input.length && input[endLine] !== '\\n') {\n endLine++;\n }\n if (endLine < input.length) {\n endLine++; // Include newline\n }\n output += input.slice(i, endLine);\n i = endLine;\n state = State.NORMAL;\n fenceChar = '';\n fenceLength = 0;\n continue;\n }\n\n output += input[i];\n i++;\n break;\n }\n }\n }\n\n return output;\n}\n\n// =============================================================================\n// Syntax Detection\n// =============================================================================\n\n/**\n * Detect which syntax style is used in a document.\n *\n * Detection is based on the FORM TAG only:\n * - `<!-- form ... id=\"...\" -->` → 'comments' style\n * - `{% form ... id=\"...\" %}` → 'tags' style\n *\n * The form tag must have `id=` attribute to be recognized as a valid Markform document.\n * Other patterns (field, group, #id, .class) do not trigger detection by themselves.\n *\n * Code blocks (fenced and inline) are skipped to avoid false positives from examples.\n *\n * @param input - The markdown content\n * @returns The detected syntax style, defaults to 'tags' if no valid form tag found\n */\nexport function detectSyntaxStyle(input: string): SyntaxStyle {\n let state: State = State.NORMAL;\n let fenceChar = '';\n let fenceLength = 0;\n let i = 0;\n\n while (i < input.length) {\n switch (state) {\n case State.NORMAL: {\n // Check for fenced code block at line start\n if (isAtLineStart(input, i)) {\n const fence = matchFenceOpening(input, i);\n if (fence) {\n state = State.FENCED_CODE;\n fenceChar = fence.char;\n fenceLength = fence.length;\n i += fence.fullMatch.length;\n continue;\n }\n }\n\n // Check for inline code (skip it entirely)\n // For 3+ backticks at line start, skip - they might be fence-like patterns\n if (input[i] === '`') {\n let backtickCount = 0;\n let j = i;\n while (j < input.length && input[j] === '`') {\n backtickCount++;\n j++;\n }\n const skipInlineCode = backtickCount >= 3 && isInLeadingWhitespace(input, i);\n if (!skipInlineCode) {\n const end = findInlineCodeEnd(input, i);\n if (end !== -1) {\n i = end;\n continue;\n }\n }\n }\n\n // Check for HTML comment form tag with id=\n if (input.slice(i, i + 4) === '<!--') {\n const endComment = input.indexOf('-->', i + 4);\n if (endComment !== -1) {\n const interior = input.slice(i + 4, endComment).trim();\n // Only the form tag with id= determines syntax style\n if (isValidFormTag(interior)) {\n return 'comments';\n }\n }\n }\n\n // Check for Markdoc form tag with id=\n if (input.slice(i, i + 2) === '{%') {\n const endTag = input.indexOf('%}', i + 2);\n if (endTag !== -1) {\n const interior = input.slice(i + 2, endTag).trim();\n // Check if this is a form tag with id=\n if (isValidFormTag(interior)) {\n return 'tags';\n }\n }\n }\n\n i++;\n break;\n }\n\n case State.FENCED_CODE: {\n // Check for fence close at line start\n if (isAtLineStart(input, i) && matchFenceClosing(input, i, fenceChar, fenceLength)) {\n // Find end of closing fence line\n let endLine = i;\n while (endLine < input.length && input[endLine] !== '\\n') {\n endLine++;\n }\n if (endLine < input.length) {\n endLine++; // Include newline\n }\n i = endLine;\n state = State.NORMAL;\n fenceChar = '';\n fenceLength = 0;\n continue;\n }\n\n i++;\n break;\n }\n }\n }\n\n // No syntax markers found, default to tags\n return 'tags';\n}\n\n// =============================================================================\n// Syntax Consistency Validation\n// =============================================================================\n\n/**\n * A violation found when validating syntax consistency.\n */\nexport interface SyntaxViolation {\n /** 1-indexed line number where the violation was found */\n line: number;\n /** The pattern that violated the expected syntax */\n pattern: string;\n /** The syntax style that was found (opposite of expected) */\n foundSyntax: SyntaxStyle;\n}\n\n/**\n * Validate that a document uses only the specified syntax style.\n *\n * Scans the document for patterns of the \"wrong\" syntax and returns violations.\n * Only checks within form tag boundaries (comments outside form are ignored).\n * Code blocks (fenced and inline) are skipped.\n *\n * @param input - The markdown content\n * @param expectedSyntax - The syntax style that should be used\n * @returns Array of violations (empty if document is consistent)\n */\nexport function validateSyntaxConsistency(\n input: string,\n expectedSyntax: SyntaxStyle,\n): SyntaxViolation[] {\n const violations: SyntaxViolation[] = [];\n let state: State = State.NORMAL;\n let fenceChar = '';\n let fenceLength = 0;\n let i = 0;\n let lineNumber = 1;\n // Track whether we're inside a form tag (only validate within form)\n let insideForm = false;\n\n while (i < input.length) {\n // Track line numbers\n if (input[i] === '\\n') {\n lineNumber++;\n }\n\n switch (state) {\n case State.NORMAL: {\n // Check for fenced code block at line start\n if (isAtLineStart(input, i)) {\n const fence = matchFenceOpening(input, i);\n if (fence) {\n state = State.FENCED_CODE;\n fenceChar = fence.char;\n fenceLength = fence.length;\n // Count newlines in the fence line\n for (const ch of fence.fullMatch) {\n if (ch === '\\n') lineNumber++;\n }\n i += fence.fullMatch.length;\n continue;\n }\n }\n\n // Check for inline code (skip it entirely)\n // For 3+ backticks at line start, skip - they might be fence-like patterns\n if (input[i] === '`') {\n let backtickCount = 0;\n let k = i;\n while (k < input.length && input[k] === '`') {\n backtickCount++;\n k++;\n }\n const skipInlineCode = backtickCount >= 3 && isInLeadingWhitespace(input, i);\n if (!skipInlineCode) {\n const end = findInlineCodeEnd(input, i);\n if (end !== -1) {\n // Count newlines in the inline code\n for (let m = i; m < end; m++) {\n if (input[m] === '\\n') lineNumber++;\n }\n i = end;\n continue;\n }\n }\n }\n\n // Track form boundaries for both syntaxes\n if (input.slice(i, i + 4) === '<!--') {\n const endComment = input.indexOf('-->', i + 4);\n if (endComment !== -1) {\n const interior = input.slice(i + 4, endComment).trim();\n if (isValidFormTag(interior)) {\n insideForm = true;\n } else if (interior === '/form' || interior.startsWith('/form ')) {\n insideForm = false;\n }\n }\n }\n if (input.slice(i, i + 2) === '{%') {\n const endTag = input.indexOf('%}', i + 2);\n if (endTag !== -1) {\n const interior = input.slice(i + 2, endTag).trim();\n if (isValidFormTag(interior)) {\n insideForm = true;\n } else if (interior === '/form' || interior.startsWith('/form ')) {\n insideForm = false;\n }\n }\n }\n\n // Check for violations based on expected syntax (only inside form)\n if (insideForm) {\n if (expectedSyntax === 'comments') {\n // Looking for Markdoc syntax violations\n if (input.slice(i, i + 2) === '{%') {\n // Find the end of this tag to get the pattern\n const endTag = input.indexOf('%}', i + 2);\n if (endTag !== -1) {\n const pattern = input.slice(i, endTag + 2);\n violations.push({\n line: lineNumber,\n pattern,\n foundSyntax: 'tags',\n });\n }\n }\n } else {\n // expectedSyntax === 'tags'\n // Looking for HTML comment syntax violations (known Markform tags)\n if (input.slice(i, i + 4) === '<!--') {\n const endComment = input.indexOf('-->', i + 4);\n if (endComment !== -1) {\n const interior = input.slice(i + 4, endComment).trim();\n const pattern = input.slice(i, endComment + 3);\n\n // Check for known Markform tag (or valid form tag)\n if (startsWithMarkformTag(interior) || isValidFormTag(interior)) {\n violations.push({\n line: lineNumber,\n pattern,\n foundSyntax: 'comments',\n });\n }\n // Check for closing tag: <!-- /tagname -->\n else if (interior.startsWith('/') && startsWithMarkformTag(interior.slice(1))) {\n violations.push({\n line: lineNumber,\n pattern,\n foundSyntax: 'comments',\n });\n }\n // Check for #id or .class annotations\n else if (/^[#.][a-zA-Z_-]/.test(interior)) {\n violations.push({\n line: lineNumber,\n pattern,\n foundSyntax: 'comments',\n });\n }\n }\n }\n }\n }\n\n i++;\n break;\n }\n\n case State.FENCED_CODE: {\n // Check for fence close at line start\n if (isAtLineStart(input, i) && matchFenceClosing(input, i, fenceChar, fenceLength)) {\n // Find end of closing fence line\n let endLine = i;\n while (endLine < input.length && input[endLine] !== '\\n') {\n endLine++;\n }\n if (endLine < input.length) {\n endLine++; // Include newline\n lineNumber++;\n }\n i = endLine;\n state = State.NORMAL;\n fenceChar = '';\n fenceLength = 0;\n continue;\n }\n\n i++;\n break;\n }\n }\n }\n\n return violations;\n}\n","/**\n * Key-based sorting utilities.\n */\n\n/** Create a comparator from a key function. */\nfunction keyComparator<T, K>(keyFn: (item: T) => K): (a: T, b: T) => number {\n return (a, b) => {\n const ka = keyFn(a),\n kb = keyFn(b);\n return ka < kb ? -1 : ka > kb ? 1 : 0;\n };\n}\n\n/**\n * Comparator that sorts priority keys first (in order), then remaining keys alphabetically.\n *\n * @example\n * ```ts\n * const attrs = { label: 'Name', kind: 'string', id: 'name', required: true };\n * const keys = Object.keys(attrs).sort(priorityKeyComparator(['kind', 'id']));\n * // => ['kind', 'id', 'label', 'required']\n * ```\n */\nexport function priorityKeyComparator(priorityKeys: string[]): (a: string, b: string) => number {\n return keyComparator((key: string) => {\n const i = priorityKeys.indexOf(key);\n return [i !== -1 ? i : Infinity, key] as [number, string];\n });\n}\n","/**\n * URL formatting utilities for display and markdown output.\n */\n\n/**\n * Extract the domain (hostname) from a URL.\n * Returns the original string if parsing fails.\n *\n * @param url - The URL to extract the domain from\n * @returns The domain (e.g., \"example.com\") or the original string if invalid\n */\nexport function extractDomain(url: string): string {\n try {\n const parsed = new URL(url);\n return parsed.hostname;\n } catch {\n // If URL parsing fails, try to extract domain-like pattern\n const match = /^(?:https?:\\/\\/)?(?:www\\.)?([^/\\s]+)/i.exec(url);\n if (match?.[1]) {\n return match[1];\n }\n // Return original if we can't extract a domain\n return url;\n }\n}\n\n/**\n * Create a friendly abbreviated display name for a URL.\n * - Drops \"www.\" prefix from domain\n * - Adds first portion of path (up to maxPathChars) if present\n * - Adds ellipsis (…) if path is truncated\n *\n * @param url - The URL to abbreviate\n * @param maxPathChars - Maximum characters to include from the path (default: 12)\n * @returns Friendly abbreviated URL (e.g., \"example.com/docs/api…\")\n */\nexport function friendlyUrlAbbrev(url: string, maxPathChars = 12): string {\n try {\n const parsed = new URL(url);\n // Remove www. prefix from hostname\n let hostname = parsed.hostname;\n if (hostname.startsWith('www.')) {\n hostname = hostname.slice(4);\n }\n\n // Get path without leading slash, excluding query string and hash\n const path = parsed.pathname.slice(1);\n if (!path) {\n return hostname;\n }\n\n // Include path up to maxPathChars\n if (path.length <= maxPathChars) {\n return `${hostname}/${path}`;\n }\n\n // Truncate path and add ellipsis\n return `${hostname}/${path.slice(0, maxPathChars)}…`;\n } catch {\n // If URL parsing fails, try basic cleanup\n let result = url;\n // Remove protocol\n result = result.replace(/^https?:\\/\\//, '');\n // Remove www.\n result = result.replace(/^www\\./, '');\n // Truncate if too long\n const maxLen = 30;\n if (result.length > maxLen) {\n return result.slice(0, maxLen) + '…';\n }\n return result;\n }\n}\n\n/**\n * Format a URL as a markdown link with a friendly abbreviated display text.\n * The full URL is preserved as the link target.\n *\n * @param url - The URL to format\n * @returns Markdown link in format [friendly-abbrev](url)\n */\nexport function formatUrlAsMarkdownLink(url: string): string {\n const display = friendlyUrlAbbrev(url);\n return `[${display}](${url})`;\n}\n\n/**\n * Check if a string looks like a URL.\n *\n * @param str - The string to check\n * @returns true if the string appears to be a URL\n */\nexport function isUrl(str: string): boolean {\n // Check for common URL patterns\n if (str.startsWith('http://') || str.startsWith('https://')) {\n return true;\n }\n // Check for www. prefix\n if (str.startsWith('www.')) {\n return true;\n }\n return false;\n}\n\n/**\n * Format bare URLs in text as HTML links with abbreviated display text.\n * Also handles markdown-style links [text](url) for consistency.\n *\n * Processing order:\n * 1. Escape all HTML to prevent XSS\n * 2. Convert markdown links [text](url) to <a> tags\n * 3. Convert bare URLs (not already in links) to <a> tags with abbreviated display\n *\n * @param text - The raw text containing URLs (will be HTML-escaped)\n * @param escapeHtml - Function to escape HTML entities\n * @returns HTML-safe text with URLs converted to <a> tags\n */\nexport function formatBareUrlsAsHtmlLinks(text: string, escapeHtml: (s: string) => string): string {\n // SECURITY: Escape the entire text first to prevent XSS\n let result = escapeHtml(text);\n\n // Convert markdown links [text](url) to <a> tags\n // After escaping, we need to unescape &amp; back to & for URLs\n result = result.replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, (_match, linkText: string, url: string) => {\n const cleanUrl = url.replace(/&amp;/g, '&');\n return `<a href=\"${escapeHtml(cleanUrl)}\" target=\"_blank\" class=\"url-link\" data-url=\"${escapeHtml(cleanUrl)}\">${linkText}</a>`;\n });\n\n // Convert bare URLs to <a> tags with abbreviated display\n // Uses negative lookbehind to skip URLs that are:\n // - Inside href=\"\" or data-url=\"\" attributes\n // - Inside anchor tag content (preceded by \">)\n // Pattern matches http://, https://, www. URLs\n result = result.replace(\n /(?<!href=\"|data-url=\"|\">)(?:https?:\\/\\/|www\\.)[^\\s<>\"]+(?<![.,;:!?'\")])/g,\n (url: string) => {\n // Unescape &amp; back to & for the actual URL\n const cleanUrl = url.replace(/&amp;/g, '&');\n // Normalize www. URLs to have https://\n const fullUrl = cleanUrl.startsWith('www.') ? `https://${cleanUrl}` : cleanUrl;\n const display = friendlyUrlAbbrev(fullUrl);\n return `<a href=\"${escapeHtml(fullUrl)}\" target=\"_blank\" class=\"url-link\" data-url=\"${escapeHtml(fullUrl)}\">${escapeHtml(display)}</a>`;\n },\n );\n\n return result;\n}\n","/**\n * Canonical serializer for .form.md files.\n *\n * Serializes a ParsedForm back to a Markdoc markdown document.\n * The output is deterministic and suitable for round-trip testing.\n */\n\nimport YAML from 'yaml';\n\nimport type {\n CellResponse,\n CheckboxesField,\n CheckboxesValue,\n CheckboxValue,\n ColumnTypeName,\n DateField,\n DateValue,\n DocumentationBlock,\n Field,\n FieldBase,\n FieldGroup,\n FieldResponse,\n FormMetadata,\n FormSchema,\n Id,\n MultiSelectField,\n MultiSelectValue,\n Note,\n NumberField,\n NumberValue,\n Option,\n ParsedForm,\n SingleSelectField,\n SingleSelectValue,\n StringField,\n StringListField,\n StringListValue,\n StringValue,\n SyntaxStyle,\n TableColumn,\n TableField,\n TableRowResponse,\n TableValue,\n UrlField,\n UrlListField,\n UrlListValue,\n UrlValue,\n YearField,\n YearValue,\n} from './coreTypes.js';\nimport {\n AGENT_ROLE,\n DEFAULT_PRIORITY,\n MF_SPEC_VERSION,\n transformHarnessConfigToYaml,\n YAML_STRINGIFY_OPTIONS,\n} from '../settings.js';\nimport { priorityKeyComparator } from '../utils/keySort.js';\nimport { formatUrlAsMarkdownLink } from '../utils/urlFormat.js';\nimport { findInlineCodeEnd, isInLeadingWhitespace } from './preprocess.js';\n\n// =============================================================================\n// Smart Fence Selection Helpers\n// =============================================================================\n\n/** Fence character types for value fences */\ntype FenceChar = '`' | '~';\n\n/** Result of pickFence analysis */\ninterface FenceChoice {\n /** The fence character to use (backticks or tildes) */\n char: FenceChar;\n /** The fence length (minimum 3) */\n len: number;\n /** Whether to add process=false for Markdoc tags */\n processFalse: boolean;\n}\n\n/**\n * Find the maximum run of fence characters at line starts (indent ≤ 3 spaces).\n * Lines with 4+ space indent are inside code blocks so don't break fences.\n */\nexport function maxRunAtLineStart(value: string, char: FenceChar): number {\n // Pattern: 0-3 leading spaces, then runs of the fence char\n const escaped = char === '`' ? '`' : '~';\n const pattern = new RegExp(`^( {0,3})${escaped}+`, 'gm');\n\n let maxRun = 0;\n let match: RegExpExecArray | null;\n\n while ((match = pattern.exec(value)) !== null) {\n // The full match includes the leading spaces, so subtract them\n const indent = match[1]?.length ?? 0;\n const runLength = match[0].length - indent;\n if (runLength > maxRun) {\n maxRun = runLength;\n }\n }\n\n return maxRun;\n}\n\n/**\n * Pick the optimal fence character and length for a value.\n * Also detects if process=false is needed for Markdoc tags.\n */\nexport function pickFence(value: string): FenceChoice {\n // Check for Markdoc tags\n const hasMarkdocTags = value.includes('{%');\n\n // Get max runs for both fence types\n const maxBackticks = maxRunAtLineStart(value, '`');\n const maxTildes = maxRunAtLineStart(value, '~');\n\n // Pick the char with smaller max-run; prefer backticks on tie\n let char: FenceChar;\n let maxRun: number;\n\n if (maxBackticks <= maxTildes) {\n char = '`';\n maxRun = maxBackticks;\n } else {\n char = '~';\n maxRun = maxTildes;\n }\n\n // Length is max(3, maxRun + 1) to ensure the fence is longer than any in content\n const len = Math.max(3, maxRun + 1);\n\n return { char, len, processFalse: hasMarkdocTags };\n}\n\n// =============================================================================\n// Postprocessor for HTML Comment Syntax\n// =============================================================================\n\n/**\n * Transform Markdoc syntax to HTML comment syntax.\n *\n * Patterns transformed:\n * - `{% tagname ... %}` → `<!-- tagname ... -->`\n * - `{% /tagname %}` → `<!-- /tagname -->`\n * - `{% tagname ... /%}` → `<!-- tagname ... /-->`\n * - `{% #id %}` → `<!-- #id -->`\n * - `{% .class %}` → `<!-- .class -->`\n *\n * Code blocks (fenced) are preserved unchanged.\n *\n * @param input - The Markdoc content\n * @returns The content with HTML comment syntax\n */\nexport function postprocessToCommentSyntax(input: string): string {\n let output = '';\n let inFencedCode = false;\n let fenceChar = '';\n let fenceLength = 0;\n let i = 0;\n\n while (i < input.length) {\n // Check for fenced code block at line start\n if (!inFencedCode && (i === 0 || input[i - 1] === '\\n')) {\n // Check for 0-3 leading spaces followed by fence\n let indent = 0;\n let j = i;\n while (j < input.length && input[j] === ' ' && indent < 4) {\n indent++;\n j++;\n }\n\n if (indent < 4 && (input[j] === '`' || input[j] === '~')) {\n const fc = input[j]!; // Guaranteed by condition above\n let len = 0;\n while (j + len < input.length && input[j + len] === fc) {\n len++;\n }\n if (len >= 3) {\n // Entering fenced code block\n inFencedCode = true;\n fenceChar = fc;\n fenceLength = len;\n // Copy up to end of line\n let endLine = j + len;\n while (endLine < input.length && input[endLine] !== '\\n') {\n endLine++;\n }\n if (endLine < input.length) {\n endLine++; // Include newline\n }\n output += input.slice(i, endLine);\n i = endLine;\n continue;\n }\n }\n }\n\n // Check for closing fence in fenced code block\n if (inFencedCode && (i === 0 || input[i - 1] === '\\n')) {\n let indent = 0;\n let j = i;\n while (j < input.length && input[j] === ' ' && indent < 4) {\n indent++;\n j++;\n }\n\n if (input[j] === fenceChar) {\n let len = 0;\n while (j + len < input.length && input[j + len] === fenceChar) {\n len++;\n }\n if (len >= fenceLength) {\n // Check rest of line is whitespace\n let afterFence = j + len;\n let isClosing = true;\n while (afterFence < input.length && input[afterFence] !== '\\n') {\n if (input[afterFence] !== ' ' && input[afterFence] !== '\\t') {\n isClosing = false;\n break;\n }\n afterFence++;\n }\n if (isClosing) {\n // Exiting fenced code block\n if (afterFence < input.length) {\n afterFence++; // Include newline\n }\n output += input.slice(i, afterFence);\n i = afterFence;\n inFencedCode = false;\n fenceChar = '';\n fenceLength = 0;\n continue;\n }\n }\n }\n }\n\n // Inside fenced code, pass through unchanged\n if (inFencedCode) {\n output += input[i];\n i++;\n continue;\n }\n\n // Check for inline code (skip it entirely to preserve content)\n // For 3+ backticks at line start, skip - they might be fence-like patterns\n // (e.g., indented code blocks that look like fences but aren't valid fences)\n // For 1-2 backticks, always treat as inline code regardless of position\n if (input[i] === '`') {\n let backtickCount = 0;\n let j = i;\n while (j < input.length && input[j] === '`') {\n backtickCount++;\n j++;\n }\n // Skip fence-like patterns (3+ backticks) at line start\n const skipInlineCode = backtickCount >= 3 && isInLeadingWhitespace(input, i);\n if (!skipInlineCode) {\n const end = findInlineCodeEnd(input, i);\n if (end !== -1) {\n output += input.slice(i, end);\n i = end;\n continue;\n }\n }\n }\n\n // Check for Markdoc tag\n if (input.slice(i, i + 2) === '{%') {\n const endTag = input.indexOf('%}', i + 2);\n if (endTag !== -1) {\n const interior = input.slice(i + 2, endTag).trim();\n\n // Check for self-closing tag: {% tagname ... /%}\n if (interior.endsWith('/')) {\n const tagContent = interior.slice(0, -1).trim();\n // Check if it's an annotation (#id or .class)\n if (tagContent.startsWith('#') || tagContent.startsWith('.')) {\n // Annotations don't have self-closing form, but handle gracefully\n output += '<!-- ' + tagContent + ' /-->';\n } else {\n output += '<!-- ' + tagContent + ' /-->';\n }\n i = endTag + 2;\n continue;\n }\n\n // Check for closing tag: {% /tagname %}\n if (interior.startsWith('/')) {\n const tagName = interior.slice(1).trim();\n output += '<!-- /' + tagName + ' -->';\n i = endTag + 2;\n continue;\n }\n\n // Check for annotation (#id or .class)\n if (interior.startsWith('#') || interior.startsWith('.')) {\n output += '<!-- ' + interior + ' -->';\n i = endTag + 2;\n continue;\n }\n\n // Regular opening tag: {% tagname ... %}\n output += '<!-- ' + interior + ' -->';\n i = endTag + 2;\n continue;\n }\n }\n\n output += input[i];\n i++;\n }\n\n return output;\n}\n\n// =============================================================================\n// Sentinel Value Helpers\n// =============================================================================\n\n/**\n * Format a value fence block with the given content.\n * Uses smart fence selection to avoid collision with code blocks in content.\n */\nfunction formatValueFence(content: string): string {\n const { char, len, processFalse } = pickFence(content);\n const fence = char.repeat(len);\n const processAttr = processFalse ? ' {% process=false %}' : '';\n return `\\n${fence}value${processAttr}\\n${content}\\n${fence}\\n`;\n}\n\n/**\n * Get sentinel value content for skipped/aborted fields with reason.\n * Returns the fence block if there's a reason, empty string otherwise.\n */\nfunction getSentinelContent(response: FieldResponse | undefined): string {\n if (response?.state === 'skipped' && response.reason) {\n return formatValueFence(`%SKIP% (${response.reason})`);\n }\n if (response?.state === 'aborted' && response.reason) {\n return formatValueFence(`%ABORT% (${response.reason})`);\n }\n return '';\n}\n\n// =============================================================================\n// Options\n// =============================================================================\n\nexport interface SerializeOptions {\n /** Markform spec version to use in frontmatter. Defaults to MF_SPEC_VERSION. */\n specVersion?: string;\n /**\n * Syntax style to use for output.\n * - 'comments': Use HTML comment syntax (<!-- tag -->) - primary/default\n * - 'tags': Use Markdoc syntax ({% tag %})\n * If not specified, uses the form's detected syntax style (form.syntaxStyle),\n * defaulting to 'tags' if not detected.\n */\n syntaxStyle?: SyntaxStyle;\n /**\n * Whether to preserve content outside Markform tags during serialization.\n * - true (default): Use splice-based serialization to preserve markdown content\n * outside Markform tags (headings, paragraphs, code blocks, etc.)\n * - false: Regenerate entire document from structured data\n *\n * When true and rawSource/tagRegions are available, content preservation uses\n * \"raw slicing\" - keeping original text and only replacing Markform tag regions.\n * Falls back to regeneration if rawSource is unavailable.\n */\n preserveContent?: boolean;\n}\n\n// =============================================================================\n// Attribute Serialization\n// =============================================================================\n\n/**\n * Serialize an attribute value to Markdoc format.\n */\nfunction serializeAttrValue(value: unknown): string {\n if (typeof value === 'string') {\n // Escape backslashes and quotes\n const escaped = value.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"');\n return `\"${escaped}\"`;\n }\n if (typeof value === 'number') {\n return String(value);\n }\n if (typeof value === 'boolean') {\n return value ? 'true' : 'false';\n }\n if (value === null) {\n return 'null';\n }\n if (Array.isArray(value)) {\n const items = value.map((v) => serializeAttrValue(v)).join(', ');\n return `[${items}]`;\n }\n if (typeof value === 'object') {\n // Serialize plain objects as Markdoc object literals: {key: value, key2: value2}\n const entries = Object.entries(value as Record<string, unknown>);\n const parts = entries.map(([k, v]) => `${k}: ${serializeAttrValue(v)}`);\n return `{${parts.join(', ')}}`;\n }\n // Handle remaining primitive types (bigint, symbol, undefined, function)\n // These shouldn't appear in form attributes but handle gracefully\n if (typeof value === 'bigint') {\n return String(value);\n }\n if (typeof value === 'undefined') {\n return 'null';\n }\n // For functions and symbols, throw - these are invalid in form attributes\n throw new Error(`Cannot serialize value of type ${typeof value} to Markdoc`);\n}\n\n/** Priority keys that appear first in serialized attributes, in this order. */\nconst ATTR_PRIORITY_KEYS = ['kind', 'id', 'role'];\n\n/**\n * Serialize attributes to Markdoc format.\n * Priority keys (kind, id, role) appear first in order, then remaining keys alphabetically.\n */\nfunction serializeAttrs(attrs: Record<string, unknown>): string {\n const keys = Object.keys(attrs).sort(priorityKeyComparator(ATTR_PRIORITY_KEYS));\n const parts: string[] = [];\n\n for (const key of keys) {\n const value = attrs[key];\n if (value !== undefined) {\n parts.push(`${key}=${serializeAttrValue(value)}`);\n }\n }\n\n return parts.join(' ');\n}\n\n// =============================================================================\n// Checkbox Marker Serialization\n// =============================================================================\n\n/** Map checkbox state to marker character */\nconst STATE_TO_MARKER: Record<CheckboxValue, string> = {\n // Multi mode\n todo: ' ',\n done: 'x',\n incomplete: '/',\n active: '*',\n na: '-',\n // Explicit mode\n unfilled: ' ',\n yes: 'y',\n no: 'n',\n};\n\n/**\n * Get the checkbox marker for a state.\n */\nfunction getMarker(state: CheckboxValue): string {\n return STATE_TO_MARKER[state] ?? ' ';\n}\n\n// =============================================================================\n// Field Serialization\n// =============================================================================\n\n/**\n * Add parallel and order attributes to a field's attrs object if defined.\n */\nfunction addParallelOrderAttrs(attrs: Record<string, unknown>, field: FieldBase): void {\n if (field.parallel !== undefined) {\n attrs.parallel = field.parallel;\n }\n if (field.order !== undefined) {\n attrs.order = field.order;\n }\n}\n\n/**\n * Serialize a string field.\n */\nfunction serializeStringField(field: StringField, response: FieldResponse | undefined): string {\n const attrs: Record<string, unknown> = { kind: 'string', id: field.id, label: field.label };\n if (field.required) {\n attrs.required = field.required;\n }\n if (field.priority !== DEFAULT_PRIORITY) {\n attrs.priority = field.priority;\n }\n if (field.role !== AGENT_ROLE) {\n attrs.role = field.role;\n }\n if (field.multiline) {\n attrs.multiline = field.multiline;\n }\n if (field.pattern) {\n attrs.pattern = field.pattern;\n }\n if (field.minLength !== undefined) {\n attrs.minLength = field.minLength;\n }\n if (field.maxLength !== undefined) {\n attrs.maxLength = field.maxLength;\n }\n if (field.validate) {\n attrs.validate = field.validate;\n }\n if (field.report !== undefined) {\n attrs.report = field.report;\n }\n addParallelOrderAttrs(attrs, field);\n if (field.placeholder) {\n attrs.placeholder = field.placeholder;\n }\n if (field.examples && field.examples.length > 0) {\n attrs.examples = field.examples;\n }\n\n // Add state attribute for skipped/aborted (markform-216)\n if (response?.state === 'skipped' || response?.state === 'aborted') {\n attrs.state = response.state;\n }\n\n const attrStr = serializeAttrs(attrs);\n let content = '';\n\n // Extract value from response if state is \"answered\"\n if (response?.state === 'answered' && response.value) {\n const value = response.value as StringValue;\n if (value.value) {\n content = formatValueFence(value.value);\n }\n }\n\n // Sentinel with reason for skipped/aborted overrides value content\n const sentinelContent = getSentinelContent(response);\n if (sentinelContent) {\n content = sentinelContent;\n }\n\n return `{% field ${attrStr} %}${content}{% /field %}`;\n}\n\n/**\n * Serialize a number field.\n */\nfunction serializeNumberField(field: NumberField, response: FieldResponse | undefined): string {\n const attrs: Record<string, unknown> = { kind: 'number', id: field.id, label: field.label };\n if (field.required) {\n attrs.required = field.required;\n }\n if (field.priority !== DEFAULT_PRIORITY) {\n attrs.priority = field.priority;\n }\n if (field.role !== AGENT_ROLE) {\n attrs.role = field.role;\n }\n if (field.min !== undefined) {\n attrs.min = field.min;\n }\n if (field.max !== undefined) {\n attrs.max = field.max;\n }\n if (field.integer) {\n attrs.integer = field.integer;\n }\n if (field.validate) {\n attrs.validate = field.validate;\n }\n if (field.report !== undefined) {\n attrs.report = field.report;\n }\n addParallelOrderAttrs(attrs, field);\n if (field.placeholder) {\n attrs.placeholder = field.placeholder;\n }\n if (field.examples && field.examples.length > 0) {\n attrs.examples = field.examples;\n }\n\n // Add state attribute for skipped/aborted (markform-216)\n if (response?.state === 'skipped' || response?.state === 'aborted') {\n attrs.state = response.state;\n }\n\n const attrStr = serializeAttrs(attrs);\n let content = '';\n\n // Extract value from response if state is \"answered\"\n if (response?.state === 'answered' && response.value) {\n const value = response.value as NumberValue;\n if (value.value !== null && value.value !== undefined) {\n content = formatValueFence(String(value.value));\n }\n }\n\n // Sentinel with reason for skipped/aborted overrides value content\n const sentinelContent = getSentinelContent(response);\n if (sentinelContent) {\n content = sentinelContent;\n }\n\n return `{% field ${attrStr} %}${content}{% /field %}`;\n}\n\n/**\n * Serialize a string-list field.\n */\nfunction serializeStringListField(\n field: StringListField,\n response: FieldResponse | undefined,\n): string {\n const attrs: Record<string, unknown> = { kind: 'string_list', id: field.id, label: field.label };\n if (field.required) {\n attrs.required = field.required;\n }\n if (field.priority !== DEFAULT_PRIORITY) {\n attrs.priority = field.priority;\n }\n if (field.role !== AGENT_ROLE) {\n attrs.role = field.role;\n }\n if (field.minItems !== undefined) {\n attrs.minItems = field.minItems;\n }\n if (field.maxItems !== undefined) {\n attrs.maxItems = field.maxItems;\n }\n if (field.itemMinLength !== undefined) {\n attrs.itemMinLength = field.itemMinLength;\n }\n if (field.itemMaxLength !== undefined) {\n attrs.itemMaxLength = field.itemMaxLength;\n }\n if (field.uniqueItems) {\n attrs.uniqueItems = field.uniqueItems;\n }\n if (field.validate) {\n attrs.validate = field.validate;\n }\n if (field.report !== undefined) {\n attrs.report = field.report;\n }\n addParallelOrderAttrs(attrs, field);\n if (field.placeholder) {\n attrs.placeholder = field.placeholder;\n }\n if (field.examples && field.examples.length > 0) {\n attrs.examples = field.examples;\n }\n\n // Add state attribute for skipped/aborted (markform-216)\n if (response?.state === 'skipped' || response?.state === 'aborted') {\n attrs.state = response.state;\n }\n\n const attrStr = serializeAttrs(attrs);\n let content = '';\n\n // Extract value from response if state is \"answered\"\n if (response?.state === 'answered' && response.value) {\n const value = response.value as StringListValue;\n if (value.items && value.items.length > 0) {\n content = formatValueFence(value.items.join('\\n'));\n }\n }\n\n // Sentinel with reason for skipped/aborted overrides value content\n const sentinelContent = getSentinelContent(response);\n if (sentinelContent) {\n content = sentinelContent;\n }\n\n return `{% field ${attrStr} %}${content}{% /field %}`;\n}\n\n/**\n * Serialize options (for single-select, multi-select, checkboxes).\n */\nfunction serializeOptions(options: Option[], selected: Record<string, CheckboxValue>): string {\n const lines: string[] = [];\n\n for (const opt of options) {\n const state = selected[opt.id] ?? 'todo';\n const marker = getMarker(state);\n lines.push(`- [${marker}] ${opt.label} {% #${opt.id} %}`);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Serialize a single-select field.\n */\nfunction serializeSingleSelectField(\n field: SingleSelectField,\n response: FieldResponse | undefined,\n): string {\n const attrs: Record<string, unknown> = {\n kind: 'single_select',\n id: field.id,\n label: field.label,\n };\n if (field.required) {\n attrs.required = field.required;\n }\n if (field.priority !== DEFAULT_PRIORITY) {\n attrs.priority = field.priority;\n }\n if (field.role !== AGENT_ROLE) {\n attrs.role = field.role;\n }\n if (field.validate) {\n attrs.validate = field.validate;\n }\n if (field.report !== undefined) {\n attrs.report = field.report;\n }\n addParallelOrderAttrs(attrs, field);\n\n // Add state attribute for skipped/aborted (markform-216)\n if (response?.state === 'skipped' || response?.state === 'aborted') {\n attrs.state = response.state;\n }\n\n const attrStr = serializeAttrs(attrs);\n\n // Extract value from response if state is \"answered\"\n let value: SingleSelectValue | undefined;\n if (response?.state === 'answered' && response.value) {\n value = response.value as SingleSelectValue;\n }\n\n // Convert selected to checkbox state format\n const selected: Record<string, CheckboxValue> = {};\n for (const opt of field.options) {\n selected[opt.id] = opt.id === value?.selected ? 'done' : 'todo';\n }\n\n const options = serializeOptions(field.options, selected);\n return `{% field ${attrStr} %}\\n${options}\\n{% /field %}`;\n}\n\n/**\n * Serialize a multi-select field.\n */\nfunction serializeMultiSelectField(\n field: MultiSelectField,\n response: FieldResponse | undefined,\n): string {\n const attrs: Record<string, unknown> = { kind: 'multi_select', id: field.id, label: field.label };\n if (field.required) {\n attrs.required = field.required;\n }\n if (field.priority !== DEFAULT_PRIORITY) {\n attrs.priority = field.priority;\n }\n if (field.role !== AGENT_ROLE) {\n attrs.role = field.role;\n }\n if (field.minSelections !== undefined) {\n attrs.minSelections = field.minSelections;\n }\n if (field.maxSelections !== undefined) {\n attrs.maxSelections = field.maxSelections;\n }\n if (field.validate) {\n attrs.validate = field.validate;\n }\n if (field.report !== undefined) {\n attrs.report = field.report;\n }\n addParallelOrderAttrs(attrs, field);\n\n // Add state attribute for skipped/aborted (markform-216)\n if (response?.state === 'skipped' || response?.state === 'aborted') {\n attrs.state = response.state;\n }\n\n const attrStr = serializeAttrs(attrs);\n\n // Extract value from response if state is \"answered\"\n let value: MultiSelectValue | undefined;\n if (response?.state === 'answered' && response.value) {\n value = response.value as MultiSelectValue;\n }\n\n // Convert selected to checkbox state format\n const selected: Record<string, CheckboxValue> = {};\n const selectedSet = new Set(value?.selected ?? []);\n for (const opt of field.options) {\n selected[opt.id] = selectedSet.has(opt.id) ? 'done' : 'todo';\n }\n\n const options = serializeOptions(field.options, selected);\n return `{% field ${attrStr} %}\\n${options}\\n{% /field %}`;\n}\n\n/**\n * Serialize a checkboxes field.\n */\nfunction serializeCheckboxesField(\n field: CheckboxesField,\n response: FieldResponse | undefined,\n): string {\n const attrs: Record<string, unknown> = { kind: 'checkboxes', id: field.id, label: field.label };\n if (field.required) {\n attrs.required = field.required;\n }\n if (field.priority !== DEFAULT_PRIORITY) {\n attrs.priority = field.priority;\n }\n if (field.role !== AGENT_ROLE) {\n attrs.role = field.role;\n }\n if (field.checkboxMode !== 'multi') {\n attrs.checkboxMode = field.checkboxMode;\n }\n if (field.minDone !== undefined) {\n attrs.minDone = field.minDone;\n }\n if (field.approvalMode !== 'none') {\n attrs.approvalMode = field.approvalMode;\n }\n if (field.validate) {\n attrs.validate = field.validate;\n }\n if (field.report !== undefined) {\n attrs.report = field.report;\n }\n addParallelOrderAttrs(attrs, field);\n\n // Add state attribute for skipped/aborted (markform-216)\n if (response?.state === 'skipped' || response?.state === 'aborted') {\n attrs.state = response.state;\n }\n\n const attrStr = serializeAttrs(attrs);\n\n // Extract value from response if state is \"answered\"\n let value: CheckboxesValue | undefined;\n if (response?.state === 'answered' && response.value) {\n value = response.value as CheckboxesValue;\n }\n\n const options = serializeOptions(field.options, value?.values ?? {});\n return `{% field ${attrStr} %}\\n${options}\\n{% /field %}`;\n}\n\n/**\n * Serialize a url-field.\n */\nfunction serializeUrlField(field: UrlField, response: FieldResponse | undefined): string {\n const attrs: Record<string, unknown> = { kind: 'url', id: field.id, label: field.label };\n if (field.required) {\n attrs.required = field.required;\n }\n if (field.priority !== DEFAULT_PRIORITY) {\n attrs.priority = field.priority;\n }\n if (field.role !== AGENT_ROLE) {\n attrs.role = field.role;\n }\n if (field.validate) {\n attrs.validate = field.validate;\n }\n if (field.report !== undefined) {\n attrs.report = field.report;\n }\n addParallelOrderAttrs(attrs, field);\n if (field.placeholder) {\n attrs.placeholder = field.placeholder;\n }\n if (field.examples && field.examples.length > 0) {\n attrs.examples = field.examples;\n }\n\n // Add state attribute for skipped/aborted (markform-216)\n if (response?.state === 'skipped' || response?.state === 'aborted') {\n attrs.state = response.state;\n }\n\n const attrStr = serializeAttrs(attrs);\n let content = '';\n\n // Extract value from response if state is \"answered\"\n if (response?.state === 'answered' && response.value) {\n const value = response.value as UrlValue;\n if (value.value) {\n content = formatValueFence(value.value);\n }\n }\n\n // Sentinel with reason for skipped/aborted overrides value content\n const sentinelContent = getSentinelContent(response);\n if (sentinelContent) {\n content = sentinelContent;\n }\n\n return `{% field ${attrStr} %}${content}{% /field %}`;\n}\n\n/**\n * Serialize a url-list field.\n */\nfunction serializeUrlListField(field: UrlListField, response: FieldResponse | undefined): string {\n const attrs: Record<string, unknown> = { kind: 'url_list', id: field.id, label: field.label };\n if (field.required) {\n attrs.required = field.required;\n }\n if (field.priority !== DEFAULT_PRIORITY) {\n attrs.priority = field.priority;\n }\n if (field.role !== AGENT_ROLE) {\n attrs.role = field.role;\n }\n if (field.minItems !== undefined) {\n attrs.minItems = field.minItems;\n }\n if (field.maxItems !== undefined) {\n attrs.maxItems = field.maxItems;\n }\n if (field.uniqueItems) {\n attrs.uniqueItems = field.uniqueItems;\n }\n if (field.validate) {\n attrs.validate = field.validate;\n }\n if (field.report !== undefined) {\n attrs.report = field.report;\n }\n addParallelOrderAttrs(attrs, field);\n if (field.placeholder) {\n attrs.placeholder = field.placeholder;\n }\n if (field.examples && field.examples.length > 0) {\n attrs.examples = field.examples;\n }\n\n // Add state attribute for skipped/aborted (markform-216)\n if (response?.state === 'skipped' || response?.state === 'aborted') {\n attrs.state = response.state;\n }\n\n const attrStr = serializeAttrs(attrs);\n let content = '';\n\n // Extract value from response if state is \"answered\"\n if (response?.state === 'answered' && response.value) {\n const value = response.value as UrlListValue;\n if (value.items && value.items.length > 0) {\n content = formatValueFence(value.items.join('\\n'));\n }\n }\n\n // Sentinel with reason for skipped/aborted overrides value content\n const sentinelContent = getSentinelContent(response);\n if (sentinelContent) {\n content = sentinelContent;\n }\n\n return `{% field ${attrStr} %}${content}{% /field %}`;\n}\n\n/**\n * Serialize a date-field.\n */\nfunction serializeDateField(field: DateField, response: FieldResponse | undefined): string {\n const attrs: Record<string, unknown> = { kind: 'date', id: field.id, label: field.label };\n if (field.required) {\n attrs.required = field.required;\n }\n if (field.priority !== DEFAULT_PRIORITY) {\n attrs.priority = field.priority;\n }\n if (field.role !== AGENT_ROLE) {\n attrs.role = field.role;\n }\n if (field.min !== undefined) {\n attrs.min = field.min;\n }\n if (field.max !== undefined) {\n attrs.max = field.max;\n }\n if (field.validate) {\n attrs.validate = field.validate;\n }\n if (field.report !== undefined) {\n attrs.report = field.report;\n }\n addParallelOrderAttrs(attrs, field);\n\n // Add state attribute for skipped/aborted\n if (response?.state === 'skipped' || response?.state === 'aborted') {\n attrs.state = response.state;\n }\n\n const attrStr = serializeAttrs(attrs);\n let content = '';\n\n // Extract value from response if state is \"answered\"\n if (response?.state === 'answered' && response.value) {\n const value = response.value as DateValue;\n if (value.value) {\n content = formatValueFence(value.value);\n }\n }\n\n // Sentinel with reason for skipped/aborted overrides value content\n const sentinelContent = getSentinelContent(response);\n if (sentinelContent) {\n content = sentinelContent;\n }\n\n return `{% field ${attrStr} %}${content}{% /field %}`;\n}\n\n/**\n * Serialize a year-field.\n */\nfunction serializeYearField(field: YearField, response: FieldResponse | undefined): string {\n const attrs: Record<string, unknown> = { kind: 'year', id: field.id, label: field.label };\n if (field.required) {\n attrs.required = field.required;\n }\n if (field.priority !== DEFAULT_PRIORITY) {\n attrs.priority = field.priority;\n }\n if (field.role !== AGENT_ROLE) {\n attrs.role = field.role;\n }\n if (field.min !== undefined) {\n attrs.min = field.min;\n }\n if (field.max !== undefined) {\n attrs.max = field.max;\n }\n if (field.validate) {\n attrs.validate = field.validate;\n }\n if (field.report !== undefined) {\n attrs.report = field.report;\n }\n addParallelOrderAttrs(attrs, field);\n\n // Add state attribute for skipped/aborted\n if (response?.state === 'skipped' || response?.state === 'aborted') {\n attrs.state = response.state;\n }\n\n const attrStr = serializeAttrs(attrs);\n let content = '';\n\n // Extract value from response if state is \"answered\"\n if (response?.state === 'answered' && response.value) {\n const value = response.value as YearValue;\n if (value.value !== null && value.value !== undefined) {\n content = formatValueFence(String(value.value));\n }\n }\n\n // Sentinel with reason for skipped/aborted overrides value content\n const sentinelContent = getSentinelContent(response);\n if (sentinelContent) {\n content = sentinelContent;\n }\n\n return `{% field ${attrStr} %}${content}{% /field %}`;\n}\n\n/**\n * Serialize a cell value for table output.\n * URL-typed columns are formatted as markdown links with domain as display text.\n */\nfunction serializeCellValue(cell: CellResponse, columnType: ColumnTypeName): string {\n if (cell.state === 'skipped') {\n return cell.reason ? `%SKIP:${cell.reason}%` : '%SKIP%';\n }\n if (cell.state === 'aborted') {\n return cell.reason ? `%ABORT:${cell.reason}%` : '%ABORT%';\n }\n if (cell.value === undefined || cell.value === null) {\n return '';\n }\n // Convert value to string based on type\n if (typeof cell.value === 'number') {\n return String(cell.value);\n }\n // Format URL columns as markdown links\n if (columnType === 'url') {\n return formatUrlAsMarkdownLink(cell.value);\n }\n return cell.value;\n}\n\n/**\n * Serialize a table row to markdown table row format.\n */\nfunction serializeTableRow(row: TableRowResponse, columns: TableColumn[]): string {\n const cells = columns.map((col) => {\n const cell = row[col.id] ?? { state: 'skipped' };\n return serializeCellValue(cell, col.type);\n });\n return `| ${cells.join(' | ')} |`;\n}\n\n/**\n * Serialize a table value to markdown table format.\n */\nfunction serializeMarkdownTable(value: TableValue, columns: TableColumn[]): string {\n if (columns.length === 0) {\n return '';\n }\n\n const lines: string[] = [];\n\n // Header row\n const headerCells = columns.map((col) => col.label);\n lines.push(`| ${headerCells.join(' | ')} |`);\n\n // Separator row\n const separatorCells = columns.map(() => '---');\n lines.push(`| ${separatorCells.join(' | ')} |`);\n\n // Data rows\n for (const row of value.rows) {\n lines.push(serializeTableRow(row, columns));\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Serialize a table-field.\n */\nfunction serializeTableField(field: TableField, response: FieldResponse | undefined): string {\n const attrs: Record<string, unknown> = { kind: 'table', id: field.id, label: field.label };\n if (field.required) {\n attrs.required = field.required;\n }\n if (field.priority !== DEFAULT_PRIORITY) {\n attrs.priority = field.priority;\n }\n if (field.role !== AGENT_ROLE) {\n attrs.role = field.role;\n }\n\n // Column attributes\n attrs.columnIds = field.columns.map((c) => c.id);\n attrs.columnLabels = field.columns.map((c) => c.label);\n attrs.columnTypes = field.columns.map((c) => {\n if (c.required) {\n return { type: c.type, required: true };\n }\n return c.type;\n });\n\n if (field.minRows !== undefined) {\n attrs.minRows = field.minRows;\n }\n if (field.maxRows !== undefined) {\n attrs.maxRows = field.maxRows;\n }\n if (field.validate) {\n attrs.validate = field.validate;\n }\n if (field.report !== undefined) {\n attrs.report = field.report;\n }\n addParallelOrderAttrs(attrs, field);\n\n // Add state attribute for skipped/aborted\n if (response?.state === 'skipped' || response?.state === 'aborted') {\n attrs.state = response.state;\n }\n\n const attrStr = serializeAttrs(attrs);\n let content = '';\n\n // Extract value from response if state is \"answered\"\n // Table values use markdown table syntax directly WITHOUT value fence (per spec)\n if (response?.state === 'answered' && response.value) {\n const value = response.value as TableValue;\n if ((value.rows?.length ?? 0) > 0) {\n const tableContent = serializeMarkdownTable(value, field.columns);\n content = '\\n' + tableContent + '\\n';\n }\n }\n\n // Sentinel with reason for skipped/aborted overrides value content\n const sentinelContent = getSentinelContent(response);\n if (sentinelContent) {\n content = sentinelContent;\n }\n\n return `{% field ${attrStr} %}${content}{% /field %}`;\n}\n\n/**\n * Serialize a field to Markdoc format.\n */\nfunction serializeField(field: Field, responses: Record<Id, FieldResponse>): string {\n const response = responses[field.id];\n\n switch (field.kind) {\n case 'string':\n return serializeStringField(field, response);\n case 'number':\n return serializeNumberField(field, response);\n case 'string_list':\n return serializeStringListField(field, response);\n case 'single_select':\n return serializeSingleSelectField(field, response);\n case 'multi_select':\n return serializeMultiSelectField(field, response);\n case 'checkboxes':\n return serializeCheckboxesField(field, response);\n case 'url':\n return serializeUrlField(field, response);\n case 'url_list':\n return serializeUrlListField(field, response);\n case 'date':\n return serializeDateField(field, response);\n case 'year':\n return serializeYearField(field, response);\n case 'table':\n return serializeTableField(field, response);\n default: {\n // Exhaustiveness check - TypeScript will error if a case is missing\n const _exhaustive: never = field;\n throw new Error(`Unhandled field kind: ${(_exhaustive as { kind: string }).kind}`);\n }\n }\n}\n\n// =============================================================================\n// Doc Block Serialization\n// =============================================================================\n\n/**\n * Serialize a documentation block.\n * Uses the semantic tag name (description, instructions, documentation).\n */\nfunction serializeDocBlock(doc: DocumentationBlock): string {\n const attrs: Record<string, unknown> = { ref: doc.ref };\n if (doc.report !== undefined) {\n attrs.report = doc.report;\n }\n const attrStr = serializeAttrs(attrs);\n return `{% ${doc.tag} ${attrStr} %}\\n${doc.bodyMarkdown}\\n{% /${doc.tag} %}`;\n}\n\n// =============================================================================\n// Note Serialization (markform-217)\n// =============================================================================\n\n/**\n * Serialize notes in sorted order.\n * Notes are sorted numerically by ID suffix (n1, n2, n10 not n1, n10, n2).\n */\nfunction serializeNotes(notes: Note[]): string {\n if (notes.length === 0) {\n return '';\n }\n\n // Sort numerically by ID suffix (n1, n2, n10 not n1, n10, n2)\n const sorted = [...notes].sort((a, b) => {\n const aNum = Number.parseInt(a.id.replace(/^n/, ''), 10) || 0;\n const bNum = Number.parseInt(b.id.replace(/^n/, ''), 10) || 0;\n return aNum - bNum;\n });\n\n const lines: string[] = [];\n for (const note of sorted) {\n const attrs: Record<string, unknown> = {\n id: note.id,\n ref: note.ref,\n role: note.role,\n };\n\n const attrStr = serializeAttrs(attrs);\n lines.push(`{% note ${attrStr} %}\\n${note.text}\\n{% /note %}`);\n }\n\n return lines.join('\\n\\n');\n}\n\n// =============================================================================\n// Group and Form Serialization\n// =============================================================================\n\n/**\n * Serialize a field group.\n * Implicit groups (fields placed directly under the form) are serialized\n * without the group wrapper tags.\n */\nfunction serializeFieldGroup(\n group: FieldGroup,\n responses: Record<Id, FieldResponse>,\n docs: DocumentationBlock[],\n): string {\n const lines: string[] = [];\n\n // Implicit groups don't have wrapper tags\n if (!group.implicit) {\n const attrs: Record<string, unknown> = { id: group.id };\n if (group.title) {\n attrs.title = group.title;\n }\n if (group.validate) {\n attrs.validate = group.validate;\n }\n if (group.report !== undefined) {\n attrs.report = group.report;\n }\n if (group.parallel !== undefined) {\n attrs.parallel = group.parallel;\n }\n if (group.order !== undefined) {\n attrs.order = group.order;\n }\n\n const attrStr = serializeAttrs(attrs);\n lines.push(`{% group ${attrStr} %}`);\n }\n\n // Group doc blocks by ref\n const docsByRef = new Map<string, DocumentationBlock[]>();\n for (const doc of docs) {\n const list = docsByRef.get(doc.ref) ?? [];\n list.push(doc);\n docsByRef.set(doc.ref, list);\n }\n\n for (const field of group.children) {\n lines.push('');\n lines.push(serializeField(field, responses));\n\n // Add any doc blocks for this field\n const fieldDocs = docsByRef.get(field.id);\n if (fieldDocs) {\n for (const doc of fieldDocs) {\n lines.push('');\n lines.push(serializeDocBlock(doc));\n }\n }\n }\n\n // Implicit groups don't have wrapper tags\n if (!group.implicit) {\n lines.push('');\n lines.push('{% /group %}');\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Serialize a form schema.\n */\nfunction serializeFormSchema(\n schema: FormSchema,\n responses: Record<Id, FieldResponse>,\n docs: DocumentationBlock[],\n notes: Note[],\n): string {\n const attrs: Record<string, unknown> = { id: schema.id };\n if (schema.title) {\n attrs.title = schema.title;\n }\n\n const attrStr = serializeAttrs(attrs);\n const lines: string[] = [`{% form ${attrStr} %}`];\n\n // Group doc blocks by ref\n const docsByRef = new Map<string, DocumentationBlock[]>();\n for (const doc of docs) {\n const list = docsByRef.get(doc.ref) ?? [];\n list.push(doc);\n docsByRef.set(doc.ref, list);\n }\n\n // Add form-level doc blocks\n const formDocs = docsByRef.get(schema.id);\n if (formDocs) {\n for (const doc of formDocs) {\n lines.push('');\n lines.push(serializeDocBlock(doc));\n }\n }\n\n for (const group of schema.groups) {\n lines.push('');\n lines.push(serializeFieldGroup(group, responses, docs));\n }\n\n // Add notes at end of form, before closing tag (markform-217)\n const notesContent = serializeNotes(notes);\n if (notesContent) {\n lines.push('');\n lines.push(notesContent);\n }\n\n lines.push('');\n lines.push('{% /form %}');\n\n return lines.join('\\n');\n}\n\n// =============================================================================\n// Main Serializer\n// =============================================================================\n\n/**\n * Build frontmatter YAML from form metadata.\n * Preserves title, description, roles, role_instructions, harness config, and run_mode.\n */\nfunction buildFrontmatter(metadata: FormMetadata | undefined, specVersion: string): string {\n // Build markform section\n const markformSection: Record<string, unknown> = {\n spec: specVersion,\n };\n\n // Add title if present\n if (metadata?.title) {\n markformSection.title = metadata.title;\n }\n\n // Add description if present\n if (metadata?.description) {\n markformSection.description = metadata.description;\n }\n\n if (metadata?.runMode) {\n markformSection.run_mode = metadata.runMode;\n }\n\n if (metadata?.harnessConfig && Object.keys(metadata.harnessConfig).length > 0) {\n markformSection.harness = transformHarnessConfigToYaml(metadata.harnessConfig);\n }\n\n // Add roles inside markform section if not default\n const defaultRoles = ['user', 'agent'];\n if (\n metadata?.roles &&\n (metadata.roles.length !== defaultRoles.length ||\n !metadata.roles.every((r, i) => r === defaultRoles[i]))\n ) {\n markformSection.roles = metadata.roles;\n }\n\n // Add role_instructions inside markform section if not default/empty\n const defaultInstructions = { user: '', agent: '' };\n if (metadata?.roleInstructions) {\n const hasCustomInstructions = Object.entries(metadata.roleInstructions).some(\n ([role, instruction]) => {\n const defaultVal = defaultInstructions[role as keyof typeof defaultInstructions] ?? '';\n return instruction !== defaultVal && instruction.trim() !== '';\n },\n );\n if (hasCustomInstructions) {\n markformSection.role_instructions = metadata.roleInstructions;\n }\n }\n\n // Build top-level frontmatter object\n const frontmatterObj: Record<string, unknown> = {\n markform: markformSection,\n };\n\n const yamlStr = YAML.stringify(frontmatterObj, YAML_STRINGIFY_OPTIONS);\n\n return `---\\n${yamlStr}---`;\n}\n\n/**\n * Serialize a ParsedForm to canonical Markdoc markdown format.\n *\n * Supports two modes:\n * 1. Content-preserving (default): When rawSource is available, preserves markdown\n * content outside Markform tags using splice-based serialization.\n * 2. Regeneration: Builds entire document from structured data.\n *\n * @param form - The parsed form to serialize\n * @param opts - Serialization options\n * @returns The canonical markdown string\n */\nexport function serializeForm(form: ParsedForm, opts?: SerializeOptions): string {\n const specVersion = opts?.specVersion ?? MF_SPEC_VERSION;\n const preserveContent = opts?.preserveContent ?? true;\n\n // Determine output syntax style:\n // 1. Use explicit option if provided\n // 2. Fall back to form's detected syntax style\n // 3. Default to 'tags'\n const syntaxStyle = opts?.syntaxStyle ?? form.syntaxStyle ?? 'tags';\n\n // Try content-preserving serialization if:\n // - preserveContent is enabled (default)\n // - rawSource is available\n // - tagRegions are available\n if (preserveContent && form.rawSource && form.tagRegions) {\n const preserved = serializeWithContentPreservation(form, specVersion, syntaxStyle);\n if (preserved !== null) {\n return preserved;\n }\n // Fall through to regeneration if preservation failed\n }\n\n // Regeneration mode: build from structured data\n return serializeFromScratch(form, specVersion, syntaxStyle);\n}\n\n/**\n * Check if position is at the start of a line.\n */\nfunction isAtLineStart(input: string, pos: number): boolean {\n if (pos === 0) return true;\n return input[pos - 1] === '\\n';\n}\n\n/**\n * Match a fenced code block opening at the given position.\n * Returns fence info if found, null otherwise.\n * Handles 0-3 leading spaces per CommonMark spec.\n */\nfunction matchFenceOpening(\n input: string,\n pos: number,\n): { char: string; length: number; endPos: number } | null {\n // Check for 0-3 leading spaces (4+ spaces = indented code block, not fence)\n let indent = 0;\n let i = pos;\n while (i < input.length && input[i] === ' ') {\n indent++;\n i++;\n }\n\n // 4+ spaces means this is not a fenced code block per CommonMark\n if (indent >= 4) {\n return null;\n }\n\n // Check for fence character (` or ~)\n const fenceChar = input[i];\n if (fenceChar !== '`' && fenceChar !== '~') {\n return null;\n }\n\n // Count consecutive fence characters (need at least 3)\n let fenceLength = 0;\n while (i + fenceLength < input.length && input[i + fenceLength] === fenceChar) {\n fenceLength++;\n }\n\n if (fenceLength < 3) {\n return null;\n }\n\n // Find end of line\n let endOfLine = i + fenceLength;\n while (endOfLine < input.length && input[endOfLine] !== '\\n') {\n endOfLine++;\n }\n if (endOfLine < input.length) {\n endOfLine++; // Include the newline\n }\n\n return { char: fenceChar, length: fenceLength, endPos: endOfLine };\n}\n\n/**\n * Check if position matches a closing fence for the given opening fence.\n */\nfunction matchFenceClosing(\n input: string,\n pos: number,\n fenceChar: string,\n fenceLength: number,\n): number {\n // Check for 0-3 leading spaces\n let indent = 0;\n let i = pos;\n while (indent < 4 && i < input.length && input[i] === ' ') {\n indent++;\n i++;\n }\n\n // Check for matching fence character\n if (input[i] !== fenceChar) {\n return -1;\n }\n\n // Count consecutive fence characters (need at least fenceLength)\n let closingLength = 0;\n while (i + closingLength < input.length && input[i + closingLength] === fenceChar) {\n closingLength++;\n }\n\n if (closingLength < fenceLength) {\n return -1;\n }\n\n // Rest of line must be whitespace only (or end of string)\n let afterFence = i + closingLength;\n while (afterFence < input.length && input[afterFence] !== '\\n') {\n if (input[afterFence] !== ' ' && input[afterFence] !== '\\t') {\n return -1;\n }\n afterFence++;\n }\n\n // Return position after the closing fence line\n if (afterFence < input.length) {\n afterFence++; // Include the newline\n }\n return afterFence;\n}\n\n/**\n * Find the form tag boundaries in rawSource, skipping code blocks.\n * Returns [startOffset, endOffset] where:\n * - startOffset: position where {% form (or <!-- form) starts\n * - endOffset: position just after {% /form %} (or <!-- /form -->)\n *\n * Code blocks (fenced with ``` or ~~~) are skipped to avoid matching\n * form tag examples in documentation or code samples.\n *\n * Returns null if form boundaries cannot be found.\n */\nfunction findFormBoundaries(rawSource: string): [number, number] | null {\n const openPattern = /(?:{%\\s*form\\s|<!--\\s*form\\s)/g;\n const closePattern = /{%\\s*\\/form\\s*%}|<!--\\s*\\/form\\s*-->/g;\n\n let startOffset: number | null = null;\n let endOffset: number | null = null;\n\n let i = 0;\n let inFencedCode = false;\n let fenceChar = '';\n let fenceLength = 0;\n\n while (i < rawSource.length) {\n // Check for fenced code block at line start\n if (!inFencedCode && isAtLineStart(rawSource, i)) {\n const fence = matchFenceOpening(rawSource, i);\n if (fence) {\n inFencedCode = true;\n fenceChar = fence.char;\n fenceLength = fence.length;\n i = fence.endPos;\n continue;\n }\n }\n\n // Check for closing fence\n if (inFencedCode && isAtLineStart(rawSource, i)) {\n const closePos = matchFenceClosing(rawSource, i, fenceChar, fenceLength);\n if (closePos !== -1) {\n inFencedCode = false;\n i = closePos;\n continue;\n }\n }\n\n // Skip if inside fenced code block\n if (inFencedCode) {\n // Move to next line\n while (i < rawSource.length && rawSource[i] !== '\\n') {\n i++;\n }\n if (i < rawSource.length) {\n i++; // Skip the newline\n }\n continue;\n }\n\n // Look for opening form tag (first one found)\n if (startOffset === null) {\n openPattern.lastIndex = i;\n const openMatch = openPattern.exec(rawSource);\n if (openMatch?.index === i) {\n startOffset = i;\n }\n }\n\n // Look for closing form tag (track the last one found)\n closePattern.lastIndex = i;\n const closeMatch = closePattern.exec(rawSource);\n if (closeMatch?.index === i) {\n endOffset = i + closeMatch[0].length;\n }\n\n i++;\n }\n\n if (startOffset === null || endOffset === null) {\n return null;\n }\n\n return [startOffset, endOffset];\n}\n\n/**\n * Serialize using content-preserving (splice-based) approach.\n *\n * Strategy:\n * 1. Preserve content BEFORE the form tag (intro markdown)\n * 2. Regenerate the form tag and its contents (canonical format)\n * 3. Preserve content AFTER the form tag (footer markdown)\n *\n * This approach:\n * - Preserves markdown content outside Markform tags (headings, paragraphs, etc.)\n * - Ensures Markform tags are in canonical format (normalization)\n * - Handles value changes correctly (regenerated content reflects current state)\n *\n * Returns null if preservation is not possible.\n */\nfunction serializeWithContentPreservation(\n form: ParsedForm,\n specVersion: string,\n syntaxStyle: SyntaxStyle,\n): string | null {\n if (!form.rawSource) {\n return null;\n }\n\n // Find actual form boundaries using regex (more reliable than AST locations)\n const boundaries = findFormBoundaries(form.rawSource);\n if (!boundaries) {\n return null;\n }\n\n const [formStart, formEnd] = boundaries;\n\n // Build fresh frontmatter (always regenerated with current metadata)\n const frontmatter = buildFrontmatter(form.metadata, specVersion);\n\n // Extract content before and after the form tag\n const contentBefore = form.rawSource.slice(0, formStart);\n const contentAfter = form.rawSource.slice(formEnd);\n\n // Serialize the form body in canonical format\n const formBody = serializeFormSchema(form.schema, form.responsesByFieldId, form.docs, form.notes);\n\n // Combine: frontmatter + preserved before + regenerated form + preserved after\n // Ensure there's always a blank line after frontmatter for consistency with scratch mode\n let result = frontmatter + '\\n';\n if (!contentBefore.startsWith('\\n')) {\n result += '\\n'; // Add blank line if original didn't have one\n }\n result += contentBefore + formBody + contentAfter;\n\n // Ensure trailing newline\n if (!result.endsWith('\\n')) {\n result += '\\n';\n }\n\n // Transform to HTML comment syntax if requested\n if (syntaxStyle === 'comments') {\n result = postprocessToCommentSyntax(result);\n }\n\n return result;\n}\n\n/**\n * Serialize form from scratch (regeneration mode).\n * Used when content preservation is disabled or not possible.\n */\nfunction serializeFromScratch(\n form: ParsedForm,\n specVersion: string,\n syntaxStyle: SyntaxStyle,\n): string {\n // Build frontmatter from metadata (preserves roles, instructions, harness config, run_mode)\n const frontmatter = buildFrontmatter(form.metadata, specVersion);\n\n // Serialize form body\n const body = serializeFormSchema(form.schema, form.responsesByFieldId, form.docs, form.notes);\n\n let result = `${frontmatter}\\n\\n${body}\\n`;\n\n // Transform to HTML comment syntax if requested\n if (syntaxStyle === 'comments') {\n result = postprocessToCommentSyntax(result);\n }\n\n return result;\n}\n\n// =============================================================================\n// Raw Markdown Serialization (human-readable, no markdoc)\n// =============================================================================\n\n/** Map checkbox state to GFM marker for raw markdown output */\nconst STATE_TO_GFM_MARKER: Record<CheckboxValue, string> = {\n // Multi mode\n todo: ' ',\n done: 'x',\n incomplete: '/',\n active: '*',\n na: '-',\n // Explicit mode\n unfilled: ' ',\n yes: 'x',\n no: ' ',\n};\n\n/**\n * Serialize a field value to raw markdown (human-readable).\n */\nfunction serializeFieldRaw(field: Field, responses: Record<Id, FieldResponse>): string {\n const response = responses[field.id];\n const lines: string[] = [];\n\n lines.push(`**${field.label}:**`);\n // Blank line after label to ensure new paragraph (and prevent Flowmark merging)\n lines.push('');\n\n // Extract value from response if state is \"answered\"\n const value = response?.state === 'answered' ? response.value : undefined;\n\n switch (field.kind) {\n case 'string': {\n const strValue = value as StringValue | undefined;\n if (strValue?.value) {\n lines.push(strValue.value);\n } else {\n lines.push('_(empty)_');\n }\n break;\n }\n case 'number': {\n const numValue = value as NumberValue | undefined;\n if (numValue?.value !== null && numValue?.value !== undefined) {\n lines.push(String(numValue.value));\n } else {\n lines.push('_(empty)_');\n }\n break;\n }\n case 'string_list': {\n const listValue = value as StringListValue | undefined;\n if (listValue?.items && listValue.items.length > 0) {\n for (const item of listValue.items) {\n lines.push(`- ${item}`);\n }\n } else {\n lines.push('_(empty)_');\n }\n break;\n }\n case 'single_select': {\n const selectValue = value as SingleSelectValue | undefined;\n const selected = field.options.find((opt) => opt.id === selectValue?.selected);\n if (selected) {\n lines.push(selected.label);\n } else {\n lines.push('_(none selected)_');\n }\n break;\n }\n case 'multi_select': {\n const multiValue = value as MultiSelectValue | undefined;\n const selectedSet = new Set(multiValue?.selected ?? []);\n // Show all options with checkbox state (consistent with checkboxes and View tab)\n for (const opt of field.options) {\n const marker = selectedSet.has(opt.id) ? 'x' : ' ';\n lines.push(`- [${marker}] ${opt.label}`);\n }\n break;\n }\n case 'checkboxes': {\n const cbValue = value as CheckboxesValue | undefined;\n for (const opt of field.options) {\n const state = cbValue?.values[opt.id] ?? 'todo';\n const marker = STATE_TO_GFM_MARKER[state] ?? ' ';\n lines.push(`- [${marker}] ${opt.label}`);\n }\n break;\n }\n case 'url': {\n const urlValue = value as UrlValue | undefined;\n if (urlValue?.value) {\n lines.push(formatUrlAsMarkdownLink(urlValue.value));\n } else {\n lines.push('_(empty)_');\n }\n break;\n }\n case 'url_list': {\n const urlListValue = value as UrlListValue | undefined;\n if (urlListValue?.items && urlListValue.items.length > 0) {\n for (const item of urlListValue.items) {\n lines.push(`- ${formatUrlAsMarkdownLink(item)}`);\n }\n } else {\n lines.push('_(empty)_');\n }\n break;\n }\n case 'date': {\n const dateValue = value as DateValue | undefined;\n if (dateValue?.value) {\n lines.push(dateValue.value);\n } else {\n lines.push('_(empty)_');\n }\n break;\n }\n case 'year': {\n const yearValue = value as YearValue | undefined;\n if (yearValue?.value !== null && yearValue?.value !== undefined) {\n lines.push(String(yearValue.value));\n } else {\n lines.push('_(empty)_');\n }\n break;\n }\n case 'table': {\n const tableValue = value as TableValue | undefined;\n const tableField = field;\n if (tableValue?.rows && tableValue.rows.length > 0) {\n lines.push(serializeMarkdownTable(tableValue, tableField.columns));\n } else {\n lines.push('_(empty)_');\n }\n break;\n }\n default: {\n // Exhaustiveness check - TypeScript will error if a case is missing\n const _exhaustive: never = field;\n throw new Error(`Unhandled field kind: ${(_exhaustive as { kind: string }).kind}`);\n }\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Serialize a ParsedForm to plain, human-readable markdown.\n *\n * This output does NOT contain markdoc directives and cannot be parsed back\n * into a form. It's intended for human consumption and export.\n *\n * @param form - The parsed form to serialize\n * @returns Plain markdown string\n */\nexport function serializeRawMarkdown(form: ParsedForm): string {\n const lines: string[] = [];\n\n // Group doc blocks by ref\n const docsByRef = new Map<string, DocumentationBlock[]>();\n for (const doc of form.docs) {\n const list = docsByRef.get(doc.ref) ?? [];\n list.push(doc);\n docsByRef.set(doc.ref, list);\n }\n\n // Add form title\n if (form.schema.title) {\n lines.push(`# ${form.schema.title}`);\n lines.push('');\n }\n\n // Add form-level docs\n const formDocs = docsByRef.get(form.schema.id);\n if (formDocs) {\n for (const doc of formDocs) {\n lines.push(doc.bodyMarkdown.trim());\n lines.push('');\n }\n }\n\n // Process each group\n for (const group of form.schema.groups) {\n // Add group title as H2\n if (group.title) {\n lines.push(`## ${group.title}`);\n lines.push('');\n }\n\n // Add group-level docs\n const groupDocs = docsByRef.get(group.id);\n if (groupDocs) {\n for (const doc of groupDocs) {\n lines.push(doc.bodyMarkdown.trim());\n lines.push('');\n }\n }\n\n // Process fields\n for (const field of group.children) {\n lines.push(serializeFieldRaw(field, form.responsesByFieldId));\n lines.push('');\n\n // Add field-level docs\n const fieldDocs = docsByRef.get(field.id);\n if (fieldDocs) {\n for (const doc of fieldDocs) {\n lines.push(doc.bodyMarkdown.trim());\n lines.push('');\n }\n }\n }\n }\n\n return lines.join('\\n').trim() + '\\n';\n}\n\n/**\n * Check if a documentation block should be included in reports.\n * Default: instructions are excluded, everything else is included.\n */\nfunction shouldIncludeDoc(doc: DocumentationBlock): boolean {\n if (doc.report !== undefined) {\n return doc.report;\n }\n // Default: instructions excluded, others included\n return doc.tag !== 'instructions';\n}\n\n/**\n * Serialize a form to filtered markdown for reports.\n *\n * Produces clean, readable markdown with filtered content based on `report` attribute:\n * - Fields with report=false are excluded\n * - Groups with report=false are excluded\n * - Documentation blocks with report=false are excluded\n * - Instructions blocks are excluded by default (unless report=true)\n *\n * @param form - The parsed form to serialize\n * @returns Filtered plain markdown string suitable for sharing\n */\nexport function serializeReport(form: ParsedForm): string {\n const lines: string[] = [];\n\n // Group doc blocks by ref, filtering out excluded docs\n const docsByRef = new Map<string, DocumentationBlock[]>();\n for (const doc of form.docs) {\n if (!shouldIncludeDoc(doc)) {\n continue;\n }\n const list = docsByRef.get(doc.ref) ?? [];\n list.push(doc);\n docsByRef.set(doc.ref, list);\n }\n\n // Add form title\n if (form.schema.title) {\n lines.push(`# ${form.schema.title}`);\n lines.push('');\n }\n\n // Add form-level docs (filtered)\n const formDocs = docsByRef.get(form.schema.id);\n if (formDocs) {\n for (const doc of formDocs) {\n lines.push(doc.bodyMarkdown.trim());\n lines.push('');\n }\n }\n\n // Process each group\n for (const group of form.schema.groups) {\n // Skip groups with report=false\n if (group.report === false) {\n continue;\n }\n\n // Filter fields with report=false\n const visibleFields = group.children.filter((field) => field.report !== false);\n if (visibleFields.length === 0 && !group.title) {\n continue;\n }\n\n // Add group title as H2\n if (group.title) {\n lines.push(`## ${group.title}`);\n lines.push('');\n }\n\n // Add group-level docs (filtered)\n const groupDocs = docsByRef.get(group.id);\n if (groupDocs) {\n for (const doc of groupDocs) {\n lines.push(doc.bodyMarkdown.trim());\n lines.push('');\n }\n }\n\n // Process visible fields\n for (const field of visibleFields) {\n lines.push(serializeFieldRaw(field, form.responsesByFieldId));\n lines.push('');\n\n // Add field-level docs (filtered)\n const fieldDocs = docsByRef.get(field.id);\n if (fieldDocs) {\n for (const doc of fieldDocs) {\n lines.push(doc.bodyMarkdown.trim());\n lines.push('');\n }\n }\n }\n }\n\n return lines.join('\\n').trim() + '\\n';\n}\n","/**\n * Summary computation for Markform documents.\n *\n * Computes structure and progress summaries from parsed forms.\n */\n\nimport type {\n CheckboxesField,\n CheckboxesValue,\n CheckboxProgressCounts,\n ColumnTypeName,\n DateValue,\n Field,\n FieldKind,\n FieldProgress,\n FieldResponse,\n FieldValue,\n FormSchema,\n Id,\n InspectIssue,\n MultiSelectValue,\n Note,\n NumberValue,\n ProgressCounts,\n ProgressState,\n ProgressSummary,\n QualifiedColumnRef,\n QualifiedOptionRef,\n AnswerState,\n SingleSelectValue,\n StringListValue,\n StringValue,\n StructureSummary,\n TableValue,\n UrlListValue,\n UrlValue,\n YearValue,\n} from './coreTypes.js';\n\n// =============================================================================\n// Structure Summary Computation\n// =============================================================================\n\n/**\n * Compute a structure summary from a form schema.\n *\n * @param schema - The form schema to summarize\n * @returns Structure summary with counts and indices\n */\nexport function computeStructureSummary(schema: FormSchema): StructureSummary {\n const fieldCountByKind: Record<FieldKind, number> = {\n string: 0,\n number: 0,\n string_list: 0,\n checkboxes: 0,\n single_select: 0,\n multi_select: 0,\n url: 0,\n url_list: 0,\n date: 0,\n year: 0,\n table: 0,\n };\n\n const groupsById: Record<Id, 'field_group'> = {};\n const fieldsById: Record<Id, FieldKind> = {};\n const optionsById: Record<QualifiedOptionRef, { parentFieldId: Id; parentFieldKind: FieldKind }> =\n {};\n const columnsById: Record<QualifiedColumnRef, { parentFieldId: Id; columnType: ColumnTypeName }> =\n {};\n\n let groupCount = 0;\n let fieldCount = 0;\n let optionCount = 0;\n let columnCount = 0;\n\n for (const group of schema.groups) {\n groupCount++;\n groupsById[group.id] = 'field_group';\n\n for (const field of group.children) {\n fieldCount++;\n fieldCountByKind[field.kind]++;\n fieldsById[field.id] = field.kind;\n\n // Count options for select/checkbox fields\n if ('options' in field) {\n for (const opt of field.options) {\n optionCount++;\n const qualifiedRef: QualifiedOptionRef = `${field.id}.${opt.id}`;\n optionsById[qualifiedRef] = {\n parentFieldId: field.id,\n parentFieldKind: field.kind,\n };\n }\n }\n\n // Count columns for table fields\n if (field.kind === 'table') {\n for (const column of field.columns) {\n columnCount++;\n const qualifiedRef: QualifiedColumnRef = `${field.id}.${column.id}`;\n columnsById[qualifiedRef] = {\n parentFieldId: field.id,\n columnType: column.type,\n };\n }\n }\n }\n }\n\n return {\n groupCount,\n fieldCount,\n optionCount,\n columnCount,\n fieldCountByKind,\n groupsById,\n fieldsById,\n optionsById,\n columnsById,\n };\n}\n\n// =============================================================================\n// Field Progress Computation\n// =============================================================================\n\n/**\n * Determine if a field value is \"submitted\" (has some user input).\n */\nfunction isFieldSubmitted(field: Field, value: FieldValue | undefined): boolean {\n if (!value) {\n return false;\n }\n\n switch (field.kind) {\n case 'string': {\n const v = value as StringValue;\n return v.value !== null && v.value.trim() !== '';\n }\n case 'number': {\n const v = value as NumberValue;\n return v.value !== null;\n }\n case 'string_list': {\n const v = value as StringListValue;\n return v.items.length > 0;\n }\n case 'single_select': {\n const v = value as SingleSelectValue;\n return v.selected !== null;\n }\n case 'multi_select': {\n const v = value as MultiSelectValue;\n return v.selected.length > 0;\n }\n case 'checkboxes': {\n const v = value as CheckboxesValue;\n const checkboxField = field;\n // For checkboxes, check if any option has been changed from default\n const mode = checkboxField.checkboxMode ?? 'multi';\n\n for (const opt of checkboxField.options) {\n const state = v.values[opt.id];\n if (mode === 'explicit') {\n // In explicit mode, \"unfilled\" is default, anything else is submitted\n if (state !== 'unfilled') {\n return true;\n }\n } else {\n // In multi/simple mode, \"todo\" is default, anything else is submitted\n if (state !== 'todo') {\n return true;\n }\n }\n }\n return false;\n }\n case 'url': {\n const v = value as UrlValue;\n return v.value !== null && v.value.trim() !== '';\n }\n case 'url_list': {\n const v = value as UrlListValue;\n return v.items.length > 0;\n }\n case 'date': {\n const v = value as DateValue;\n return v.value !== null && v.value.trim() !== '';\n }\n case 'year': {\n const v = value as YearValue;\n return v.value !== null;\n }\n case 'table': {\n const v = value as TableValue;\n return (v.rows?.length ?? 0) > 0;\n }\n default: {\n // Exhaustiveness check - TypeScript will error if a case is missing\n const _exhaustive: never = field;\n throw new Error(`Unhandled field kind: ${(_exhaustive as { kind: string }).kind}`);\n }\n }\n}\n\n/**\n * Compute checkbox progress counts for a checkboxes field.\n */\nfunction computeCheckboxProgress(\n field: CheckboxesField,\n value: CheckboxesValue | undefined,\n): CheckboxProgressCounts {\n const result: CheckboxProgressCounts = {\n total: field.options.length,\n todo: 0,\n done: 0,\n incomplete: 0,\n active: 0,\n na: 0,\n unfilled: 0,\n yes: 0,\n no: 0,\n };\n\n if (!value) {\n // All options in default state\n const defaultState = field.checkboxMode === 'explicit' ? 'unfilled' : 'todo';\n for (const _opt of field.options) {\n result[defaultState]++;\n }\n return result;\n }\n\n for (const opt of field.options) {\n const state = value.values[opt.id] ?? 'todo';\n result[state]++;\n }\n\n return result;\n}\n\n/**\n * Compute whether a field is empty (has no value).\n */\nfunction isFieldEmpty(field: Field, value: FieldValue | undefined): boolean {\n return !isFieldSubmitted(field, value);\n}\n\n/**\n * Compute progress for a single field.\n */\nfunction computeFieldProgress(\n field: Field,\n response: FieldResponse,\n notes: Note[],\n issues: InspectIssue[],\n): FieldProgress {\n const fieldIssues = issues.filter((i) => i.ref === field.id);\n const issueCount = fieldIssues.length;\n const value = response.value;\n // `empty` means \"field has no substantive value\" - this is about value presence,\n // not workflow state. A multi_select with selected=[] is empty even if answered.\n // The answerState dimension (unanswered/answered/skipped/aborted) tracks workflow.\n const empty = isFieldEmpty(field, value);\n\n // Determine validity:\n // - Skipped/aborted fields with any issues are invalid (they've been addressed but problematic)\n // - Empty unanswered fields with only \"required_missing\" issues are NOT invalid (just empty)\n // - Fields with value validation issues are invalid\n let valid = true;\n if (response.state === 'skipped' || response.state === 'aborted') {\n // Skipped/aborted fields with any issues are invalid\n valid = issueCount === 0;\n } else if (empty) {\n // Empty unanswered fields: only invalid if they have issues OTHER than required_missing\n const valueIssues = fieldIssues.filter((i) => i.reason !== 'required_missing');\n valid = valueIssues.length === 0;\n } else {\n // Filled fields: invalid if any issues\n valid = issueCount === 0;\n }\n\n // Compute note-related fields\n const fieldNotes = notes.filter((n) => n.ref === field.id);\n const hasNotes = fieldNotes.length > 0;\n const noteCount = fieldNotes.length;\n\n const progress: FieldProgress = {\n kind: field.kind,\n required: field.required,\n answerState: response.state,\n hasNotes,\n noteCount,\n empty,\n valid,\n issueCount,\n };\n\n // Add checkbox progress for checkboxes fields\n if (field.kind === 'checkboxes') {\n progress.checkboxProgress = computeCheckboxProgress(\n field,\n value as CheckboxesValue | undefined,\n );\n }\n\n return progress;\n}\n\n// =============================================================================\n// Progress Summary Computation\n// =============================================================================\n\n/**\n * Get the response state from a field response.\n * Helper function for progress computation.\n *\n * @param response - The field response (may be undefined)\n * @returns The response state\n */\n\nfunction _getAnswerState(response: FieldResponse | undefined): AnswerState {\n if (!response) {\n return 'unanswered';\n }\n return response.state;\n}\n\n/**\n * Compute a progress summary for a form.\n *\n * @param schema - The form schema\n * @param responsesByFieldId - Current field responses (state + optional value)\n * @param notes - Notes attached to fields/groups/form\n * @param issues - Validation issues (from inspect)\n * @returns Progress summary with field states and counts\n */\nexport function computeProgressSummary(\n schema: FormSchema,\n responsesByFieldId: Record<Id, FieldResponse>,\n notes: Note[],\n issues: InspectIssue[],\n): ProgressSummary {\n const fields: Record<Id, FieldProgress> = {};\n const counts: ProgressCounts = {\n totalFields: 0,\n requiredFields: 0,\n // Dimension 1: AnswerState\n unansweredFields: 0,\n answeredFields: 0,\n skippedFields: 0,\n abortedFields: 0,\n // Dimension 2: Validity\n validFields: 0,\n invalidFields: 0,\n // Dimension 3: Value presence\n emptyFields: 0,\n filledFields: 0,\n // Derived\n emptyRequiredFields: 0,\n totalNotes: notes.length,\n };\n\n for (const group of schema.groups) {\n for (const field of group.children) {\n const response = responsesByFieldId[field.id] ?? { state: 'unanswered' };\n const progress = computeFieldProgress(field, response, notes, issues);\n fields[field.id] = progress;\n\n // Update counts\n counts.totalFields++;\n if (progress.required) {\n counts.requiredFields++;\n }\n\n // Dimension 1: AnswerState (mutually exclusive)\n if (progress.answerState === 'answered') {\n counts.answeredFields++;\n } else if (progress.answerState === 'skipped') {\n counts.skippedFields++;\n } else if (progress.answerState === 'aborted') {\n counts.abortedFields++;\n } else if (progress.answerState === 'unanswered') {\n counts.unansweredFields++;\n }\n\n // Dimension 2: Validity (mutually exclusive)\n if (progress.valid) {\n counts.validFields++;\n } else {\n counts.invalidFields++;\n }\n\n // Dimension 3: Value presence (mutually exclusive)\n if (progress.empty) {\n counts.emptyFields++;\n if (progress.required) {\n counts.emptyRequiredFields++;\n }\n } else {\n counts.filledFields++;\n }\n }\n }\n\n return { counts, fields };\n}\n\n// =============================================================================\n// Form State Computation\n// =============================================================================\n\n/**\n * Compute the overall form state from progress summary.\n *\n * @param progress - The progress summary\n * @returns The overall form state\n */\nexport function computeFormState(progress: ProgressSummary): ProgressState {\n // Aborted fields = invalid state\n if (progress.counts.abortedFields > 0) {\n return 'invalid';\n }\n\n // If any field is invalid, form is invalid\n if (progress.counts.invalidFields > 0) {\n return 'invalid';\n }\n\n // If all required fields are filled and valid\n if (progress.counts.emptyRequiredFields === 0) {\n return 'complete';\n }\n\n // If any field is answered but not all required fields filled\n if (progress.counts.answeredFields > 0) {\n return 'incomplete';\n }\n\n return 'empty';\n}\n\n/**\n * Determine if the form is complete (ready for submission).\n *\n * A form is complete when:\n * 1. No aborted fields (aborted fields block completion)\n * 2. No required fields are empty\n * 3. No fields have validation errors\n * 4. No fields are in incomplete state (e.g., partial checkbox completion)\n * 5. All fields must be addressed (answered + skipped == total)\n *\n * Every field must be explicitly addressed - either filled with a value or\n * skipped with a reason. This ensures agents fully process all fields.\n *\n * @param progress - The progress summary\n * @returns True if the form is complete\n */\nexport function isFormComplete(progress: ProgressSummary): boolean {\n const { counts } = progress;\n\n // Aborted fields block completion\n if (counts.abortedFields > 0) {\n return false;\n }\n\n // Basic requirements: no invalid fields, all required fields filled\n const baseComplete = counts.invalidFields === 0 && counts.emptyRequiredFields === 0;\n\n // All fields must be addressed (filled or skipped)\n const allFieldsAccountedFor = counts.answeredFields + counts.skippedFields === counts.totalFields;\n\n return baseComplete && allFieldsAccountedFor;\n}\n\n// =============================================================================\n// Helper to compute all summaries at once\n// =============================================================================\n\nexport interface ComputedSummaries {\n structureSummary: StructureSummary;\n progressSummary: ProgressSummary;\n formState: ProgressState;\n isComplete: boolean;\n}\n\n/**\n * Compute all summaries for a parsed form.\n *\n * @param schema - The form schema\n * @param responsesByFieldId - Current field responses (state + optional value)\n * @param notes - Notes attached to fields/groups/form\n * @param issues - Validation issues\n * @returns All computed summaries\n */\nexport function computeAllSummaries(\n schema: FormSchema,\n responsesByFieldId: Record<Id, FieldResponse>,\n notes: Note[],\n issues: InspectIssue[],\n): ComputedSummaries {\n const structureSummary = computeStructureSummary(schema);\n const progressSummary = computeProgressSummary(schema, responsesByFieldId, notes, issues);\n const formState = computeFormState(progressSummary);\n const isComplete = isFormComplete(progressSummary);\n\n return {\n structureSummary,\n progressSummary,\n formState,\n isComplete,\n };\n}\n","/**\n * Validation engine for Markform documents.\n *\n * Runs built-in validators and optional code validators.\n */\n\nimport type {\n CellResponse,\n CheckboxesField,\n CheckboxesValue,\n DateField,\n DateValue,\n Field,\n FieldGroup,\n FieldResponse,\n FieldValue,\n FormSchema,\n Id,\n MultiSelectField,\n MultiSelectValue,\n NumberField,\n NumberValue,\n ParsedForm,\n SingleSelectField,\n SingleSelectValue,\n StringField,\n StringListField,\n StringListValue,\n StringValue,\n TableColumn,\n TableField,\n TableRowResponse,\n TableValue,\n UrlField,\n UrlListField,\n UrlListValue,\n UrlValue,\n ValidationIssue,\n ValidatorContext,\n ValidatorRef,\n ValidatorRegistry,\n YearField,\n YearValue,\n} from './coreTypes.js';\n\n// =============================================================================\n// Validation Options and Results\n// =============================================================================\n\nexport interface ValidateOptions {\n /** Skip code validators (only run built-in validation). */\n skipCodeValidators?: boolean;\n /** Custom validator registry (for testing or overrides). */\n validatorRegistry?: ValidatorRegistry;\n}\n\nexport interface ValidateResult {\n /** All validation issues found. */\n issues: ValidationIssue[];\n /** Whether the form is valid (no required-severity issues). */\n isValid: boolean;\n}\n\n// =============================================================================\n// Built-in Validators\n// =============================================================================\n\n/**\n * Validate a string field.\n */\nfunction validateStringField(\n field: StringField,\n value: StringValue | undefined,\n): ValidationIssue[] {\n const issues: ValidationIssue[] = [];\n const strValue = value?.value ?? null;\n\n // Required check\n if (field.required && (strValue === null || strValue.trim() === '')) {\n issues.push({\n severity: 'error',\n message: `Required field \"${field.label}\" is empty`,\n ref: field.id,\n source: 'builtin',\n });\n return issues; // Skip other checks if required and empty\n }\n\n // Skip other checks if no value\n if (strValue === null || strValue === '') {\n return issues;\n }\n\n // Min length\n if (field.minLength !== undefined && strValue.length < field.minLength) {\n issues.push({\n severity: 'error',\n message: `\"${field.label}\" must be at least ${field.minLength} characters (got ${strValue.length})`,\n ref: field.id,\n source: 'builtin',\n });\n }\n\n // Max length\n if (field.maxLength !== undefined && strValue.length > field.maxLength) {\n issues.push({\n severity: 'error',\n message: `\"${field.label}\" must be at most ${field.maxLength} characters (got ${strValue.length})`,\n ref: field.id,\n source: 'builtin',\n });\n }\n\n // Pattern\n if (field.pattern) {\n try {\n const regex = new RegExp(field.pattern);\n if (!regex.test(strValue)) {\n issues.push({\n severity: 'error',\n message: `\"${field.label}\" does not match required pattern`,\n ref: field.id,\n source: 'builtin',\n });\n }\n } catch {\n issues.push({\n severity: 'error',\n message: `Invalid pattern \"${field.pattern}\" for field \"${field.label}\"`,\n ref: field.id,\n source: 'builtin',\n });\n }\n }\n\n return issues;\n}\n\n/**\n * Validate a number field.\n */\nfunction validateNumberField(\n field: NumberField,\n value: NumberValue | undefined,\n): ValidationIssue[] {\n const issues: ValidationIssue[] = [];\n const numValue = value?.value ?? null;\n\n // Required check\n if (field.required && numValue === null) {\n issues.push({\n severity: 'error',\n message: `Required field \"${field.label}\" is empty`,\n ref: field.id,\n source: 'builtin',\n });\n return issues;\n }\n\n // Skip other checks if no value\n if (numValue === null) {\n return issues;\n }\n\n // Integer check\n if (field.integer && !Number.isInteger(numValue)) {\n issues.push({\n severity: 'error',\n message: `\"${field.label}\" must be an integer`,\n ref: field.id,\n source: 'builtin',\n });\n }\n\n // Min\n if (field.min !== undefined && numValue < field.min) {\n issues.push({\n severity: 'error',\n message: `\"${field.label}\" must be at least ${field.min} (got ${numValue})`,\n ref: field.id,\n source: 'builtin',\n });\n }\n\n // Max\n if (field.max !== undefined && numValue > field.max) {\n issues.push({\n severity: 'error',\n message: `\"${field.label}\" must be at most ${field.max} (got ${numValue})`,\n ref: field.id,\n source: 'builtin',\n });\n }\n\n return issues;\n}\n\n/**\n * Validate a string-list field.\n */\nfunction validateStringListField(\n field: StringListField,\n value: StringListValue | undefined,\n): ValidationIssue[] {\n const issues: ValidationIssue[] = [];\n const items = value?.items ?? [];\n\n // Required check\n if (field.required && items.length === 0) {\n issues.push({\n severity: 'error',\n message: `Required field \"${field.label}\" is empty`,\n ref: field.id,\n source: 'builtin',\n });\n return issues;\n }\n\n // Skip other checks if no items\n if (items.length === 0) {\n return issues;\n }\n\n // Min items\n if (field.minItems !== undefined && items.length < field.minItems) {\n issues.push({\n severity: 'error',\n message: `\"${field.label}\" must have at least ${field.minItems} items (got ${items.length})`,\n ref: field.id,\n source: 'builtin',\n });\n }\n\n // Max items\n if (field.maxItems !== undefined && items.length > field.maxItems) {\n issues.push({\n severity: 'error',\n message: `\"${field.label}\" must have at most ${field.maxItems} items (got ${items.length})`,\n ref: field.id,\n source: 'builtin',\n });\n }\n\n // Item constraints\n for (let i = 0; i < items.length; i++) {\n const item = items[i];\n if (item === undefined) {\n continue;\n }\n\n // Item min length\n if (field.itemMinLength !== undefined && item.length < field.itemMinLength) {\n issues.push({\n severity: 'error',\n message: `Item ${i + 1} in \"${field.label}\" must be at least ${field.itemMinLength} characters`,\n ref: field.id,\n source: 'builtin',\n });\n }\n\n // Item max length\n if (field.itemMaxLength !== undefined && item.length > field.itemMaxLength) {\n issues.push({\n severity: 'error',\n message: `Item ${i + 1} in \"${field.label}\" must be at most ${field.itemMaxLength} characters`,\n ref: field.id,\n source: 'builtin',\n });\n }\n }\n\n // Unique items\n if (field.uniqueItems) {\n const seen = new Set<string>();\n for (const item of items) {\n if (seen.has(item)) {\n issues.push({\n severity: 'error',\n message: `Duplicate item \"${item}\" in \"${field.label}\"`,\n ref: field.id,\n source: 'builtin',\n });\n break; // Only report once\n }\n seen.add(item);\n }\n }\n\n return issues;\n}\n\n/**\n * Validate a single-select field.\n */\nfunction validateSingleSelectField(\n field: SingleSelectField,\n value: SingleSelectValue | undefined,\n): ValidationIssue[] {\n const issues: ValidationIssue[] = [];\n const selected = value?.selected ?? null;\n\n // Required check\n if (field.required && selected === null) {\n issues.push({\n severity: 'error',\n message: `Required field \"${field.label}\" has no selection`,\n ref: field.id,\n source: 'builtin',\n });\n return issues;\n }\n\n // Validate selected option exists\n if (selected !== null) {\n const validOptions = new Set(field.options.map((o) => o.id));\n if (!validOptions.has(selected)) {\n issues.push({\n severity: 'error',\n message: `Invalid selection \"${selected}\" in \"${field.label}\"`,\n ref: field.id,\n source: 'builtin',\n });\n }\n }\n\n return issues;\n}\n\n/**\n * Validate a multi-select field.\n */\nfunction validateMultiSelectField(\n field: MultiSelectField,\n value: MultiSelectValue | undefined,\n): ValidationIssue[] {\n const issues: ValidationIssue[] = [];\n const selected = value?.selected ?? [];\n\n // Required check\n if (field.required && selected.length === 0) {\n issues.push({\n severity: 'error',\n message: `Required field \"${field.label}\" has no selections`,\n ref: field.id,\n source: 'builtin',\n });\n return issues;\n }\n\n // Skip other checks if no selections\n if (selected.length === 0) {\n return issues;\n }\n\n // Min selections\n if (field.minSelections !== undefined && selected.length < field.minSelections) {\n issues.push({\n severity: 'error',\n message: `\"${field.label}\" must have at least ${field.minSelections} selections (got ${selected.length})`,\n ref: field.id,\n source: 'builtin',\n });\n }\n\n // Max selections\n if (field.maxSelections !== undefined && selected.length > field.maxSelections) {\n issues.push({\n severity: 'error',\n message: `\"${field.label}\" must have at most ${field.maxSelections} selections (got ${selected.length})`,\n ref: field.id,\n source: 'builtin',\n });\n }\n\n // Validate selected options exist\n const validOptions = new Set(field.options.map((o) => o.id));\n for (const sel of selected) {\n if (!validOptions.has(sel)) {\n issues.push({\n severity: 'error',\n message: `Invalid selection \"${sel}\" in \"${field.label}\"`,\n ref: field.id,\n source: 'builtin',\n });\n }\n }\n\n return issues;\n}\n\n/**\n * Validate a checkboxes field.\n */\nfunction validateCheckboxesField(\n field: CheckboxesField,\n value: CheckboxesValue | undefined,\n): ValidationIssue[] {\n const issues: ValidationIssue[] = [];\n const values = value?.values ?? {};\n const mode = field.checkboxMode ?? 'multi';\n\n // Count completed items based on mode\n let doneCount = 0;\n let incompleteCount = 0;\n let unfilledCount = 0;\n\n for (const opt of field.options) {\n const state = values[opt.id] ?? (mode === 'explicit' ? 'unfilled' : 'todo');\n\n if (mode === 'explicit') {\n if (state === 'unfilled') {\n unfilledCount++;\n } else {\n doneCount++; // yes or no counts as answered\n }\n } else if (mode === 'multi') {\n if (state === 'done' || state === 'na') {\n doneCount++;\n } else if (state === 'incomplete' || state === 'active') {\n incompleteCount++;\n }\n } else {\n // simple mode\n if (state === 'done') {\n doneCount++;\n }\n }\n }\n\n // Required check - for checkboxes, required means all must be addressed\n if (field.required) {\n if (mode === 'explicit' && unfilledCount > 0) {\n issues.push({\n severity: 'error',\n message: `All items in \"${field.label}\" must be answered (${unfilledCount} unfilled)`,\n ref: field.id,\n source: 'builtin',\n });\n } else if (mode === 'multi' && (incompleteCount > 0 || doneCount === 0)) {\n issues.push({\n severity: 'error',\n message: `All items in \"${field.label}\" must be completed`,\n ref: field.id,\n source: 'builtin',\n });\n } else if (mode === 'simple' && doneCount < field.options.length) {\n const remaining = field.options.length - doneCount;\n issues.push({\n severity: 'error',\n message: `All items in \"${field.label}\" must be checked (${remaining} unchecked)`,\n ref: field.id,\n source: 'builtin',\n });\n }\n }\n\n // Min done (optional constraint)\n if (field.minDone !== undefined && doneCount < field.minDone) {\n issues.push({\n severity: 'error',\n message: `\"${field.label}\" requires at least ${field.minDone} items done (got ${doneCount})`,\n ref: field.id,\n source: 'builtin',\n });\n }\n\n return issues;\n}\n\n/**\n * Check if a string is a valid URL.\n * Uses URL constructor for validation (RFC 3986 compliant).\n */\nfunction isValidUrl(str: string): boolean {\n try {\n const url = new URL(str);\n // Only allow http and https protocols\n return url.protocol === 'http:' || url.protocol === 'https:';\n } catch {\n return false;\n }\n}\n\n/**\n * Validate a URL field.\n */\nfunction validateUrlField(field: UrlField, value: UrlValue | undefined): ValidationIssue[] {\n const issues: ValidationIssue[] = [];\n const urlValue = value?.value ?? null;\n\n // Required check\n if (field.required && (urlValue === null || urlValue.trim() === '')) {\n issues.push({\n severity: 'error',\n message: `Required field \"${field.label}\" is empty`,\n ref: field.id,\n source: 'builtin',\n });\n return issues;\n }\n\n // Skip other checks if no value\n if (urlValue === null || urlValue === '') {\n return issues;\n }\n\n // URL format validation\n if (!isValidUrl(urlValue)) {\n issues.push({\n severity: 'error',\n message: `\"${field.label}\" is not a valid URL`,\n ref: field.id,\n source: 'builtin',\n });\n }\n\n return issues;\n}\n\n/**\n * Validate a URL list field.\n */\nfunction validateUrlListField(\n field: UrlListField,\n value: UrlListValue | undefined,\n): ValidationIssue[] {\n const issues: ValidationIssue[] = [];\n const items = value?.items ?? [];\n\n // Required check\n if (field.required && items.length === 0) {\n issues.push({\n severity: 'error',\n message: `Required field \"${field.label}\" is empty`,\n ref: field.id,\n source: 'builtin',\n });\n return issues;\n }\n\n // Skip other checks if no items\n if (items.length === 0) {\n return issues;\n }\n\n // Min items\n if (field.minItems !== undefined && items.length < field.minItems) {\n issues.push({\n severity: 'error',\n message: `\"${field.label}\" must have at least ${field.minItems} items (got ${items.length})`,\n ref: field.id,\n source: 'builtin',\n });\n }\n\n // Max items\n if (field.maxItems !== undefined && items.length > field.maxItems) {\n issues.push({\n severity: 'error',\n message: `\"${field.label}\" must have at most ${field.maxItems} items (got ${items.length})`,\n ref: field.id,\n source: 'builtin',\n });\n }\n\n // URL format validation for each item\n for (let i = 0; i < items.length; i++) {\n const item = items[i];\n if (item !== undefined && !isValidUrl(item)) {\n issues.push({\n severity: 'error',\n message: `Item ${i + 1} in \"${field.label}\" is not a valid URL`,\n ref: field.id,\n source: 'builtin',\n });\n }\n }\n\n // Unique items\n if (field.uniqueItems) {\n const seen = new Set<string>();\n for (const item of items) {\n if (seen.has(item)) {\n issues.push({\n severity: 'error',\n message: `Duplicate URL \"${item}\" in \"${field.label}\"`,\n ref: field.id,\n source: 'builtin',\n });\n break; // Only report once\n }\n seen.add(item);\n }\n }\n\n return issues;\n}\n\n/**\n * Check if a string is a valid ISO 8601 date (YYYY-MM-DD).\n */\nfunction isValidDate(str: string): boolean {\n const pattern = /^\\d{4}-\\d{2}-\\d{2}$/;\n if (!pattern.test(str)) {\n return false;\n }\n const date = new Date(str);\n if (Number.isNaN(date.getTime())) {\n return false;\n }\n // Ensure the date components match (guards against invalid dates like 2024-02-30)\n const [year, month, day] = str.split('-').map(Number);\n return (\n date.getUTCFullYear() === year &&\n date.getUTCMonth() === (month ?? 0) - 1 &&\n date.getUTCDate() === day\n );\n}\n\n/**\n * Parse a date string to compare for min/max validation.\n * Returns date value or null if invalid.\n */\nfunction parseDateForComparison(str: string): number | null {\n if (!isValidDate(str)) {\n return null;\n }\n return new Date(str).getTime();\n}\n\n/**\n * Validate a date field.\n */\nfunction validateDateField(field: DateField, value: DateValue | undefined): ValidationIssue[] {\n const issues: ValidationIssue[] = [];\n const dateValue = value?.value ?? null;\n\n // Required check\n if (field.required && (dateValue === null || dateValue.trim() === '')) {\n issues.push({\n severity: 'error',\n message: `Required field \"${field.label}\" is empty`,\n ref: field.id,\n source: 'builtin',\n });\n return issues;\n }\n\n // Skip other checks if no value\n if (dateValue === null || dateValue === '') {\n return issues;\n }\n\n // Date format validation\n if (!isValidDate(dateValue)) {\n issues.push({\n severity: 'error',\n message: `\"${field.label}\" is not a valid date (expected YYYY-MM-DD)`,\n ref: field.id,\n source: 'builtin',\n });\n return issues; // Skip range checks if format is invalid\n }\n\n const dateTime = parseDateForComparison(dateValue);\n\n // Min date\n if (field.min !== undefined) {\n const minTime = parseDateForComparison(field.min);\n if (minTime !== null && dateTime !== null && dateTime < minTime) {\n issues.push({\n severity: 'error',\n message: `\"${field.label}\" must be on or after ${field.min} (got ${dateValue})`,\n ref: field.id,\n source: 'builtin',\n });\n }\n }\n\n // Max date\n if (field.max !== undefined) {\n const maxTime = parseDateForComparison(field.max);\n if (maxTime !== null && dateTime !== null && dateTime > maxTime) {\n issues.push({\n severity: 'error',\n message: `\"${field.label}\" must be on or before ${field.max} (got ${dateValue})`,\n ref: field.id,\n source: 'builtin',\n });\n }\n }\n\n return issues;\n}\n\n/**\n * Validate a year field.\n */\nfunction validateYearField(field: YearField, value: YearValue | undefined): ValidationIssue[] {\n const issues: ValidationIssue[] = [];\n const yearValue = value?.value ?? null;\n\n // Required check\n if (field.required && yearValue === null) {\n issues.push({\n severity: 'error',\n message: `Required field \"${field.label}\" is empty`,\n ref: field.id,\n source: 'builtin',\n });\n return issues;\n }\n\n // Skip other checks if no value\n if (yearValue === null) {\n return issues;\n }\n\n // Min year\n if (field.min !== undefined && yearValue < field.min) {\n issues.push({\n severity: 'error',\n message: `\"${field.label}\" must be at least ${field.min} (got ${yearValue})`,\n ref: field.id,\n source: 'builtin',\n });\n }\n\n // Max year\n if (field.max !== undefined && yearValue > field.max) {\n issues.push({\n severity: 'error',\n message: `\"${field.label}\" must be at most ${field.max} (got ${yearValue})`,\n ref: field.id,\n source: 'builtin',\n });\n }\n\n return issues;\n}\n\n/**\n * Validate a cell value according to its column type.\n */\nfunction validateCellValue(\n cell: CellResponse,\n column: TableColumn,\n fieldId: string,\n rowIndex: number,\n): ValidationIssue[] {\n const issues: ValidationIssue[] = [];\n const cellRef = `${fieldId}[${rowIndex}].${column.id}`;\n\n // Skip validation for skipped/aborted cells\n if (cell.state === 'skipped' || cell.state === 'aborted') {\n // But check if cell is required\n if (column.required) {\n issues.push({\n severity: 'error',\n message: `Required cell \"${column.label}\" in row ${rowIndex + 1} is ${cell.state}`,\n ref: cellRef,\n source: 'builtin',\n });\n }\n return issues;\n }\n\n // Check for empty required cells\n if (column.required && (cell.value === undefined || cell.value === null || cell.value === '')) {\n issues.push({\n severity: 'error',\n message: `Required cell \"${column.label}\" in row ${rowIndex + 1} is empty`,\n ref: cellRef,\n source: 'builtin',\n });\n return issues;\n }\n\n // Skip type validation if no value\n if (cell.value === undefined || cell.value === null || cell.value === '') {\n return issues;\n }\n\n // Type-specific validation\n switch (column.type) {\n case 'number':\n if (typeof cell.value !== 'number') {\n issues.push({\n severity: 'error',\n message: `Cell \"${column.label}\" in row ${rowIndex + 1} must be a number (got \"${cell.value}\")`,\n ref: cellRef,\n source: 'builtin',\n });\n }\n break;\n\n case 'year':\n if (typeof cell.value !== 'number' || !Number.isInteger(cell.value)) {\n issues.push({\n severity: 'error',\n message: `Cell \"${column.label}\" in row ${rowIndex + 1} must be an integer year (got \"${cell.value}\")`,\n ref: cellRef,\n source: 'builtin',\n });\n }\n break;\n\n case 'url':\n if (typeof cell.value === 'string') {\n try {\n new URL(cell.value);\n } catch {\n issues.push({\n severity: 'error',\n message: `Cell \"${column.label}\" in row ${rowIndex + 1} must be a valid URL (got \"${cell.value}\")`,\n ref: cellRef,\n source: 'builtin',\n });\n }\n }\n break;\n\n case 'date':\n if (typeof cell.value === 'string') {\n // Basic ISO date validation (YYYY-MM-DD)\n const datePattern = /^\\d{4}-\\d{2}-\\d{2}$/;\n if (!datePattern.test(cell.value)) {\n issues.push({\n severity: 'error',\n message: `Cell \"${column.label}\" in row ${rowIndex + 1} must be a valid date in YYYY-MM-DD format (got \"${cell.value}\")`,\n ref: cellRef,\n source: 'builtin',\n });\n }\n }\n break;\n\n case 'string':\n // String type accepts any value\n break;\n }\n\n return issues;\n}\n\n/**\n * Validate a table row.\n */\nfunction validateTableRow(\n row: TableRowResponse,\n columns: TableColumn[],\n fieldId: string,\n rowIndex: number,\n): ValidationIssue[] {\n const issues: ValidationIssue[] = [];\n\n for (const column of columns) {\n const cell = row[column.id] ?? { state: 'skipped' };\n issues.push(...validateCellValue(cell, column, fieldId, rowIndex));\n }\n\n return issues;\n}\n\n/**\n * Validate a table field.\n */\nfunction validateTableField(field: TableField, value: TableValue | undefined): ValidationIssue[] {\n const issues: ValidationIssue[] = [];\n\n // Check required (guard against malformed values without rows)\n const rows = value?.rows ?? [];\n const isEmpty = !value || rows.length === 0;\n if (field.required && isEmpty) {\n issues.push({\n severity: 'error',\n message: `Required field \"${field.label}\" is empty`,\n ref: field.id,\n source: 'builtin',\n });\n return issues;\n }\n\n // Skip other checks if no value\n if (isEmpty) {\n return issues;\n }\n\n // Check minRows\n if (field.minRows !== undefined && rows.length < field.minRows) {\n issues.push({\n severity: 'error',\n message: `\"${field.label}\" must have at least ${field.minRows} row(s) (has ${rows.length})`,\n ref: field.id,\n source: 'builtin',\n });\n }\n\n // Check maxRows\n if (field.maxRows !== undefined && rows.length > field.maxRows) {\n issues.push({\n severity: 'error',\n message: `\"${field.label}\" must have at most ${field.maxRows} row(s) (has ${rows.length})`,\n ref: field.id,\n source: 'builtin',\n });\n }\n\n // Validate each row\n for (let i = 0; i < rows.length; i++) {\n issues.push(...validateTableRow(rows[i]!, field.columns, field.id, i));\n }\n\n return issues;\n}\n\n/**\n * Validate a single field.\n */\nfunction validateField(field: Field, responses: Record<Id, FieldResponse>): ValidationIssue[] {\n const response = responses[field.id];\n const value = response?.state === 'answered' ? response.value : undefined;\n\n switch (field.kind) {\n case 'string':\n return validateStringField(field, value as StringValue | undefined);\n case 'number':\n return validateNumberField(field, value as NumberValue | undefined);\n case 'string_list':\n return validateStringListField(field, value as StringListValue | undefined);\n case 'single_select':\n return validateSingleSelectField(field, value as SingleSelectValue | undefined);\n case 'multi_select':\n return validateMultiSelectField(field, value as MultiSelectValue | undefined);\n case 'checkboxes':\n return validateCheckboxesField(field, value as CheckboxesValue | undefined);\n case 'url':\n return validateUrlField(field, value as UrlValue | undefined);\n case 'url_list':\n return validateUrlListField(field, value as UrlListValue | undefined);\n case 'date':\n return validateDateField(field, value as DateValue | undefined);\n case 'year':\n return validateYearField(field, value as YearValue | undefined);\n case 'table':\n return validateTableField(field, value as TableValue | undefined);\n default: {\n // Exhaustiveness check - TypeScript will error if a case is missing\n const _exhaustive: never = field;\n throw new Error(`Unhandled field kind: ${(_exhaustive as { kind: string }).kind}`);\n }\n }\n}\n\n// =============================================================================\n// Code Validator Support\n// =============================================================================\n\n/**\n * Parse a validator reference to extract id and params.\n */\nfunction parseValidatorRef(ref: ValidatorRef): {\n id: string;\n params: Record<string, unknown>;\n} {\n if (typeof ref === 'string') {\n return { id: ref, params: {} };\n }\n const { id, ...params } = ref;\n return { id, params };\n}\n\n/**\n * Run code validators for a field.\n */\nfunction runCodeValidators(\n field: Field,\n schema: FormSchema,\n responses: Record<Id, FieldResponse>,\n registry: ValidatorRegistry,\n): ValidationIssue[] {\n if (!field.validate) {\n return [];\n }\n\n const refs = Array.isArray(field.validate) ? field.validate : [field.validate];\n const issues: ValidationIssue[] = [];\n\n // Convert responses to values for validator context\n const values: Record<Id, FieldValue> = {};\n for (const [id, response] of Object.entries(responses)) {\n if (response.state === 'answered' && response.value !== undefined) {\n values[id] = response.value;\n }\n }\n\n for (const ref of refs) {\n const { id, params } = parseValidatorRef(ref);\n const validator = registry[id];\n\n if (!validator) {\n issues.push({\n severity: 'warning',\n message: `Validator \"${id}\" not found for field \"${field.label}\"`,\n ref: field.id,\n source: 'code',\n validatorId: id,\n });\n continue;\n }\n\n const ctx: ValidatorContext = {\n schema,\n values,\n targetId: field.id,\n targetSchema: field,\n params,\n };\n\n try {\n const validatorIssues = validator(ctx);\n issues.push(...validatorIssues);\n } catch (err) {\n issues.push({\n severity: 'error',\n message: `Validator \"${id}\" threw an error: ${err instanceof Error ? err.message : String(err)}`,\n ref: field.id,\n source: 'code',\n validatorId: id,\n });\n }\n }\n\n return issues;\n}\n\n/**\n * Run code validators for a field group.\n */\nfunction runGroupValidators(\n group: FieldGroup,\n schema: FormSchema,\n responses: Record<Id, FieldResponse>,\n registry: ValidatorRegistry,\n): ValidationIssue[] {\n if (!group.validate) {\n return [];\n }\n\n const refs = Array.isArray(group.validate) ? group.validate : [group.validate];\n const issues: ValidationIssue[] = [];\n\n // Convert responses to values for validator context\n const values: Record<Id, FieldValue> = {};\n for (const [id, response] of Object.entries(responses)) {\n if (response.state === 'answered' && response.value !== undefined) {\n values[id] = response.value;\n }\n }\n\n for (const ref of refs) {\n const { id, params } = parseValidatorRef(ref);\n const validator = registry[id];\n\n if (!validator) {\n issues.push({\n severity: 'warning',\n message: `Validator \"${id}\" not found for group \"${group.id}\"`,\n ref: group.id,\n source: 'code',\n validatorId: id,\n });\n continue;\n }\n\n const ctx: ValidatorContext = {\n schema,\n values,\n targetId: group.id,\n targetSchema: group,\n params,\n };\n\n try {\n const validatorIssues = validator(ctx);\n issues.push(...validatorIssues);\n } catch (err) {\n issues.push({\n severity: 'error',\n message: `Validator \"${id}\" threw an error: ${err instanceof Error ? err.message : String(err)}`,\n ref: group.id,\n source: 'code',\n validatorId: id,\n });\n }\n }\n\n return issues;\n}\n\n// =============================================================================\n// Main Validate Function\n// =============================================================================\n\n/**\n * Validate a parsed form.\n *\n * @param form - The parsed form to validate\n * @param opts - Validation options\n * @returns Validation result with issues and validity flag\n */\nexport function validate(form: ParsedForm, opts?: ValidateOptions): ValidateResult {\n const issues: ValidationIssue[] = [];\n const registry = opts?.validatorRegistry ?? {};\n\n // Validate each field\n for (const group of form.schema.groups) {\n for (const field of group.children) {\n // Built-in validation\n issues.push(...validateField(field, form.responsesByFieldId));\n\n // Code validators\n if (!opts?.skipCodeValidators) {\n issues.push(...runCodeValidators(field, form.schema, form.responsesByFieldId, registry));\n }\n }\n\n // Group-level validators\n if (!opts?.skipCodeValidators) {\n issues.push(...runGroupValidators(group, form.schema, form.responsesByFieldId, registry));\n }\n }\n\n // Determine if valid (no error-severity issues)\n const isValid = !issues.some((i) => i.severity === 'error');\n\n return { issues, isValid };\n}\n","/**\n * Inspect module - provides the main entry point for form inspection.\n *\n * Combines validation results with summaries into a unified InspectResult,\n * with issues sorted by priority (ascending, 1 = highest).\n */\nimport type {\n ParsedForm,\n InspectResult,\n InspectIssue,\n ValidationIssue,\n IssueScope,\n IssueReason,\n FieldPriorityLevel,\n Field,\n FieldProgress,\n Id,\n} from './coreTypes';\nimport { DEFAULT_PRIORITY } from '../settings.js';\nimport { validate } from './validate';\nimport { computeStructureSummary, computeProgressSummary, computeFormState } from './summaries';\n\n/**\n * Inspect options for customizing behavior.\n */\nexport interface InspectOptions {\n /** Skip code validators */\n skipCodeValidators?: boolean;\n /** Target roles to filter issues by (default: all roles, '*' means all) */\n targetRoles?: string[];\n}\n\n/**\n * Inspect a parsed form and return a unified result with summaries and issues.\n *\n * This is the main entry point for form inspection. It:\n * 1. Runs validation to get all issues\n * 2. Converts ValidationIssues to InspectIssues\n * 3. Computes structure and progress summaries\n * 4. Adds issues for empty optional fields\n * 5. Sorts all issues by priority (ascending, 1 = highest)\n *\n * @param form - The parsed form to inspect\n * @param options - Optional inspection options\n * @returns InspectResult with summaries and prioritized issues\n */\nexport function inspect(form: ParsedForm, options: InspectOptions = {}): InspectResult {\n // Run validation (synchronous)\n const validationResult = validate(form, {\n skipCodeValidators: options.skipCodeValidators,\n });\n\n // Convert validation issues to inspect issues first\n const validationInspectIssues = convertValidationIssues(validationResult.issues, form);\n\n // Compute structure summary\n const structureSummary = computeStructureSummary(form.schema);\n\n // Compute progress summary with the converted issues and responses\n const progressSummary = computeProgressSummary(\n form.schema,\n form.responsesByFieldId,\n form.notes,\n validationInspectIssues,\n );\n const formState = computeFormState(progressSummary);\n\n // Add issues for unanswered optional fields\n const allIssues = addOptionalUnansweredIssues(\n validationInspectIssues,\n form,\n progressSummary.fields,\n );\n\n // Sort and assign priorities\n const sortedIssues = sortAndAssignPriorities(allIssues, form);\n\n // Filter by role and add blocking annotations\n const issues = filterIssuesByRole(sortedIssues, form, options.targetRoles);\n\n // Form is complete when all issues for the target role(s) are resolved.\n // This is role-aware: if targeting only \"agent\" role, user-role fields don't matter.\n // Each field must be either filled (no optional_unanswered issue) or skipped.\n const isComplete = issues.length === 0;\n\n return {\n structureSummary,\n progressSummary,\n issues,\n isComplete,\n formState,\n };\n}\n\n/**\n * Convert ValidationIssues to InspectIssues.\n */\nfunction convertValidationIssues(\n validationIssues: ValidationIssue[],\n form: ParsedForm,\n): InspectIssue[] {\n return validationIssues.map((vi) => ({\n ref: vi.ref ?? '',\n scope: determineScope(vi.ref ?? '', form),\n reason: mapValidationToInspectReason(vi),\n message: vi.message,\n severity: vi.severity === 'error' ? 'required' : 'recommended',\n priority: 0, // Will be assigned after sorting\n }));\n}\n\n/**\n * Add issues for unanswered optional fields.\n *\n * An `optional_unanswered` issue is only added when:\n * - The field is optional (not required)\n * - The field has no value (empty=true)\n * - The field is unanswered (answerState='unanswered')\n *\n * Fields that have been addressed (answered, skipped, or aborted) do NOT get\n * optional_unanswered issues, even if their value is empty. For example:\n * - A multi_select answered with no selections (selected=[])\n * - A string_list answered with no items (items=[])\n * These are intentional \"none\" answers, not missing data.\n */\nfunction addOptionalUnansweredIssues(\n existingIssues: InspectIssue[],\n form: ParsedForm,\n fieldProgress: Record<string, FieldProgress>,\n): InspectIssue[] {\n const issues = [...existingIssues];\n const fieldsWithIssues = new Set(existingIssues.map((i) => i.ref));\n\n for (const [fieldId, progress] of Object.entries(fieldProgress)) {\n // Only add optional_unanswered for truly unanswered fields.\n // Fields that have been addressed (answered/skipped/aborted) should not\n // get this issue - the agent has made a decision about them.\n if (progress.answerState !== 'unanswered') {\n continue;\n }\n\n if (progress.empty && !fieldsWithIssues.has(fieldId) && !isRequiredField(fieldId, form)) {\n issues.push({\n ref: fieldId,\n scope: 'field',\n reason: 'optional_unanswered',\n message: 'Optional field not yet addressed',\n severity: 'recommended',\n priority: 0,\n });\n }\n }\n\n return issues;\n}\n\n/**\n * Map ValidationIssue to InspectIssue reason code.\n */\nfunction mapValidationToInspectReason(vi: ValidationIssue): IssueReason {\n const msg = vi.message.toLowerCase();\n\n // Check for specific patterns in the message or code\n // Required empty - check code and message patterns for various field kinds\n if (\n vi.code === 'REQUIRED_EMPTY' ||\n (msg.includes('required') && msg.includes('empty')) ||\n (msg.includes('required') && msg.includes('no selection')) ||\n (msg.includes('required') && msg.includes('no selections')) ||\n msg.includes('must be answered') ||\n msg.includes('must be completed') ||\n msg.includes('must be checked')\n ) {\n return 'required_missing';\n }\n\n // Invalid checkbox state (not about being empty)\n if (\n vi.code === 'INVALID_CHECKBOX_STATE' ||\n vi.code === 'CHECKBOXES_INCOMPLETE' ||\n msg.includes('checkbox')\n ) {\n return 'checkbox_incomplete';\n }\n\n // Min items violations\n if (\n vi.code === 'MULTI_SELECT_TOO_FEW' ||\n vi.code === 'STRING_LIST_MIN_ITEMS' ||\n vi.message.includes('at least')\n ) {\n return 'min_items_not_met';\n }\n\n // Default to validation_error for other issues\n return 'validation_error';\n}\n\n/**\n * Determine the scope of an issue based on the ref.\n */\nfunction determineScope(ref: string, form: ParsedForm): IssueScope {\n // Check if it's an option reference (contains a dot)\n if (ref.includes('.')) {\n return 'option';\n }\n\n // Check if it's the form ID\n if (ref === form.schema.id) {\n return 'form';\n }\n\n // Check if it's a group ID\n for (const group of form.schema.groups) {\n if (ref === group.id) {\n return 'group';\n }\n }\n\n // Default to field\n return 'field';\n}\n\n/**\n * Check if a field is required.\n */\nfunction isRequiredField(fieldId: string, form: ParsedForm): boolean {\n for (const group of form.schema.groups) {\n for (const field of group.children) {\n if (field.id === fieldId) {\n return field.required;\n }\n }\n }\n return false;\n}\n\n/**\n * Priority scoring constants.\n *\n * Field priority weights:\n * - high: 3\n * - medium: 2 (default)\n * - low: 1\n *\n * Issue type scores:\n * - required_missing: 3\n * - validation_error: 2\n * - checkbox_incomplete: 3 (when required), 2 (when recommended)\n * - min_items_not_met: 2\n * - optional_unanswered: 1\n *\n * Total score = field_priority_weight + issue_type_score\n *\n * Priority tiers:\n * - P1: score >= 5\n * - P2: score >= 4\n * - P3: score >= 3\n * - P4: score >= 2\n * - P5: score >= 1\n */\nconst FIELD_PRIORITY_WEIGHTS: Record<FieldPriorityLevel, number> = {\n high: 3,\n medium: 2,\n low: 1,\n};\n\nconst ISSUE_TYPE_SCORES: Record<IssueReason, number> = {\n required_missing: 3,\n validation_error: 2,\n checkbox_incomplete: 2, // Base score, adjusted by severity\n min_items_not_met: 2,\n optional_unanswered: 1,\n};\n\n/**\n * Calculate the priority tier (1-5) from a score.\n */\nfunction scoreToTier(score: number): number {\n if (score >= 5) {\n return 1;\n }\n if (score >= 4) {\n return 2;\n }\n if (score >= 3) {\n return 3;\n }\n if (score >= 2) {\n return 4;\n }\n return 5;\n}\n\n/**\n * Get the issue type score, potentially adjusted by severity.\n */\nfunction getIssueTypeScore(reason: IssueReason, severity: 'required' | 'recommended'): number {\n const baseScore = ISSUE_TYPE_SCORES[reason];\n // checkbox_incomplete gets +1 when required\n if (reason === 'checkbox_incomplete' && severity === 'required') {\n return baseScore + 1;\n }\n return baseScore;\n}\n\n/**\n * Sort issues by priority tier and assign priority numbers.\n *\n * Priority is computed as a tier (1-5, P1-P5) based on:\n * - Field priority weight (high=3, medium=2, low=1)\n * - Issue type score (required_missing=3, validation_error=2, optional_unanswered=1)\n *\n * Within each tier, issues are sorted by severity (required first) then by ref.\n */\nfunction sortAndAssignPriorities(issues: InspectIssue[], form: ParsedForm): InspectIssue[] {\n // Calculate scores and assign tier-based priorities\n const scoredIssues = issues.map((issue) => {\n const fieldPriority = getFieldPriority(issue.ref, form);\n const fieldWeight = FIELD_PRIORITY_WEIGHTS[fieldPriority];\n const issueScore = getIssueTypeScore(issue.reason, issue.severity);\n const totalScore = fieldWeight + issueScore;\n const tier = scoreToTier(totalScore);\n\n return {\n ...issue,\n priority: tier,\n _score: totalScore, // For sorting within tier\n };\n });\n\n // Sort by:\n // 1. Priority tier (ascending, P1 first)\n // 2. Severity (required before recommended)\n // 3. Score (descending, higher scores first within tier)\n // 4. Ref (alphabetically for deterministic output)\n scoredIssues.sort((a, b) => {\n if (a.priority !== b.priority) {\n return a.priority - b.priority;\n }\n if (a.severity !== b.severity) {\n return a.severity === 'required' ? -1 : 1;\n }\n if (a._score !== b._score) {\n return b._score - a._score;\n }\n return a.ref.localeCompare(b.ref);\n });\n\n // Remove internal _score field\n return scoredIssues.map(({ _score, ...issue }) => issue);\n}\n\n/**\n * Get the priority level for a field.\n */\nfunction getFieldPriority(ref: string, form: ParsedForm): FieldPriorityLevel {\n // Handle option refs (fieldId.optionId)\n const fieldId = ref.includes('.') ? ref.split('.')[0] : ref;\n\n for (const group of form.schema.groups) {\n for (const field of group.children) {\n if (field.id === fieldId) {\n return field.priority;\n }\n }\n }\n return DEFAULT_PRIORITY; // Fallback for non-field refs (groups, form)\n}\n\n// =============================================================================\n// Role Filtering and Blocking Checkpoint Helpers\n// =============================================================================\n\n/**\n * Get all fields from the form schema as a flat array.\n */\nexport function getAllFields(form: ParsedForm): Field[] {\n return form.schema.groups.flatMap((g) => g.children);\n}\n\n/**\n * Find a field by its ID.\n */\nexport function findFieldById(form: ParsedForm, fieldId: Id): Field | undefined {\n for (const group of form.schema.groups) {\n for (const field of group.children) {\n if (field.id === fieldId) {\n return field;\n }\n }\n }\n return undefined;\n}\n\n/**\n * Get fields that match the target roles.\n * If targetRoles includes '*', returns all fields.\n */\nexport function getFieldsForRoles(form: ParsedForm, targetRoles: string[]): Field[] {\n const allFields = getAllFields(form);\n if (targetRoles.includes('*')) {\n return allFields;\n }\n return allFields.filter((field) => targetRoles.includes(field.role));\n}\n\n/**\n * Check if a checkbox field is complete based on its mode.\n *\n * Completion semantics by mode:\n * - 'all': All options must be done or n/a\n * - 'any': At least minDone (default 1) options must be done\n * - 'explicit': No options may be 'unfilled'\n */\nexport function isCheckboxComplete(form: ParsedForm, fieldId: Id): boolean {\n const field = findFieldById(form, fieldId);\n if (field?.kind !== 'checkboxes') {\n return true; // Non-checkbox fields are not blocking\n }\n\n const checkboxField = field;\n const response = form.responsesByFieldId[fieldId];\n\n // If no response or not answered, checkbox is not complete\n if (response?.state !== 'answered') {\n return false;\n }\n\n const value = response.value;\n if (value?.kind !== 'checkboxes') {\n return false;\n }\n\n const values = value.values;\n const optionIds = checkboxField.options.map((o) => o.id);\n const mode = checkboxField.checkboxMode;\n\n if (mode === 'multi') {\n // Multi mode: all options must be done or na (not todo, incomplete, or active)\n // If minDone is set, at least that many must be done\n const minDone = checkboxField.minDone;\n if (minDone !== undefined) {\n const doneCount = optionIds.filter((id) => values[id] === 'done').length;\n return doneCount >= minDone;\n }\n // Otherwise, all must be done or na\n return optionIds.every((id) => values[id] === 'done' || values[id] === 'na');\n }\n\n if (mode === 'simple') {\n // Simple mode (GFM-compatible): all options must be done (not todo)\n return optionIds.every((id) => values[id] === 'done');\n }\n\n if (mode === 'explicit') {\n // Explicit mode: no unfilled values remain (all must be yes or no)\n return optionIds.every((id) => values[id] !== 'unfilled');\n }\n\n // Default case (shouldn't happen with valid CheckboxMode)\n return true;\n}\n\n/**\n * Result of finding a blocking checkpoint.\n */\nexport interface BlockingCheckpointResult {\n /** Index in orderIndex where the blocking checkpoint is */\n index: number;\n /** Field ID of the blocking checkpoint */\n fieldId: Id;\n}\n\n/**\n * Find the first incomplete blocking checkpoint in the form.\n * Returns null if no blocking checkpoint is incomplete.\n */\nexport function findBlockingCheckpoint(form: ParsedForm): BlockingCheckpointResult | null {\n for (let i = 0; i < form.orderIndex.length; i++) {\n const fieldId = form.orderIndex[i];\n if (!fieldId) {\n continue;\n }\n\n const field = findFieldById(form, fieldId);\n if (field?.kind !== 'checkboxes') {\n continue;\n }\n\n const checkboxField = field;\n if (checkboxField.approvalMode === 'blocking' && !isCheckboxComplete(form, fieldId)) {\n return { index: i, fieldId };\n }\n }\n return null;\n}\n\n/**\n * Get the set of field IDs that are blocked by a checkpoint.\n * These are all fields that appear after the blocking checkpoint in orderIndex.\n */\nexport function getBlockedFieldIds(\n form: ParsedForm,\n blockingCheckpoint: BlockingCheckpointResult,\n): Set<Id> {\n const blocked = new Set<Id>();\n for (let i = blockingCheckpoint.index + 1; i < form.orderIndex.length; i++) {\n const fieldId = form.orderIndex[i];\n if (fieldId) {\n blocked.add(fieldId);\n }\n }\n return blocked;\n}\n\n/**\n * Filter issues to only include those for fields matching target roles.\n * Also adds blockedBy annotation for fields blocked by a checkpoint.\n */\nexport function filterIssuesByRole(\n issues: InspectIssue[],\n form: ParsedForm,\n targetRoles?: string[],\n): InspectIssue[] {\n // Find blocking checkpoint first\n const blockingCheckpoint = findBlockingCheckpoint(form);\n const blockedFieldIds = blockingCheckpoint\n ? getBlockedFieldIds(form, blockingCheckpoint)\n : new Set<Id>();\n\n // Get field IDs for target roles\n const targetFieldIds =\n targetRoles && !targetRoles.includes('*')\n ? new Set(getFieldsForRoles(form, targetRoles).map((f) => f.id))\n : null;\n\n return issues\n .map((issue) => {\n // Extract field ID from ref (handles both field refs and option refs)\n const fieldId = issue.ref.includes('.') ? issue.ref.split('.')[0] : issue.ref;\n\n // Check if this field is blocked\n const isBlocked = fieldId && blockedFieldIds.has(fieldId);\n const annotatedIssue: InspectIssue = isBlocked\n ? { ...issue, blockedBy: blockingCheckpoint!.fieldId }\n : issue;\n\n return annotatedIssue;\n })\n .filter((issue) => {\n // If no target roles specified, include all issues\n if (!targetFieldIds) {\n return true;\n }\n\n // Extract field ID and check if it matches target roles\n const fieldId = issue.ref.includes('.') ? (issue.ref.split('.')[0] ?? issue.ref) : issue.ref;\n\n // Include if field matches target roles, or if it's not a field ref (form/group level)\n const field = findFieldById(form, fieldId);\n return !field || targetFieldIds.has(fieldId);\n });\n}\n","/**\n * Patch application for Markform documents.\n *\n * Applies patches to update field values with validation.\n */\n\nimport type {\n ApplyResult,\n ApplyStatus,\n CellResponse,\n CheckboxesValue,\n CheckboxMode,\n CheckboxValue,\n Field,\n FieldResponse,\n FieldValue,\n Id,\n InspectIssue,\n Note,\n NoteId,\n ParsedForm,\n Patch,\n PatchCoercionType,\n PatchWarning,\n SetCheckboxesPatch,\n TableRowResponse,\n TableValue,\n} from './coreTypes.js';\nimport { detectSentinel } from './parseSentinels.js';\nimport { computeAllSummaries, computeFormState, isFormComplete } from './summaries.js';\nimport { validate } from './validate.js';\n\n// =============================================================================\n// Patch Validation\n// =============================================================================\n\n/**\n * Internal patch error type that matches PatchRejection interface.\n * Includes field info for type mismatch errors to help LLM generate correct patches.\n */\ninterface PatchError {\n patchIndex: number;\n message: string;\n /** Field ID if available (for type mismatch errors) */\n fieldId?: string;\n /** Field kind if available (for type mismatch errors) */\n fieldKind?: string;\n /** Column IDs if available (for table fields) */\n columnIds?: string[];\n}\n\n/** Mapping from patch op to required field kind for simple type checks. */\nconst PATCH_OP_TO_FIELD_KIND: Record<string, string> = {\n set_string: 'string',\n set_number: 'number',\n set_string_list: 'string_list',\n set_single_select: 'single_select',\n set_multi_select: 'multi_select',\n set_checkboxes: 'checkboxes',\n set_url: 'url',\n set_url_list: 'url_list',\n set_date: 'date',\n set_year: 'year',\n set_table: 'table',\n};\n\n/**\n * Find a field by ID in the form schema.\n */\nfunction findField(form: ParsedForm, fieldId: Id): Field | undefined {\n for (const group of form.schema.groups) {\n for (const field of group.children) {\n if (field.id === fieldId) {\n return field;\n }\n }\n }\n return undefined;\n}\n\n/**\n * Result of normalizing a patch, including optional coercion warning.\n */\ninterface NormalizationResult {\n patch: Patch;\n warning?: PatchWarning;\n}\n\n/**\n * Coerce a boolean value to the appropriate checkbox string based on mode.\n */\nfunction coerceBooleanToCheckboxValue(value: boolean, mode: CheckboxMode): CheckboxValue {\n if (mode === 'explicit') {\n return value ? 'yes' : 'no';\n }\n return value ? 'done' : 'todo';\n}\n\n/**\n * Create a patch warning for coercion.\n */\nfunction createWarning(\n index: number,\n fieldId: string,\n coercion: PatchCoercionType,\n message: string,\n): PatchWarning {\n return { patchIndex: index, fieldId, message, coercion };\n}\n\n/**\n * Normalize a patch, coercing common type mismatches with warnings.\n *\n * Coercions performed:\n * - Single string → string_list array\n * - Single URL string → url_list array\n * - Single option ID → multi_select array\n * - Boolean → checkbox string\n *\n * Returns the normalized patch and any coercion warning.\n */\nfunction normalizePatch(form: ParsedForm, patch: Patch, index: number): NormalizationResult {\n // Handle patches without fieldId (add_note, remove_note)\n if (patch.op === 'add_note' || patch.op === 'remove_note') {\n return { patch };\n }\n\n const field = findField(form, patch.fieldId);\n if (!field) {\n return { patch }; // Let validation handle missing field\n }\n\n // Coerce single string → string_list\n if (patch.op === 'set_string_list' && field.kind === 'string_list') {\n if (typeof patch.value === 'string') {\n return {\n patch: { ...patch, value: [patch.value] },\n warning: createWarning(\n index,\n field.id,\n 'string_to_list',\n `Coerced single string to string_list`,\n ),\n };\n }\n }\n\n // Coerce single URL → url_list\n if (patch.op === 'set_url_list' && field.kind === 'url_list') {\n if (typeof patch.value === 'string') {\n return {\n patch: { ...patch, value: [patch.value] },\n warning: createWarning(index, field.id, 'url_to_list', `Coerced single URL to url_list`),\n };\n }\n }\n\n // Coerce single option → multi_select\n if (patch.op === 'set_multi_select' && field.kind === 'multi_select') {\n if (typeof patch.value === 'string') {\n return {\n patch: { ...patch, value: [patch.value] },\n warning: createWarning(\n index,\n field.id,\n 'option_to_array',\n `Coerced single option ID to multi_select array`,\n ),\n };\n }\n }\n\n // Coerce array → checkboxes object or boolean → checkbox string\n if (patch.op === 'set_checkboxes' && field.kind === 'checkboxes') {\n // Handle null/undefined values - let validation handle the error\n if (!patch.value) {\n return { patch };\n }\n\n // Coerce array of option IDs to checkbox object with default state\n if (Array.isArray(patch.value)) {\n const defaultState = field.checkboxMode === 'explicit' ? 'yes' : 'done';\n const values: Record<string, CheckboxValue> = {};\n\n for (const item of patch.value) {\n if (typeof item === 'string') {\n values[item] = defaultState;\n }\n // Invalid items will be caught by validation\n }\n\n // Empty array: no warning\n if (patch.value.length === 0) {\n return { patch: { ...patch, value: values } as SetCheckboxesPatch };\n }\n\n return {\n patch: { ...patch, value: values } as SetCheckboxesPatch,\n warning: createWarning(\n index,\n field.id,\n 'array_to_checkboxes',\n `Coerced array to checkboxes object with '${defaultState}' state`,\n ),\n };\n }\n\n // Check if any values are booleans\n let needsNormalization = false;\n for (const value of Object.values(patch.value)) {\n if (typeof value === 'boolean') {\n needsNormalization = true;\n break;\n }\n }\n\n if (!needsNormalization) {\n return { patch };\n }\n\n // Coerce boolean values to strings\n const normalizedValues: Record<string, CheckboxValue> = {};\n for (const [optId, value] of Object.entries(patch.value)) {\n if (typeof value === 'boolean') {\n normalizedValues[optId] = coerceBooleanToCheckboxValue(value, field.checkboxMode);\n } else {\n normalizedValues[optId] = value;\n }\n }\n\n return {\n patch: { ...patch, value: normalizedValues } as SetCheckboxesPatch,\n warning: createWarning(\n index,\n field.id,\n 'boolean_to_checkbox',\n `Coerced boolean values to checkbox state strings`,\n ),\n };\n }\n\n return { patch };\n}\n\n/**\n * Create a type mismatch error with field metadata for LLM guidance.\n */\nfunction typeMismatchError(index: number, op: string, field: Field): PatchError {\n return {\n patchIndex: index,\n message: `Cannot apply ${op} to ${field.kind} field \"${field.id}\"`,\n fieldId: field.id,\n fieldKind: field.kind,\n columnIds: field.kind === 'table' ? field.columns.map((c) => c.id) : undefined,\n };\n}\n\n/**\n * Create an error for embedded sentinel in patch value.\n */\nfunction embeddedSentinelError(\n index: number,\n fieldId: string,\n sentinelType: 'skip' | 'abort',\n): PatchError {\n const operation = sentinelType === 'skip' ? 'skip_field' : 'abort_field';\n const sentinel = sentinelType === 'skip' ? '%SKIP%' : '%ABORT%';\n return {\n patchIndex: index,\n message: `Value contains ${sentinel} sentinel for field \"${fieldId}\". Use ${operation} operation instead of embedding sentinel in value.`,\n fieldId,\n };\n}\n\n/**\n * Validate a single patch against the form schema.\n */\nfunction validatePatch(form: ParsedForm, patch: Patch, index: number): PatchError | null {\n // Handle patches without fieldId\n if (patch.op === 'add_note') {\n if (!form.idIndex.has(patch.ref)) {\n return { patchIndex: index, message: `Reference \"${patch.ref}\" not found in form` };\n }\n return null;\n }\n\n if (patch.op === 'remove_note') {\n const noteExists = form.notes.some((n) => n.id === patch.noteId);\n if (!noteExists) {\n return { patchIndex: index, message: `Note with id '${patch.noteId}' not found` };\n }\n return null;\n }\n\n // All other patches have fieldId\n const field = findField(form, patch.fieldId);\n if (!field) {\n return { patchIndex: index, message: `Field \"${patch.fieldId}\" not found` };\n }\n\n // Check field kind match for set_* patches\n const expectedKind = PATCH_OP_TO_FIELD_KIND[patch.op];\n if (expectedKind && field.kind !== expectedKind) {\n return typeMismatchError(index, patch.op, field);\n }\n\n // Check for embedded sentinels in string values (issue #119)\n // Agents should use skip_field/abort_field operations instead of embedding sentinels\n if (patch.op === 'set_string' || patch.op === 'set_url' || patch.op === 'set_date') {\n const sentinel = detectSentinel(patch.value);\n if (sentinel) {\n return embeddedSentinelError(index, field.id, sentinel.type);\n }\n }\n\n // Additional validation for container types, select/checkbox options, and table columns\n if (patch.op === 'set_string_list' && field.kind === 'string_list') {\n // Validate value is a non-null array\n if (!Array.isArray(patch.value)) {\n return {\n patchIndex: index,\n message: `Invalid set_string_list patch for field \"${field.id}\": value must be an array of strings`,\n fieldId: field.id,\n fieldKind: field.kind,\n };\n }\n // Check for embedded sentinels in list items (issue #119)\n for (const item of patch.value) {\n const sentinel = detectSentinel(item);\n if (sentinel) {\n return embeddedSentinelError(index, field.id, sentinel.type);\n }\n }\n } else if (patch.op === 'set_single_select' && field.kind === 'single_select') {\n if (patch.value !== null) {\n const validOptions = new Set(field.options.map((o) => o.id));\n if (!validOptions.has(patch.value)) {\n return {\n patchIndex: index,\n message: `Invalid option \"${patch.value}\" for field \"${field.id}\"`,\n };\n }\n }\n } else if (patch.op === 'set_multi_select' && field.kind === 'multi_select') {\n // Validate value is a non-null array\n if (!Array.isArray(patch.value)) {\n return {\n patchIndex: index,\n message: `Invalid set_multi_select patch for field \"${field.id}\": value must be an array of option IDs`,\n fieldId: field.id,\n fieldKind: field.kind,\n };\n }\n const validOptions = new Set(field.options.map((o) => o.id));\n for (const optId of patch.value) {\n if (!validOptions.has(optId)) {\n return { patchIndex: index, message: `Invalid option \"${optId}\" for field \"${field.id}\"` };\n }\n }\n } else if (patch.op === 'set_checkboxes' && field.kind === 'checkboxes') {\n // Validate value is a non-null object (not array, string, undefined, null)\n if (patch.value == null || typeof patch.value !== 'object' || Array.isArray(patch.value)) {\n return {\n patchIndex: index,\n message: `Invalid set_checkboxes patch for field \"${field.id}\": value must be an object mapping option IDs to checkbox state strings (e.g. \"todo\", \"done\", \"yes\", \"no\")`,\n fieldId: field.id,\n fieldKind: field.kind,\n };\n }\n const validOptions = new Set(field.options.map((o) => o.id));\n for (const optId of Object.keys(patch.value)) {\n if (!validOptions.has(optId)) {\n return { patchIndex: index, message: `Invalid option \"${optId}\" for field \"${field.id}\"` };\n }\n }\n } else if (patch.op === 'set_url_list' && field.kind === 'url_list') {\n // Validate value is a non-null array\n if (!Array.isArray(patch.value)) {\n return {\n patchIndex: index,\n message: `Invalid set_url_list patch for field \"${field.id}\": value must be an array of URLs`,\n fieldId: field.id,\n fieldKind: field.kind,\n };\n }\n // Check for embedded sentinels in list items (issue #119)\n for (const item of patch.value) {\n const sentinel = detectSentinel(item);\n if (sentinel) {\n return embeddedSentinelError(index, field.id, sentinel.type);\n }\n }\n } else if (patch.op === 'set_table' && field.kind === 'table') {\n // Validate value is a non-null array\n const columnIds = field.columns.map((c) => c.id);\n if (!Array.isArray(patch.value)) {\n return {\n patchIndex: index,\n message: `Invalid set_table patch for field \"${field.id}\": value must be an array of row objects. Each row should map column IDs to values. Columns: [${columnIds.join(', ')}]`,\n fieldId: field.id,\n fieldKind: field.kind,\n columnIds,\n };\n }\n const validColumns = new Set(columnIds);\n for (const row of patch.value) {\n if (row != null) {\n for (const colId of Object.keys(row)) {\n if (!validColumns.has(colId)) {\n return {\n patchIndex: index,\n message: `Invalid column \"${colId}\" for table field \"${field.id}\"`,\n };\n }\n }\n }\n }\n } else if (patch.op === 'skip_field' && field.required) {\n return { patchIndex: index, message: `Cannot skip required field \"${field.id}\"` };\n }\n\n return null;\n}\n\n// =============================================================================\n// Patch Application\n// =============================================================================\n\n/**\n * Generate a unique note ID for the form.\n */\nfunction generateNoteId(form: ParsedForm): NoteId {\n const existingIds = new Set(form.notes.map((n) => n.id));\n let counter = 1;\n while (existingIds.has(`n${counter}`)) {\n counter++;\n }\n return `n${counter}`;\n}\n\n/**\n * Set a simple value response (string, number, url, date, year).\n */\nfunction setSimpleValue(\n responses: Record<Id, FieldResponse>,\n fieldId: Id,\n kind: string,\n value: string | number | null,\n): void {\n responses[fieldId] = { state: 'answered', value: { kind, value } as FieldValue };\n}\n\n/**\n * Set a list value response (string_list, url_list).\n */\nfunction setListValue(\n responses: Record<Id, FieldResponse>,\n fieldId: Id,\n kind: string,\n items: string[],\n): void {\n responses[fieldId] = { state: 'answered', value: { kind, items } as FieldValue };\n}\n\n/**\n * Set a single select value response.\n */\nfunction setSingleSelectValue(\n responses: Record<Id, FieldResponse>,\n fieldId: Id,\n selected: string | null,\n): void {\n responses[fieldId] = {\n state: 'answered',\n value: { kind: 'single_select', selected } as FieldValue,\n };\n}\n\n/**\n * Set a multi select value response.\n */\nfunction setMultiSelectValue(\n responses: Record<Id, FieldResponse>,\n fieldId: Id,\n selected: string[],\n): void {\n responses[fieldId] = {\n state: 'answered',\n value: { kind: 'multi_select', selected } as FieldValue,\n };\n}\n\n/**\n * Convert a patch row value to a cell response.\n */\nfunction patchValueToCell(value: string | number | null | undefined): CellResponse {\n if (value === null || value === undefined) {\n return { state: 'skipped' };\n }\n\n // Use shared sentinel detection for consistent handling across all patch types\n const sentinel = detectSentinel(value);\n if (sentinel) {\n return sentinel.type === 'skip'\n ? { state: 'skipped', ...(sentinel.reason && { reason: sentinel.reason }) }\n : { state: 'aborted', ...(sentinel.reason && { reason: sentinel.reason }) };\n }\n\n if (typeof value === 'string') {\n return { state: 'answered', value: value.trim() };\n }\n\n return { state: 'answered', value };\n}\n\n/**\n * Apply a single patch to the form.\n */\nfunction applyPatch(form: ParsedForm, responses: Record<Id, FieldResponse>, patch: Patch): void {\n switch (patch.op) {\n // Simple string value types\n case 'set_string':\n setSimpleValue(responses, patch.fieldId, 'string', patch.value);\n break;\n case 'set_url':\n setSimpleValue(responses, patch.fieldId, 'url', patch.value);\n break;\n case 'set_date':\n setSimpleValue(responses, patch.fieldId, 'date', patch.value);\n break;\n\n // Simple number value types\n case 'set_number':\n setSimpleValue(responses, patch.fieldId, 'number', patch.value);\n break;\n case 'set_year':\n setSimpleValue(responses, patch.fieldId, 'year', patch.value);\n break;\n\n // List types\n case 'set_string_list':\n setListValue(responses, patch.fieldId, 'string_list', patch.value);\n break;\n case 'set_url_list':\n setListValue(responses, patch.fieldId, 'url_list', patch.value);\n break;\n\n // Select types\n case 'set_single_select':\n setSingleSelectValue(responses, patch.fieldId, patch.value);\n break;\n\n case 'set_multi_select':\n setMultiSelectValue(responses, patch.fieldId, patch.value);\n break;\n\n // Checkboxes (merge with existing)\n case 'set_checkboxes': {\n const existing =\n (responses[patch.fieldId]?.value as CheckboxesValue | undefined)?.values ?? {};\n responses[patch.fieldId] = {\n state: 'answered',\n value: { kind: 'checkboxes', values: { ...existing, ...patch.value } } as CheckboxesValue,\n };\n break;\n }\n\n // Table\n case 'set_table': {\n const rows: TableRowResponse[] = (patch.value ?? []).map((patchRow) => {\n const row: TableRowResponse = {};\n if (patchRow != null) {\n for (const [colId, cellValue] of Object.entries(patchRow)) {\n row[colId] = patchValueToCell(cellValue);\n }\n }\n return row;\n });\n responses[patch.fieldId] = {\n state: 'answered',\n value: { kind: 'table', rows } as TableValue,\n };\n break;\n }\n\n // State changes\n case 'clear_field':\n responses[patch.fieldId] = { state: 'unanswered' };\n break;\n\n case 'skip_field':\n responses[patch.fieldId] = {\n state: 'skipped',\n ...(patch.reason && { reason: patch.reason }),\n };\n break;\n\n case 'abort_field':\n responses[patch.fieldId] = {\n state: 'aborted',\n ...(patch.reason && { reason: patch.reason }),\n };\n break;\n\n // Notes\n case 'add_note': {\n const noteId = generateNoteId(form);\n form.notes.push({ id: noteId, ref: patch.ref, role: patch.role, text: patch.text });\n break;\n }\n\n case 'remove_note': {\n const idx = form.notes.findIndex((n) => n.id === patch.noteId);\n if (idx >= 0) form.notes.splice(idx, 1);\n break;\n }\n }\n}\n\n// =============================================================================\n// Issue Conversion (ValidationIssue -> InspectIssue)\n// =============================================================================\n\n/**\n * Convert validation issues to inspect issues with priorities.\n */\nfunction convertToInspectIssues(form: ParsedForm): InspectIssue[] {\n const result = validate(form, { skipCodeValidators: true });\n const issues: InspectIssue[] = [];\n let priority = 1;\n\n for (const vi of result.issues) {\n issues.push({\n ref: vi.ref ?? '',\n scope: 'field', // Default to field scope; can be refined based on validator context\n reason: vi.severity === 'error' ? 'validation_error' : 'optional_unanswered',\n message: vi.message,\n severity: vi.severity === 'error' ? 'required' : 'recommended',\n priority: priority++,\n });\n }\n\n return issues;\n}\n\n// =============================================================================\n// Main Apply Function\n// =============================================================================\n\n/**\n * Apply patches to a parsed form.\n *\n * Uses best-effort semantics: valid patches are applied even when some fail.\n * Invalid patches are rejected with detailed error messages.\n *\n * @param form - The parsed form to update\n * @param patches - Array of patches to apply\n * @returns Apply result with new summaries, status, and detailed feedback\n */\nexport function applyPatches(form: ParsedForm, patches: Patch[]): ApplyResult {\n // Normalize patches and collect coercion warnings\n const normalized: NormalizationResult[] = patches.map((p, i) => normalizePatch(form, p, i));\n const warnings: PatchWarning[] = normalized.filter((r) => r.warning).map((r) => r.warning!);\n const normalizedPatches = normalized.map((r) => r.patch);\n\n // Validate each patch independently (best-effort semantics)\n const validPatches: Patch[] = [];\n const errors: PatchError[] = [];\n\n for (let i = 0; i < normalizedPatches.length; i++) {\n const patch = normalizedPatches[i];\n if (patch) {\n const error = validatePatch(form, patch, i);\n if (error) {\n errors.push(error);\n } else {\n validPatches.push(patch);\n }\n }\n }\n\n // If all patches failed, return rejected (no changes to form)\n if (validPatches.length === 0 && errors.length > 0) {\n const issues = convertToInspectIssues(form);\n const summaries = computeAllSummaries(form.schema, form.responsesByFieldId, form.notes, issues);\n\n return {\n applyStatus: 'rejected',\n structureSummary: summaries.structureSummary,\n progressSummary: summaries.progressSummary,\n issues,\n isComplete: summaries.isComplete,\n formState: summaries.formState,\n appliedPatches: [],\n rejectedPatches: errors,\n warnings: [], // No warnings if nothing applied\n };\n }\n\n // Create new responses and notes (don't mutate original)\n const newResponses: Record<Id, FieldResponse> = { ...form.responsesByFieldId };\n const newNotes: Note[] = [...form.notes];\n form.notes = newNotes;\n\n // Apply only valid patches\n for (const patch of validPatches) {\n applyPatch(form, newResponses, patch);\n }\n\n // Update form with new responses\n form.responsesByFieldId = newResponses;\n\n // Compute new summaries\n const issues = convertToInspectIssues(form);\n const summaries = computeAllSummaries(form.schema, newResponses, newNotes, issues);\n\n // Determine status: 'applied' if all succeeded, 'partial' if some failed\n const applyStatus: ApplyStatus = errors.length > 0 ? 'partial' : 'applied';\n\n return {\n applyStatus,\n structureSummary: summaries.structureSummary,\n progressSummary: summaries.progressSummary,\n issues,\n isComplete: isFormComplete(summaries.progressSummary),\n formState: computeFormState(summaries.progressSummary),\n appliedPatches: validPatches,\n rejectedPatches: errors,\n warnings,\n };\n}\n"],"mappings":"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"}