diff --git a/.github/actions/prune-repository/prune.py b/.github/actions/prune-repository/prune.py index 7b4055dabe5..3600ac71c1d 100644 --- a/.github/actions/prune-repository/prune.py +++ b/.github/actions/prune-repository/prune.py @@ -40,24 +40,11 @@ "@rust/hash-graph-types": ["@rust/hash-graph-test-data"], } -# Extras that must not fire on a transitive or prefix match. Brunch core's -# architecture and contract tests inspect the app and shipped plugins, but a job -# whose requested scope is only a sibling or a consumer of core must not pull -# those fixtures. -REQUESTED_DEPENDENCIES: dict[str, list[str]] = { - "@hashintel/brunch-agent": [ - "@apps/brunch-agent", - "@hashintel/brunch-agent-plugin-gherkin", - "@hashintel/brunch-agent-plugin-sdcpn", - ], -} - # Non-workspace paths required by packages in the *requested* scope. # `turbo prune` copies workspace directories and root manifests only. REQUESTED_PATHS: dict[str, list[str]] = { - # The Brunch context root is deliberately not a workspace, but the - # architecture tests in packages/core read its docs, scripts, and agent - # contract files + # Core's shipped-definition and baseline tests read the non-workspace + # context root alongside their plugin task dependencies. "@hashintel/brunch-agent": [ ".config/oxlint/brunch", "libs/@hashintel/brunch-agent/AGENTS.md", @@ -66,9 +53,18 @@ "libs/@hashintel/brunch-agent/evaluations", "libs/@hashintel/brunch-agent/scripts", ], - # The app's condition-5 test executes the evaluation runner as a child - # process; the context root is not a workspace and must be copied explicitly. - "@apps/brunch-agent": ["libs/@hashintel/brunch-agent/evaluations"], + # The app's tests execute evaluation runners and govern the complete Brunch + # composition. Its context root is not a workspace, so copy the docs, + # scripts, and agent contract files explicitly. + "@apps/brunch-agent": [ + ".config/oxlint/brunch", + "libs/@hashintel/brunch-agent/AGENTS.md", + "libs/@hashintel/brunch-agent/CONTEXT.md", + "libs/@hashintel/brunch-agent/docs", + "libs/@hashintel/brunch-agent/evaluations", + "libs/@hashintel/brunch-agent/scripts", + "libs/@hashintel/petrinaut/docs", + ], } TURBO_QUERY = """ @@ -129,20 +125,6 @@ def turbo_dependency_map() -> dict[str, frozenset[str]]: return dep_map -def extras_for_requested(requested: Iterable[str]) -> frozenset[str]: - """Return extras implied by the job's requested scopes only. - - Exact identity: a name that is a prefix of its siblings must not match them. - """ - - names = set(requested) - extras: set[str] = set() - for trigger, additions in REQUESTED_DEPENDENCIES.items(): - if trigger in names: - extras.update(additions) - return frozenset(extras) - - def extra_paths_for_requested(requested: Iterable[str]) -> list[str]: """Return non-workspace paths implied by the job's requested scopes only.""" @@ -297,7 +279,7 @@ def main(argv: list[str] | None = None) -> None: } dependencies = turbo_dependency_map() - scopes = fixpoint_expand(initial | extras_for_requested(initial), dependencies) + scopes = fixpoint_expand(initial, dependencies) turbo_prune(scopes, dry_run=args.dry_run) copy_extra_paths(initial, dry_run=args.dry_run) stub_missing_members(dry_run=args.dry_run) diff --git a/.github/actions/prune-repository/prune_test.py b/.github/actions/prune-repository/prune_test.py index fa07b4fb326..9b45e95c46b 100644 --- a/.github/actions/prune-repository/prune_test.py +++ b/.github/actions/prune-repository/prune_test.py @@ -8,7 +8,6 @@ from prune import ( expand_scopes, extra_paths_for_requested, - extras_for_requested, fixpoint_expand, ) @@ -21,21 +20,19 @@ class BrunchRequestedExtras(unittest.TestCase): - def test_core_job_adds_the_app_plugins_and_context_paths(self) -> None: - expected_workspaces = frozenset({APP, PLUGIN_GHERKIN, PLUGIN_SDCPN}) - self.assertEqual(extras_for_requested({CORE}), expected_workspaces) + def test_app_task_adds_the_core_plugins_and_context_paths(self) -> None: + expected_workspaces = frozenset({CORE, PLUGIN_GHERKIN, PLUGIN_SDCPN}) expanded = fixpoint_expand( - {CORE} | extras_for_requested({CORE}), + {APP}, { - CORE: frozenset(), - APP: frozenset({CORE}), + APP: expected_workspaces, PLUGIN_GHERKIN: frozenset({CORE}), PLUGIN_SDCPN: frozenset({CORE}), }, ) self.assertTrue(expected_workspaces.issubset(expanded)) self.assertEqual( - extra_paths_for_requested({CORE}), + extra_paths_for_requested({APP}), [ ".config/oxlint/brunch", "libs/@hashintel/brunch-agent/AGENTS.md", @@ -43,32 +40,27 @@ def test_core_job_adds_the_app_plugins_and_context_paths(self) -> None: "libs/@hashintel/brunch-agent/docs", "libs/@hashintel/brunch-agent/evaluations", "libs/@hashintel/brunch-agent/scripts", + "libs/@hashintel/petrinaut/docs", ], ) - def test_app_job_adds_the_baseline_evaluation_paths(self) -> None: + def test_core_adds_its_non_workspace_test_fixtures(self) -> None: self.assertEqual( - extra_paths_for_requested({APP}), - ["libs/@hashintel/brunch-agent/evaluations"], + extra_paths_for_requested({CORE}), + [ + ".config/oxlint/brunch", + "libs/@hashintel/brunch-agent/AGENTS.md", + "libs/@hashintel/brunch-agent/CONTEXT.md", + "libs/@hashintel/brunch-agent/docs", + "libs/@hashintel/brunch-agent/evaluations", + "libs/@hashintel/brunch-agent/scripts", + ], ) - def test_sibling_or_website_job_does_not_add_brunch_extras(self) -> None: - self.assertEqual(extras_for_requested({TRANSPORT}), frozenset()) - self.assertEqual(extras_for_requested({WEBSITE}), frozenset()) + def test_sibling_or_website_job_does_not_add_context_paths(self) -> None: self.assertEqual(extra_paths_for_requested({TRANSPORT}), []) self.assertEqual(extra_paths_for_requested({WEBSITE}), []) - def test_core_in_the_dependency_closure_does_not_add_the_app(self) -> None: - dependencies = { - WEBSITE: frozenset({CORE, TRANSPORT}), - TRANSPORT: frozenset(), - CORE: frozenset(), - } - expanded = fixpoint_expand({WEBSITE}, dependencies) - self.assertNotIn(APP, expanded) - self.assertEqual(extras_for_requested({WEBSITE}), frozenset()) - - class DarwinPrefix(unittest.TestCase): def test_child_crate_still_triggers_the_prefix_family(self) -> None: extras = expand_scopes({"@rust/darwin-kperf-sys"}) diff --git a/apps/brunch-agent/README.md b/apps/brunch-agent/README.md index d31d1761cfd..21648e24119 100644 --- a/apps/brunch-agent/README.md +++ b/apps/brunch-agent/README.md @@ -10,13 +10,25 @@ yarn dev:brunch The first step builds the Petrinaut libraries the panel imports (`dist/` and design-system codegen). Then it starts the Brunch server at `http://127.0.0.1:4321` and the real Petrinaut -website at `http://127.0.0.1:4915`. The website proxies `/api/chat` to Brunch. The panel talks to one plain -Flue chat agent: streamed text and reasoning, one server `ping` tool, one stub -skill (`confirm-path`, activated via `activate_skill`), and the existing Petrinaut -`readPetrinautDoc` client tool. There is no elicitation loop, sweep tool, or -`brunch_ask` on this path. Capture is a harness-side pipe: an explicit settled -range of Flue history is applied into a JSON store beside the conversation -database, not by the interviewer. +website at `http://127.0.0.1:4915`. The website proxies `/api/chat` to Brunch. The panel talks to one Flue +chat agent: streamed text and reasoning, one server `ping` tool, one +modelling runbook skill (`sdcpn-modelling`, activated via `activate_skill`, +with supporting resources via `read_skill_resource`), and the existing +Petrinaut `readPetrinautDoc` client tool. There is no elicitation loop, +sweep tool, or `brunch_ask` on this path. Capture is a harness-side pipe: +an explicit settled range of Flue history is applied into a JSON store +beside the conversation database, not by the interviewer. + +A headless Mission 3 drive (simulated expert, same `ChatAgent` door): + +```sh +yarn workspace @apps/brunch-agent runbook:headless +``` + +`ANTHROPIC_API_KEY` is required. `BRUNCH_CHAT_MODEL` selects the interviewer +(default `claude-sonnet-4-5` for this script only). Artifacts write under +`libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-runbook-headless/` +unless `BRUNCH_RUNBOOK_OUTPUT_DIR` is set. Conversations persist in `apps/brunch-agent/.data-wipe-me/conversations.db`. `BRUNCH_DEV_DB_PATH` overrides that local path. Capture envelopes for one Flue conversation sit beside that sqlite diff --git a/apps/brunch-agent/docs/task-dependencies.json b/apps/brunch-agent/docs/task-dependencies.json index 77f28e8f85a..6204b265254 100644 --- a/apps/brunch-agent/docs/task-dependencies.json +++ b/apps/brunch-agent/docs/task-dependencies.json @@ -40,7 +40,10 @@ ], "test:unit": [ "@apps/brunch-agent#build", + "@hashintel/brunch-agent#build", "@hashintel/brunch-agent-binding-flue#build", + "@hashintel/brunch-agent-plugin-gherkin#build", + "@hashintel/brunch-agent-plugin-sdcpn#build", "@hashintel/brunch-agent-transport-aisdk#build", "@hashintel/petrinaut-core#build" ] diff --git a/apps/brunch-agent/files/brunch-clipped.png b/apps/brunch-agent/files/brunch-clipped.png new file mode 100644 index 00000000000..486b0bfef05 Binary files /dev/null and b/apps/brunch-agent/files/brunch-clipped.png differ diff --git a/apps/brunch-agent/files/favicon.ico b/apps/brunch-agent/files/favicon.ico new file mode 100644 index 00000000000..95a803d9f5f Binary files /dev/null and b/apps/brunch-agent/files/favicon.ico differ diff --git a/apps/brunch-agent/package.json b/apps/brunch-agent/package.json index fb22a4e054a..b44ec1eb722 100644 --- a/apps/brunch-agent/package.json +++ b/apps/brunch-agent/package.json @@ -12,6 +12,8 @@ "lint:eslint": "oxlint --type-aware --type-check --report-unused-disable-directives-severity=error .", "lint:tsc": "tsgo --noEmit", "petrinaut:dev": "vite dev --config petrinaut-local.vite.config.ts", + "runbook:elicit": "vite build && node --experimental-strip-types src/runbook-elicitation-run.ts", + "runbook:headless": "vite build && node --experimental-strip-types src/runbook-headless-run.ts", "test:unit": "vitest run --config vitest.config.ts", "transcript": "node --experimental-strip-types src/transcript-cli.ts" }, diff --git a/apps/brunch-agent/src/agents/chat-agent.ts b/apps/brunch-agent/src/agents/chat-agent.ts index 01768fcbdad..36238b44164 100644 --- a/apps/brunch-agent/src/agents/chat-agent.ts +++ b/apps/brunch-agent/src/agents/chat-agent.ts @@ -1,46 +1,68 @@ "use agent"; /** - * One plain Flue chat agent for the Petrinaut panel throughline. + * One Flue chat agent for the Petrinaut panel throughline. * - * Capture is a harness-side pipe, not an interviewer tool. One stub skill is - * mounted so activation can appear in Flue history. + * Capture is a harness-side pipe, not an interviewer tool. One runbook skill + * carries the modelling lifecycle and its supporting resources. */ -import { defineSkill, useModel, useSkill, useTool } from "@flue/runtime"; +import { useInitialData, useModel, useSkill, useTool } from "@flue/runtime"; +import * as v from "valibot"; +import sdcpnModellingSkill from "../skills/sdcpn-modelling/SKILL.md"; +import { + petrinautConstructionTools, + VALIDATED_CONSTRUCTION_MODE, +} from "../tools/petrinaut-construction.ts"; import { ping } from "../tools/ping.ts"; import { readPetrinautDoc } from "../tools/read-petrinaut-doc.ts"; export const CHAT_MODEL_ID = process.env["BRUNCH_CHAT_MODEL"] || "claude-haiku-4-5"; -export const STUB_SKILL_NAME = "confirm-path"; +export const RUNBOOK_SKILL_NAME = sdcpnModellingSkill.name; export const ACTIVATE_SKILL_TOOL_NAME = "activate_skill"; -const confirmPath = defineSkill({ - name: STUB_SKILL_NAME, - description: - "Confirm how this assistant is mounted. Use when checking the server path or tool layout.", - instructions: - "Say that ping confirms the server tool path. Then continue helping the user.", -}); +export const chatAgentInitialDataSchema = v.optional( + v.object({ + mode: v.literal(VALIDATED_CONSTRUCTION_MODE), + }), +); + +export type ChatAgentInitialData = v.InferOutput< + typeof chatAgentInitialDataSchema +>; export function ChatAgent() { + const initialData = useInitialData(); useModel(`anthropic/${CHAT_MODEL_ID}`); - useSkill(confirmPath); + useSkill(sdcpnModellingSkill); useTool(ping); useTool(readPetrinautDoc); - return [ - "You are a concise assistant inside the Petrinaut editor.", + if (initialData?.mode === VALIDATED_CONSTRUCTION_MODE) { + for (const constructionTool of petrinautConstructionTools) { + useTool(constructionTool); + } + } + const instructions = [ + "You are the Brunch modelling assistant inside the Petrinaut editor.", + `Activate the \`${RUNBOOK_SKILL_NAME}\` skill before interviewing or constructing a process model.`, + "The Markdown IR is the shared workpiece of one looping lifecycle.", "Call ping when you need to confirm the server tool path.", - `Activate the \`${STUB_SKILL_NAME}\` skill before calling ping.`, "When the user asks how Petrinaut's UI works, call readPetrinautDoc.", "A client-tool-result signal is JSON [{ toolCallId, toolName, output }]. Treat output as the browser's result for that call and continue helping the user.", - ].join("\n"); + ]; + if (initialData?.mode === VALIDATED_CONSTRUCTION_MODE) { + instructions.push( + "This is a construct-only headless conversation. Use only the supplied runbook IR as modelling input, do not interview, and build the net through the mounted Petrinaut tools instead of emitting net JSON.", + ); + } + return instructions.join("\n"); } /** * Pinned, and never to be edited: conversation storage keys on this literal. */ ChatAgent.agentName = "brunch-chat-agent"; +ChatAgent.initialData = chatAgentInitialDataSchema; diff --git a/apps/brunch-agent/src/capture-sweep.ts b/apps/brunch-agent/src/capture-sweep.ts index 0b991c0c5b6..738bb236073 100644 --- a/apps/brunch-agent/src/capture-sweep.ts +++ b/apps/brunch-agent/src/capture-sweep.ts @@ -34,15 +34,22 @@ export interface CaptureSweepResult { const conversationUrl = (instanceId: string): string => `http://brunch.local/agents/${CHAT_AGENT_ROUTE}/${instanceId}`; -const ownedTransport = (identity: ConversationIdentity): typeof fetch => { +const sourceAppTransport: typeof fetch = async (input, init) => { + const { default: app } = await import("./app.ts"); + return app.fetch(input instanceof Request ? input : new Request(input, init)); +}; + +const ownedTransport = ( + identity: ConversationIdentity, + transport: typeof fetch, +): typeof fetch => { const ownership = agentOwnershipHeaders(identity); return async (input, init) => { - const { default: app } = await import("./app.ts"); const headers = new Headers(init?.headers); for (const [key, value] of Object.entries(ownership)) { headers.set(key, value); } - return app.fetch( + return transport( input instanceof Request ? new Request(input, { headers }) : new Request(input, { ...init, headers }), @@ -53,6 +60,7 @@ const ownedTransport = (identity: ConversationIdentity): typeof fetch => { export const applyCaptureSweep = async ( identity: ConversationIdentity, userEntryIds: readonly string[], + transport: typeof fetch = sourceAppTransport, ): Promise => { const instanceId = flueConversationIdFrom(identity); const store = createLocalCaptureStore(captureStorePath(instanceId), { @@ -60,7 +68,7 @@ export const applyCaptureSweep = async ( }); const historyReader = createFlueHistoryReader({ resolveConversationUrl: conversationUrl, - transport: ownedTransport(identity), + transport: ownedTransport(identity, transport), archive: store, }); const snapshot = await historyReader.read(instanceId); diff --git a/apps/brunch-agent/src/headless-petrinaut-client.ts b/apps/brunch-agent/src/headless-petrinaut-client.ts new file mode 100644 index 00000000000..6bcb0ab6d55 --- /dev/null +++ b/apps/brunch-agent/src/headless-petrinaut-client.ts @@ -0,0 +1,138 @@ +import { + createJsonDocHandle, + createPetrinaut, + parseSDCPNFile, +} from "@hashintel/petrinaut-core"; +import { + createPetrinautAiWritableCallbacks, + getLatestNetDefinitionToolName, +} from "@hashintel/petrinaut-core/ai"; + +import { + PETRINAUT_CONSTRUCTION_TOOL_NAMES, + type PetrinautConstructionToolName, +} from "./tools/petrinaut-construction.ts"; + +import type { Petrinaut } from "@hashintel/petrinaut-core"; + +export interface HeadlessPetrinautToolCall { + readonly toolCallId: string; + readonly toolName: string; + readonly input: unknown; +} + +export interface HeadlessPetrinautToolResult { + readonly toolCallId: string; + readonly toolName: string; + readonly output: + | { + readonly applied: true; + } + | { + readonly applied: false; + readonly error: string; + } + | { + readonly title: string; + readonly definition: ReturnType; + readonly extensions: Petrinaut["extensions"]; + }; +} + +const constructionToolNames = new Set( + PETRINAUT_CONSTRUCTION_TOOL_NAMES, +); + +const errorMessageFrom = (error: unknown): string => + error instanceof Error ? error.message : String(error); + +export const createHeadlessPetrinautClient = (title: string) => { + const handle = createJsonDocHandle({ + initial: { + places: [], + transitions: [], + types: [], + parameters: [], + differentialEquations: [], + }, + }); + const instance = createPetrinaut({ document: handle }); + const writableCallbacks = createPetrinautAiWritableCallbacks( + instance, + ) as unknown as Record unknown>; + + const execute = async ( + call: HeadlessPetrinautToolCall, + ): Promise => { + if (!constructionToolNames.has(call.toolName)) { + return { + toolCallId: call.toolCallId, + toolName: call.toolName, + output: { + applied: false, + error: `Headless Petrinaut client does not allow ${call.toolName}`, + }, + }; + } + + if (call.toolName === getLatestNetDefinitionToolName) { + return { + toolCallId: call.toolCallId, + toolName: call.toolName, + output: { + title, + definition: instance.definition.get(), + extensions: instance.extensions, + }, + }; + } + + const callback = writableCallbacks[call.toolName]; + if (callback === undefined) { + return { + toolCallId: call.toolCallId, + toolName: call.toolName, + output: { + applied: false, + error: `Petrinaut has no writable callback for ${call.toolName}`, + }, + }; + } + + try { + await callback(call.input); + return { + toolCallId: call.toolCallId, + toolName: call.toolName, + output: { applied: true }, + }; + } catch (error) { + return { + toolCallId: call.toolCallId, + toolName: call.toolName, + output: { applied: false, error: errorMessageFrom(error) }, + }; + } + }; + + const definition = () => instance.definition.get(); + const document = () => ({ title, ...definition() }); + const parse = () => parseSDCPNFile(document()); + + return { + definition, + document, + execute, + parse, + dispose: instance.dispose, + }; +}; + +export type HeadlessPetrinautClient = ReturnType< + typeof createHeadlessPetrinautClient +>; + +export const isPetrinautConstructionToolName = ( + toolName: string, +): toolName is PetrinautConstructionToolName => + constructionToolNames.has(toolName); diff --git a/apps/brunch-agent/src/load-built-application.ts b/apps/brunch-agent/src/load-built-application.ts new file mode 100644 index 00000000000..d1681374b34 --- /dev/null +++ b/apps/brunch-agent/src/load-built-application.ts @@ -0,0 +1,24 @@ +export interface BuiltBrunchApplication { + readonly fetch: (request: Request) => Response | Promise; + readonly stop: (timeoutMs?: number) => Promise; +} + +type BuiltApplicationModule = { + readonly loadFlueNodeApplication?: () => Promise; +}; + +/** + * Load the non-listening production artifact. Keep the specifier computed so + * TypeScript does not require generated `dist` declarations. + */ +export const loadBuiltBrunchApplication = + async (): Promise => { + const applicationUrl = new URL("../dist/app.mjs", import.meta.url).href; + const builtModule = (await import( + applicationUrl + )) as BuiltApplicationModule; + if (builtModule.loadFlueNodeApplication === undefined) { + throw new Error("dist/app.mjs does not export loadFlueNodeApplication"); + } + return builtModule.loadFlueNodeApplication(); + }; diff --git a/apps/brunch-agent/src/runbook-artifacts.ts b/apps/brunch-agent/src/runbook-artifacts.ts new file mode 100644 index 00000000000..e29fe7b14d4 --- /dev/null +++ b/apps/brunch-agent/src/runbook-artifacts.ts @@ -0,0 +1,61 @@ +/** Recover Mission 3 workpieces from a Flue `history()` snapshot. */ + +import type { FlueConversationPart, FlueConversationSnapshot } from "@flue/sdk"; + +export const RUNBOOK_IR_FENCE = "runbook-ir"; + +const runbookIrFencePattern = /```runbook-ir\s*\n([\s\S]*?)```/g; + +export const latestRunbookIrBlock = (text: string): string | undefined => { + const matches = [...text.matchAll(runbookIrFencePattern)]; + const last = matches.at(-1)?.[1]; + return last === undefined ? undefined : last.trim(); +}; + +const assistantTextFrom = (snapshot: FlueConversationSnapshot): string => + snapshot.messages + .filter((message) => message.purpose === "assistant") + .flatMap((message) => + message.parts.filter( + (part): part is Extract => + part.type === "text", + ), + ) + .map((part) => part.text) + .join("\n\n"); + +export const recoverRunbookIr = ( + snapshot: FlueConversationSnapshot, +): string | undefined => latestRunbookIrBlock(assistantTextFrom(snapshot)); + +export const interviewerToolNamesFrom = ( + snapshot: FlueConversationSnapshot, +): readonly string[] => [ + ...new Set( + snapshot.messages.flatMap((message) => + message.parts + .filter((part) => part.type === "dynamic-tool") + .map((part) => part.toolName), + ), + ), +]; + +export const skillResourcePathsFrom = ( + snapshot: FlueConversationSnapshot, +): readonly string[] => + snapshot.messages.flatMap((message) => + message.parts.flatMap((part) => { + if (part.type !== "dynamic-tool") return []; + if (part.toolName !== "read_skill_resource") return []; + if (part.state !== "output-available") return []; + if ( + typeof part.input !== "object" || + part.input === null || + !("path" in part.input) || + typeof part.input.path !== "string" + ) { + return []; + } + return [part.input.path]; + }), + ); diff --git a/apps/brunch-agent/src/runbook-elicitation-run.ts b/apps/brunch-agent/src/runbook-elicitation-run.ts new file mode 100644 index 00000000000..34aed31a349 --- /dev/null +++ b/apps/brunch-agent/src/runbook-elicitation-run.ts @@ -0,0 +1,431 @@ +/** + * Prospective elicitation-to-IR drive. The production ChatAgent interviews a + * second model, then emits a Markdown runbook IR without entering construction. + * + * Build first, then run: + * yarn turbo run build --filter @apps/brunch-agent + * yarn workspace @apps/brunch-agent runbook:elicit + */ + +import { execFile } from "node:child_process"; +import { createHash, randomUUID } from "node:crypto"; +import { mkdir, readFile, rm, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { basename, join } from "node:path"; +import { fileURLToPath } from "node:url"; +import { promisify } from "node:util"; + +import { observe, setProvider } from "@flue/runtime"; +import { createFlueClient } from "@flue/sdk"; + +import { + agentOwnershipHeaders, + flueConversationIdFrom, +} from "./conversation-identity.ts"; +import { formatFlueTranscript } from "./flue-transcript.ts"; +import { loadBuiltBrunchApplication } from "./load-built-application.ts"; +import { CHAT_AGENT_ROUTE } from "./routes.ts"; +import { + interviewerToolNamesFrom, + recoverRunbookIr, + skillResourcePathsFrom, +} from "./runbook-artifacts.ts"; + +import type Anthropic from "@anthropic-ai/sdk"; +import type { Provider } from "@earendil-works/pi-ai"; +import type { FlueConversationSnapshot } from "@flue/sdk"; + +process.env["BRUNCH_CHAT_MODEL"] ??= "claude-sonnet-4-5"; + +const execFileAsync = promisify(execFile); +const repositoryRoot = new URL("../../../", import.meta.url); +const repositoryRootPath = fileURLToPath(repositoryRoot); +const evaluationRoot = new URL( + "../../../libs/@hashintel/brunch-agent/evaluations/", + import.meta.url, +); +const caseDirectory = new URL("cases/vestera-scheduling/", evaluationRoot); +const defaultOutputDirectory = new URL( + "../../../libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-prospective-baseline-v1/", + import.meta.url, +).pathname; + +const expertModel = + process.env["BRUNCH_RUNBOOK_EXPERT_MODEL"] ?? "claude-sonnet-4-5"; +const hardStop = Number(process.env["BRUNCH_RUNBOOK_HARD_STOP"] ?? "8"); +const latencyStopMs = Number( + process.env["BRUNCH_RUNBOOK_LATENCY_STOP_MS"] ?? "180000", +); +const outputDirectory = + process.env["BRUNCH_RUNBOOK_OUTPUT_DIR"] ?? defaultOutputDirectory; +const expertClientModule = process.env["BRUNCH_RUNBOOK_ANTHROPIC_MODULE"]; +const interviewerProviderModule = + process.env["BRUNCH_RUNBOOK_INTERVIEWER_PROVIDER_MODULE"]; +const allowDirtyInstrument = + process.env["BRUNCH_RUNBOOK_ALLOW_DIRTY_INSTRUMENT"] === "1"; +const apiKey = process.env["ANTHROPIC_API_KEY"]; + +if (!Number.isSafeInteger(hardStop) || hardStop < 1) { + throw new Error("BRUNCH_RUNBOOK_HARD_STOP must be a positive integer"); +} +if (!Number.isFinite(latencyStopMs) || latencyStopMs <= 0) { + throw new Error("BRUNCH_RUNBOOK_LATENCY_STOP_MS must be positive"); +} +if (!apiKey && !expertClientModule) { + throw new Error( + "ANTHROPIC_API_KEY is required unless BRUNCH_RUNBOOK_ANTHROPIC_MODULE is set", + ); +} +const usesFrozenV1Configuration = + process.env["BRUNCH_CHAT_MODEL"] === "claude-sonnet-4-5" && + expertModel === "claude-sonnet-4-5" && + hardStop === 8 && + latencyStopMs === 180_000 && + expertClientModule === undefined && + interviewerProviderModule === undefined && + !allowDirtyInstrument; +if (outputDirectory === defaultOutputDirectory && !usesFrozenV1Configuration) { + throw new Error( + "Only the frozen v1 configuration may write to vestera-prospective-baseline-v1; set BRUNCH_RUNBOOK_OUTPUT_DIR for tests or a different campaign.", + ); +} + +const instrumentFiles = [ + "apps/brunch-agent/package.json", + "apps/brunch-agent/src/agents/chat-agent.ts", + "apps/brunch-agent/src/runbook-artifacts.ts", + "apps/brunch-agent/src/runbook-elicitation-run.ts", + "apps/brunch-agent/src/skills/sdcpn-modelling/SKILL.md", + "apps/brunch-agent/src/skills/sdcpn-modelling/elicitation.md", + "apps/brunch-agent/src/skills/sdcpn-modelling/ir-template.md", + "apps/brunch-agent/src/skills/sdcpn-modelling/pn-construction.md", + "apps/brunch-agent/src/skills/sdcpn-modelling/checks.md", + "libs/@hashintel/brunch-agent/evaluations/cases/vestera-scheduling/opening-message.md", + "libs/@hashintel/brunch-agent/evaluations/cases/vestera-scheduling/situation-pack.md", + "libs/@hashintel/brunch-agent/evaluations/oracles/vestera-scheduling/truth-ledger-v1-prospective.yaml", + "libs/@hashintel/brunch-agent/evaluations/oracles/ir-quality-ruler-v1.md", + "libs/@hashintel/brunch-agent/evaluations/protocols/ir-quality-ruler-v1/omniscient-grader.md", + "libs/@hashintel/brunch-agent/evaluations/protocols/ir-quality-ruler-v1/cold-ir-reviewer.md", + "libs/@hashintel/brunch-agent/evaluations/protocols/prospective-runbook-v1/protocol.md", +] as const; + +const sha256 = (content: string | Buffer): string => + createHash("sha256").update(content).digest("hex"); + +const hashedInstrumentFiles = await Promise.all( + instrumentFiles.map(async (path) => ({ + path, + hash: sha256(await readFile(new URL(path, repositoryRoot))), + })), +); +const fileSha256: Record = {}; +for (const file of hashedInstrumentFiles) { + fileSha256[file.path] = file.hash; +} +const { stdout: commitOutput } = await execFileAsync( + "git", + ["rev-parse", "HEAD"], + { cwd: repositoryRootPath }, +); +const sourceCommit = commitOutput.trim(); +const { stdout: statusOutput } = await execFileAsync( + "git", + ["status", "--short", "--", ...instrumentFiles], + { cwd: repositoryRootPath }, +); +const instrumentStatus = statusOutput.trim(); +if (instrumentStatus.length > 0 && !allowDirtyInstrument) { + throw new Error( + `Prospective instrument files are not clean:\n${instrumentStatus}\n` + + "Commit them before a paid run, or set BRUNCH_RUNBOOK_ALLOW_DIRTY_INSTRUMENT=1 only for a hermetic test.", + ); +} + +const openingRaw = await readFile( + new URL("opening-message.md", caseDirectory), + "utf8", +); +const openingSeparator = openingRaw.indexOf("\n---\n"); +const openingMessage = ( + openingSeparator === -1 ? openingRaw : openingRaw.slice(openingSeparator + 5) +).trim(); +const situationPack = await readFile( + new URL("situation-pack.md", caseDirectory), + "utf8", +); +const finalizationMessage = [ + "[Evaluation stop instruction; not expert evidence]", + "The interview turn budget is exhausted.", + "Emit the full current Markdown runbook IR in one `runbook-ir` fenced block.", + "Do not ask another question, construct the Petri net, or read construction resources.", + "Preserve every unresolved unknown, assumption, conflict, omission, and loss.", +].join(" "); + +interface ExpertMessage { + readonly role: "user" | "assistant"; + readonly content: string; +} + +interface ExpertClient { + messages: { + create(request: Anthropic.MessageCreateParamsNonStreaming): Promise<{ + readonly content: readonly { + readonly type: string; + readonly text?: string; + }[]; + readonly model?: string; + readonly stop_reason?: string | null; + readonly usage?: Partial; + }>; + }; +} + +const defaultExportFrom = async (specifier: string): Promise => { + const loaded: unknown = await import(specifier); + if (typeof loaded !== "object" || loaded === null || !("default" in loaded)) { + throw new Error(`${specifier} has no default export`); + } + return loaded.default as Value; +}; + +if (interviewerProviderModule) { + setProvider(await defaultExportFrom(interviewerProviderModule)); +} + +const AnthropicClient = (await import("@anthropic-ai/sdk")).default; +const expertClient: ExpertClient = expertClientModule + ? await defaultExportFrom(expertClientModule) + : (new AnthropicClient({ + apiKey, + maxRetries: 5, + timeout: 30 * 60 * 1000, + }) as ExpertClient); + +const expertMessages: ExpertMessage[] = []; +const expertUsage = { + calls: 0, + inputTokens: 0, + outputTokens: 0, + cacheReadTokens: 0, + cacheWriteTokens: 0, +}; + +const errorMessageFrom = (error: unknown): string => + error instanceof Error ? error.message : String(error); + +const askExpert = async (interviewerText: string): Promise => { + expertMessages.push({ role: "user", content: interviewerText }); + const response = await expertClient.messages.create({ + model: expertModel, + max_tokens: 1500, + thinking: { type: "disabled" }, + system: situationPack, + messages: expertMessages, + }); + expertUsage.calls += 1; + expertUsage.inputTokens += response.usage?.input_tokens ?? 0; + expertUsage.outputTokens += response.usage?.output_tokens ?? 0; + expertUsage.cacheReadTokens += response.usage?.cache_read_input_tokens ?? 0; + expertUsage.cacheWriteTokens += + response.usage?.cache_creation_input_tokens ?? 0; + const text = response.content + .filter( + (block): block is { readonly type: "text"; readonly text: string } => + block.type === "text" && typeof block.text === "string", + ) + .map((block) => block.text) + .join("\n") + .trim(); + if (text.length === 0) + throw new Error("The simulated expert returned no text"); + expertMessages.push({ role: "assistant", content: text }); + return text; +}; + +const latestAssistantText = (snapshot: FlueConversationSnapshot): string => { + for (const message of snapshot.messages.toReversed()) { + if (message.purpose !== "assistant") continue; + const text = message.parts + .filter((part) => part.type === "text") + .map((part) => part.text) + .join("\n") + .trim(); + if (text.length > 0) return text; + } + return ""; +}; + +type ModelCall = { + readonly durationMs: number; + readonly inputTokens: number; + readonly outputTokens: number; + readonly totalTokens: number; + readonly cost: number; +}; +const modelCalls: ModelCall[] = []; +const stopObserving = observe((event) => { + if (event.type !== "turn") return; + const usage = event.response.usage; + modelCalls.push({ + durationMs: event.durationMs, + inputTokens: usage?.input ?? 0, + outputTokens: usage?.output ?? 0, + totalTokens: usage?.totalTokens ?? 0, + cost: usage?.cost.total ?? 0, + }); +}); + +const startedAt = new Date().toISOString(); +const runId = `runbook-elicitation-${startedAt.replaceAll( + /[:.]/gu, + "-", +)}-${randomUUID().slice(0, 8)}`; +const identity = { + principalKey: "principal-runbook-elicitation", + conversationId: runId, +}; +const instanceId = flueConversationIdFrom(identity); +const databasePath = join(tmpdir(), `${runId}.db`); +process.env["BRUNCH_DEV_DB_PATH"] = databasePath; + +await mkdir(outputDirectory, { recursive: true }); +const application = await loadBuiltBrunchApplication(); + +try { + const client = createFlueClient({ + url: `http://brunch.local/agents/${CHAT_AGENT_ROUTE}/${instanceId}`, + fetch: (input, init) => + Promise.resolve( + application.fetch( + input instanceof Request ? input : new Request(input, init), + ), + ).then((response) => response), + headers: agentOwnershipHeaders(identity), + }); + + const logicalTurnDurationsMs: number[] = []; + const dispatch = async (body: string): Promise => { + const firstCallIndex = modelCalls.length; + const admission = await client.send({ message: { kind: "user", body } }); + await client.wait(admission); + logicalTurnDurationsMs.push( + modelCalls + .slice(firstCallIndex) + .reduce((total, call) => total + call.durationMs, 0), + ); + }; + + let interviewTurns = 1; + let stopReason = "hard-stop"; + let failure: string | undefined; + await dispatch(openingMessage); + + while (interviewTurns < hardStop) { + const previousDuration = logicalTurnDurationsMs.at(-1) ?? 0; + if (previousDuration > latencyStopMs) { + stopReason = "latency-stop"; + break; + } + // oxlint-disable-next-line eslint/no-await-in-loop -- interview turns are causally sequential. + const snapshot = await client.history(); + const interviewerText = latestAssistantText(snapshot); + if (interviewerText.length === 0) { + stopReason = "empty-interviewer"; + break; + } + let expertReply: string; + try { + // oxlint-disable-next-line eslint/no-await-in-loop -- the expert must answer this settled interviewer turn. + expertReply = await askExpert(interviewerText); + } catch (error) { + stopReason = "expert-error"; + failure = errorMessageFrom(error); + break; + } + // oxlint-disable-next-line eslint/no-await-in-loop -- the next interviewer turn depends on the expert reply. + await dispatch(expertReply); + interviewTurns += 1; + } + + await dispatch(finalizationMessage); + const snapshot = await client.history(); + const ir = recoverRunbookIr(snapshot); + const toolNames = interviewerToolNamesFrom(snapshot); + const resourcePaths = skillResourcePathsFrom(snapshot); + const resourceFilesRead = [ + ...new Set(resourcePaths.map((path) => basename(path))), + ]; + const transcript = formatFlueTranscript(snapshot); + const builtArtifact = await readFile( + new URL("../dist/app.mjs", import.meta.url), + ); + const record = { + runId, + startedAt, + interviewerModel: process.env["BRUNCH_CHAT_MODEL"], + expertModel, + hardStop, + latencyStopMs, + interviewTurns, + stopReason, + finalizationMessage, + logicalTurnDurationsMs, + modelCalls, + expertUsage, + toolNames, + resourcePaths, + ir, + failure, + wroteCaptureStore: false, + instrument: { + sourceCommit, + instrumentStatus, + fileSha256, + builtArtifactSha256: sha256(builtArtifact), + }, + transcript, + }; + const artifactBase = join(outputDirectory, runId); + await writeFile( + `${artifactBase}.json`, + `${JSON.stringify(record, null, 2)}\n`, + ); + await writeFile( + `${artifactBase}.md`, + [ + `# Prospective runbook elicitation — ${runId}`, + "", + `- Source commit: \`${sourceCommit}\``, + `- Interviewer: \`${record.interviewerModel}\``, + `- Simulated expert: \`${expertModel}\``, + `- Interview turns: ${interviewTurns} (hard stop ${hardStop})`, + `- Stop reason before final IR request: \`${stopReason}\``, + `- Recoverable IR: ${ir === undefined ? "no" : "yes"}`, + "- Final user message is an evaluation stop instruction, not expert evidence.", + "", + transcript, + "", + ].join("\n"), + ); + if (ir !== undefined) { + await writeFile(`${artifactBase}.ir.md`, `${ir}\n`); + } + + process.stdout.write( + `RUNBOOK_ELICITATION_RESULT ${JSON.stringify({ + stopReason, + hasIr: ir !== undefined, + interviewTurns, + toolNames, + resourceFilesRead, + failure, + wroteCaptureStore: false, + artifactBase, + })}\n`, + ); + if (failure !== undefined || ir === undefined) process.exitCode = 1; +} finally { + stopObserving(); + await application.stop(); + await rm(databasePath, { force: true }); +} diff --git a/apps/brunch-agent/src/runbook-headless-run.ts b/apps/brunch-agent/src/runbook-headless-run.ts new file mode 100644 index 00000000000..233a4571539 --- /dev/null +++ b/apps/brunch-agent/src/runbook-headless-run.ts @@ -0,0 +1,372 @@ +/** + * Construct-only side-quest drive. A filled runbook IR is the sole modelling + * input; the built production ChatAgent constructs through a headless + * Petrinaut client and never emits free-form net JSON. + * + * Build first, then run: + * yarn turbo run build --filter @apps/brunch-agent + * yarn workspace @apps/brunch-agent runbook:headless + */ + +import { mkdir, readFile, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { fileURLToPath } from "node:url"; + +import { observe } from "@flue/runtime"; +import { createFlueClient } from "@flue/sdk"; + +import { CLIENT_TOOL_RESULT_SIGNAL, isAwaitingClient } from "./client-tool.ts"; +import { + agentOwnershipHeaders, + flueConversationIdFrom, +} from "./conversation-identity.ts"; +import { + createHeadlessPetrinautClient, + isPetrinautConstructionToolName, + type HeadlessPetrinautToolCall, + type HeadlessPetrinautToolResult, +} from "./headless-petrinaut-client.ts"; +import { loadBuiltBrunchApplication } from "./load-built-application.ts"; +import { CHAT_AGENT_ROUTE } from "./routes.ts"; +import { + interviewerToolNamesFrom, + skillResourcePathsFrom, +} from "./runbook-artifacts.ts"; +import { VALIDATED_CONSTRUCTION_MODE } from "./tools/petrinaut-construction.ts"; + +import type { FlueConversationPart, FlueConversationSnapshot } from "@flue/sdk"; + +process.env.BRUNCH_CHAT_MODEL ??= "claude-sonnet-4-5"; + +const DOCUMENT_TITLE = "Coatings line scheduling"; +const MAX_CLIENT_ROUNDS = Number( + process.env.BRUNCH_RUNBOOK_CLIENT_ROUNDS ?? "80", +); + +const irPath = fileURLToPath( + new URL( + "../../../libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-runbook-headless/runbook-headless-2026-08-28T11-03-53-683Z.ir.md", + import.meta.url, + ), +); +const defaultOutputDirectory = fileURLToPath( + new URL( + "../../../libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-runbook-headless/", + import.meta.url, + ), +); +const outputDirectory = + process.env.BRUNCH_RUNBOOK_OUTPUT_DIR ?? defaultOutputDirectory; + +const filledIr = await readFile(irPath, "utf8"); +const conversationId = `runbook-validated-construction-${new Date() + .toISOString() + .replaceAll(/[:.]/gu, "-")}`; +const identity = { + principalKey: "principal-runbook-validated-construction", + conversationId, +}; +const instanceId = flueConversationIdFrom(identity); +const dbFile = join(tmpdir(), `${conversationId}.db`); +process.env.BRUNCH_DEV_DB_PATH = dbFile; + +const constructionRequest = [ + "Construct a Petrinaut net from the filled runbook IR below.", + "The IR is the only modelling input: do not interview or ask follow-up questions.", + "Activate the sdcpn-modelling skill, read its construction and check resources, and use the mounted validated Petrinaut tools.", + "Do not emit a pn-json block or any other free-form net JSON.", + "Finish by naming every inference, approximation, default, omission, unrepresentable fact, and still-open unknown.", + "", + filledIr.trim(), +].join("\n"); + +type TurnUsage = { + readonly durationMs?: number; + readonly inputTokens?: number; + readonly outputTokens?: number; + readonly totalTokens?: number; + readonly cost?: number; +}; + +const turnUsage: TurnUsage[] = []; +const stopObserving = observe((event) => { + if (event.type !== "turn") return; + const usage = event.response.usage; + turnUsage.push({ + ...(typeof event.durationMs === "number" + ? { durationMs: event.durationMs } + : {}), + ...(usage + ? { + inputTokens: usage.input, + outputTokens: usage.output, + totalTokens: usage.totalTokens, + cost: usage.cost.total, + } + : {}), + }); +}); + +const dynamicPartsFrom = ( + snapshot: FlueConversationSnapshot, +): Extract[] => + snapshot.messages.flatMap((message) => + message.parts.filter( + (part): part is Extract => + part.type === "dynamic-tool", + ), + ); + +const pendingCallsFrom = ( + snapshot: FlueConversationSnapshot, + completedCallIds: ReadonlySet, +): HeadlessPetrinautToolCall[] => + dynamicPartsFrom(snapshot).flatMap((part) => { + if (!isPetrinautConstructionToolName(part.toolName)) return []; + if (completedCallIds.has(part.toolCallId)) return []; + if (part.state !== "output-available" || !isAwaitingClient(part.output)) { + return []; + } + return [ + { + toolCallId: part.toolCallId, + toolName: part.toolName, + input: part.input, + }, + ]; + }); + +const validationRejectionsFrom = (snapshot: FlueConversationSnapshot) => + dynamicPartsFrom(snapshot).flatMap((part) => + isPetrinautConstructionToolName(part.toolName) && + part.state === "output-error" + ? [ + { + toolCallId: part.toolCallId, + toolName: part.toolName, + error: part.errorText, + }, + ] + : [], + ); + +const latestAssistantTextFrom = (snapshot: FlueConversationSnapshot): string => + snapshot.messages + .filter((message) => message.purpose === "assistant") + .flatMap((message) => + message.parts.filter( + (part): part is Extract => + part.type === "text", + ), + ) + .map((part) => part.text) + .join("\n\n") + .trim(); + +const totalFrom = (values: readonly (number | undefined)[]): number => + values.reduce((total, value) => total + (value ?? 0), 0); + +await mkdir(outputDirectory, { recursive: true }); + +const petrinautClient = createHeadlessPetrinautClient(DOCUMENT_TITLE); +let application: Awaited> | null = + null; + +try { + const loadedApplication = await loadBuiltBrunchApplication(); + application = loadedApplication; + const appTransport: typeof fetch = async (input, init) => + loadedApplication.fetch( + input instanceof Request ? input : new Request(input, init), + ); + const client = createFlueClient({ + url: `http://brunch.local/agents/${CHAT_AGENT_ROUTE}/${instanceId}`, + fetch: appTransport, + headers: agentOwnershipHeaders(identity), + }); + + const firstAdmission = await client.send({ + initialData: { mode: VALIDATED_CONSTRUCTION_MODE }, + message: { kind: "user", body: constructionRequest }, + }); + await client.wait(firstAdmission); + + const completedCallIds = new Set(); + const clientToolResults: HeadlessPetrinautToolResult[] = []; + let stopReason = "settled"; + + const serviceClientCalls = async (clientRound: number): Promise => { + if (clientRound >= MAX_CLIENT_ROUNDS) return clientRound; + const snapshot = await client.history(); + const pendingCalls = pendingCallsFrom(snapshot, completedCallIds); + if (pendingCalls.length === 0) return clientRound; + + const results = await Promise.all( + pendingCalls.map((pendingCall) => petrinautClient.execute(pendingCall)), + ); + for (const result of results) { + completedCallIds.add(result.toolCallId); + clientToolResults.push(result); + } + + const admission = await client.send({ + message: { + kind: "signal", + type: CLIENT_TOOL_RESULT_SIGNAL, + tagName: CLIENT_TOOL_RESULT_SIGNAL, + body: JSON.stringify(results), + }, + }); + await client.wait(admission); + return serviceClientCalls(clientRound + 1); + }; + + const clientRounds = await serviceClientCalls(0); + const snapshot = await client.history(); + if (pendingCallsFrom(snapshot, completedCallIds).length > 0) { + stopReason = "client-round-limit"; + } + + const parsed = petrinautClient.parse(); + const validationRejections = validationRejectionsFrom(snapshot); + const callbackRejections = clientToolResults.filter( + (result) => "applied" in result.output && result.output.applied === false, + ); + const correctionClasses = [ + ...(validationRejections.length > 0 ? ["schema-validation"] : []), + ...(callbackRejections.length > 0 ? ["client-callback"] : []), + ]; + const toolNames = interviewerToolNamesFrom(snapshot); + const resourcePaths = skillResourcePathsFrom(snapshot); + const assistantText = latestAssistantTextFrom(snapshot); + const definition = petrinautClient.definition(); + const searchableDefinition = JSON.stringify(definition).toLowerCase(); + const arcs = definition.transitions.flatMap((transition) => [ + ...transition.inputArcs, + ...transition.outputArcs, + ]); + const changeoverCrewPlaceIds = new Set( + definition.places + .filter((place) => { + const identity = `${place.id} ${place.name}`.toLowerCase(); + return identity.includes("changeover") && identity.includes("crew"); + }) + .map((place) => place.id), + ); + const semanticInspection = { + hasPlacesAndTransitions: + definition.places.length > 0 && definition.transitions.length > 0, + hasExclusiveLineModes: ["white", "tint", "specialty"].every((mode) => + searchableDefinition.includes(mode), + ), + hasReturnedChangeoverCrew: definition.transitions.some((transition) => + transition.inputArcs.some( + (inputArc) => + inputArc.placeId !== undefined && + changeoverCrewPlaceIds.has(inputArc.placeId) && + transition.outputArcs.some( + (outputArc) => outputArc.placeId === inputArc.placeId, + ), + ), + ), + hasLineProductRestrictions: + searchableDefinition.includes("meridian") && + searchableDefinition.includes("ct-12") && + searchableDefinition.includes("ct-14"), + hasDirectionalWashdowns: + searchableDefinition.includes("white") && + searchableDefinition.includes("tint") && + searchableDefinition.includes("washdown"), + hasOnlyPositiveArcWeights: + arcs.length > 0 && arcs.every((arc) => arc.weight > 0), + namesIrLossesAndUnknowns: [ + "vw-02", + "idle", + "breakdown", + "commercial", + ].every((term) => assistantText.toLowerCase().includes(term)), + }; + const semanticFidelityOk = Object.values(semanticInspection).every(Boolean); + const record = { + startedAt: conversationId, + interviewerModel: process.env.BRUNCH_CHAT_MODEL, + sourceIrPath: irPath, + documentTitle: DOCUMENT_TITLE, + stopReason, + clientRounds, + toolNames, + resourcePaths, + clientToolResults, + validationRejections, + callbackRejections, + correctionCount: validationRejections.length + callbackRejections.length, + correctionClasses, + definition, + document: petrinautClient.document(), + parse: parsed.ok + ? { ok: true, hadMissingPositions: parsed.hadMissingPositions } + : { ok: false, error: parsed.error }, + assistantText, + semanticInspection, + semanticFidelityOk, + proofSatisfied: parsed.ok && semanticFidelityOk, + noInterviewTurns: true, + emittedFreeFormPnJson: assistantText.includes("```pn-json"), + wroteCaptureStore: false, + usage: { + turns: turnUsage, + inputTokens: totalFrom(turnUsage.map((turn) => turn.inputTokens)), + outputTokens: totalFrom(turnUsage.map((turn) => turn.outputTokens)), + totalTokens: totalFrom(turnUsage.map((turn) => turn.totalTokens)), + cost: totalFrom(turnUsage.map((turn) => turn.cost)), + }, + transcript: (await import("./flue-transcript.ts")).formatFlueTranscript( + snapshot, + ), + }; + + const artifactBase = `${outputDirectory}/${conversationId}`; + await writeFile( + `${artifactBase}.json`, + `${JSON.stringify(record, null, 2)}\n`, + ); + await writeFile( + `${artifactBase}.md`, + [ + `# Runbook validated construction ${conversationId}`, + "", + `- Parser accepted: ${record.parse.ok}`, + `- Semantic fidelity accepted: ${record.semanticFidelityOk}`, + `- Proof satisfied: ${record.proofSatisfied}`, + `- Corrections: ${record.correctionCount} (${record.correctionClasses.join(", ") || "none"})`, + `- Cost: ${record.usage.cost}`, + `- Client rounds: ${record.clientRounds}`, + "", + record.transcript, + "", + ].join("\n"), + ); + + process.stdout.write( + `RUNBOOK_VALIDATED_CONSTRUCTION_RESULT ${JSON.stringify({ + stopReason, + parseOk: parsed.ok, + semanticFidelityOk, + proofSatisfied: record.proofSatisfied, + correctionCount: record.correctionCount, + correctionClasses, + cost: record.usage.cost, + clientRounds, + toolNames, + resourcePaths, + emittedFreeFormPnJson: record.emittedFreeFormPnJson, + wroteCaptureStore: false, + artifactBase, + })}\n`, + ); +} finally { + petrinautClient.dispose(); + stopObserving(); + await application?.stop(); +} diff --git a/apps/brunch-agent/src/skills/sdcpn-modelling/SKILL.md b/apps/brunch-agent/src/skills/sdcpn-modelling/SKILL.md new file mode 100644 index 00000000000..7cfe680578c --- /dev/null +++ b/apps/brunch-agent/src/skills/sdcpn-modelling/SKILL.md @@ -0,0 +1,33 @@ +--- +name: sdcpn-modelling +description: Conduct a process-modelling interview, keep a structured Markdown IR, and construct a validated Petri net the expert can load. Use when someone wants a simulatable process model, a Petri net, or an interview that produces one. +--- + +# Lifecycle + +You own one looping lifecycle. Phases are modes of the same conversation, not handoffs. + +1. **Orient** — establish what the model must answer, for whom, with what time and accuracy, and what sits inside the boundary. +2. **Elicit** — interview in the expert's vocabulary. Read `elicitation.md` before asking substantive questions. Read `ir-template.md` when you first need to start or update the workpiece. +3. **Maintain the IR** — after each useful stretch, update the relevant IR sections. Emit the full current IR in a `runbook-ir` fenced block whenever you substantially change it, and always before construction. +4. **Construct** — only after a recoverable IR exists. Read `pn-construction.md` and `checks.md`. Infer the net from the IR, not by rereading the transcript as the primary model. When Petrinaut construction tools are mounted, use them for every net change and inspect the resulting definition instead of emitting net JSON. +5. **Check and deliver** — run the checks. Name inferences, approximations, defaults, omissions, and unrepresentable material. If a check exposes an IR gap, return to elicitation, amend the IR, and reconstruct. + +## Resource routing + +- Elicitation and IR maintenance: `elicitation.md`, `ir-template.md`. +- Construction and delivery: `pn-construction.md`, `checks.md`. +- Do not read construction material to frame ordinary interview questions. +- Do not interview through places, transitions, arcs, colours, tokens, or firing rules. + +## IR emission + +Whenever you emit the workpiece, use a fenced block whose language tag is exactly `runbook-ir`. The block is the full current document, not a delta. That block is how the conversation recovers the IR — there is no other store. + +## Return from construction + +If construction or checks show a hole the IR cannot fill, say what is missing, ask the smallest question that would fill it, update the IR, and only then regenerate. Do not invent a workflow engine or wait for a phase command. + +## Partial delivery + +When the expert stops, open no new topic. Deliver the best current IR and, if asked or already possible, the best current net, with gaps and assumptions named. diff --git a/apps/brunch-agent/src/skills/sdcpn-modelling/checks.md b/apps/brunch-agent/src/skills/sdcpn-modelling/checks.md new file mode 100644 index 00000000000..9ef86854436 --- /dev/null +++ b/apps/brunch-agent/src/skills/sdcpn-modelling/checks.md @@ -0,0 +1,52 @@ +# Completion and checks + +provenance: mixed — job/done/check adapted from Jetty; SDCPN validity and loss review from target-formalism teaching. + +## Elicitation sufficiency + +Enough for a first construction when: + +- at least one objective is named in the expert's terms; +- one concrete case has been walked end to end; +- the IR can locate goals, the process boundary, the main activities and their order, and the resources those activities contend for; +- unknowns, assumptions, and omissions are visible rather than silently filled. + +Not enough: a fluent conversation, a stable-looking IR with empty demanded sections, or headings filled by your inference without an assumption mark. + +## IR checks + +Before constructing: + +- every section is present; +- no precise quantity or rule appears without either an expert source or an **Assumed** mark; +- conflicts are listed rather than averaged; +- construction could proceed without inventing a missing spine (what flows, what happens to it, in what order). + +If a check fails, fix the IR or return to elicitation. Three tries, then deliver the partial result and say why. + +## PN validity + +Use `getLatestNetDefinition` to inspect the client-owned result. Every change +must have passed the mounted Petrinaut tool schema; correct any rejection +before delivery. Check that the net has at least one place and transition, all +arc weights are positive, shared resources are returned where the IR says they +are reserved rather than consumed, exclusive modes are structurally exclusive, +and the activities have the order the IR claims. Missing canvas positions are +allowed. + +A net that cannot run because order was never stated is a failed check, not a styling issue. + +## Loss and uncertainty review + +The delivery names: + +- inferences and approximations used in construction; +- defaults you introduced; +- omissions the objective permitted; +- material the net cannot hold. + +Do not silently harden a hedge into a number. + +## Stopping outcomes + +Name one: `complete-enough-to-parse`, `partial-with-named-gaps`, `unsupported-objective`, `expert-stopped`, `returned-to-elicitation`. diff --git a/apps/brunch-agent/src/skills/sdcpn-modelling/elicitation.md b/apps/brunch-agent/src/skills/sdcpn-modelling/elicitation.md new file mode 100644 index 00000000000..3325eae4e4c --- /dev/null +++ b/apps/brunch-agent/src/skills/sdcpn-modelling/elicitation.md @@ -0,0 +1,204 @@ +# Elicitation teaching + +Merged universal interviewing judgment and SDCPN target-formalism investigation. No concrete plant, fleet, or fab belongs here. + +## Purpose and outcome + +provenance: sdcpn + +Interview someone who knows an operational system and is not a modeller. Derive a process description a simulation can run. The model must answer the questions they actually have, in their vocabulary, with every load-bearing value traceable to something they said or marked assumed. Where their knowledge stops, the IR says so. + +You do not build the net during the interview. + +## Lifecycle and elicitation approach + +### Posture, appetite, budget, boundary, and horizon + +provenance: universal + +From the first exchanges, take time available, what the model is for, how confident it must be, and how far they will tolerate proposed assumptions. These set stance; they are not a form. + +Establish what is inside, what is outside, why that boundary serves the objective, and how far in time the model must remain useful — before asking how the system is built. + +Ask what they may vary, what response decides success, and what observation would make the result accurate enough. + +### Questioning and deepening + +provenance: universal + +- Objectives before structure. A bounded three-to-six-step account begins the slice; do not request a diagram. +- Walk one real case from arrival to leaving before sweeping a property across many cases. +- Prefer "when did that last happen, and what did you do?" to a generalisation. +- Never ask "why do you do it this way?" as the primary probe. Ask for an occasion and what was attended to. +- Vague terms ("usually", "roughly", "mostly fine") hide a distribution or an exception. Deepen before recording. +- Normative language ("we would", "the rule is") is policy, not practice. Ask when that last actually happened. +- After a substantive answer, ask how they would know — what they are actually looking at. +- Before a quantity, ask whether the typical case or the bad one matters. Then typical, then one-in-ten worse, then one-in-ten better. Do not ask min / most-likely / max. +- A memorable incident is not a rate. Ask how many opportunities and over what period. +- Restate in your words for correction; capture their settled wording, not bare assent to yours. +- When two answers tension, say so and ask. Do not pick one silently. +- Batch two to four related survey questions only when they share a frame. Probe one thread when deepening. An opening battery is a failure. + +### Evidence and uncertainty + +provenance: universal + +You may propose an assumption to unblock, stated as yours, entered in the IR with why and how to check. You may never let it pass as theirs. + +You may defer a topic only by recording what is missing, why, and where it would come from. + +A value the expert did not give must not appear as theirs. Find the words, mark it assumed, or drop it. + +### Prioritization and return paths + +provenance: universal + +Walk one case, then ask one property across what that case revealed. Return to a new case when a sweep exposes one the first slice missed. + +When several turns produce nothing new, change technique — a story, a contrast, absences — rather than more of the same. + +Depth is objective-relative. Do not probe a thread that no stated question depends on. + +When appetite is high, follow the slice. When time is tight, synthesise and invite correction. + +### Stopping and partial delivery + +provenance: universal + +Before delivering, summarise, state what is missing or assumed, and give one chance to correct. Do not end because they seem busy; name what is still missing and let them choose. When they stop, open no new topic. + +A fluent conversation is not completion. + +## What to investigate + +provenance: sdcpn — situation typologies, not a questionnaire to read aloud. + +### Goals, constraints, measures, and thresholds + +What the process seeks to achieve or avoid; how each is measured; what factors affect whether they are reached; numerical thresholds they can actually judge (desired or tolerated probability, quantities to keep above or below). + +### Process boundary, triggers, and prerequisites + +What starts a case: schedule, receipt, threshold crossing, or event. What else is required — instructions, approval, a resource being free. + +### Participants, locations, and resources + +Who is involved and what they decide. Which places matter and how they relate. Which resources are capped. Properties that change what the process does. + +A machine, team, or bay named in passing is often a contended resource whose rule the expert has least examined. + +### Activities, inputs, outputs, and resource usage + +For each discrete step, in their words: inputs and whether each is consumed, reserved and later released, or only read; whether the step takes time; whether it can fail and what happens then. + +### Flow, branching, retries, failures, and recovery + +How steps relate. Unhappy paths and the conditions that enter them. What happens to the work in hand, to the case, and what recovery looks like. + +### Time, quantities, and stochastic behavior + +Durations, rates, arrivals, scrap, queues implied by waiting. Typical versus tail. Whether a quantity varies by type of thing. + +### Policies, exceptions, and practiced rules + +Who wins a contended resource. What a document says versus what people do. Unwritten rules: what a newcomer gets wrong; what is always or never done that is written nowhere. + +### Validation criteria + +What observation or replay would make the result accurate enough. Do not ask the expert to predict the model's answer and store that prediction as structure. + +## Target-formalism guidance + +### Lenses + +provenance: sdcpn, kinds stripped + +- **"It depends"** hides a branch, a decision rule, or a quantity that varies by type. Ask which. +- **"Sometimes it breaks" / "we have to wait"** is an event with a rate and a duration, or an input the system does not control. First accounts omit both. +- Warming up, wearing down, filling: something changing while nothing discrete happens, or a mode change with a loss. Ask the rate or the threshold that matters. +- **"Always" and "never"** are constraints or policies. Ask what enforces them and whether an exception has overridden them. +- A duration that crosses a calendar boundary depends on availability, not only on the work. + +### Situation typologies + +Each pattern below is a question shape, not a node type to assign. + +#### Timed work + +- Notice when: a step takes time, or time is what the objective cares about. +- Information needed: start, finish, what is occupied while it runs, typical duration and a tail if the tail matters. +- Questions that may help: last time it ran; how long it usually takes; one time in ten, worse than. +- Record in the IR: under activities and under time. +- Transform to PN: when constructing, a start / in-progress / done shape. Not during the interview. +- Caveats: do not force a distribution the expert cannot observe. +- Checks: duration has a source or an assumption mark. + +#### Probabilistic or branching outcome + +- Notice when: success is not guaranteed, or two different next steps can follow. +- Information needed: what decides the branch; roughly how often; what each path produces. +- Questions that may help: last failure; what you do then; is that rare or ordinary. +- Record in the IR: flow / failures / recovery. +- Transform to PN: alternative outgoing paths. Not during the interview. +- Caveats: one vivid incident is not a probability. +- Checks: both paths named, or the missing one marked unknown. + +#### Contended resource + +- Notice when: two bits of work want the same people, machine, or bay. +- Information needed: how many instances; who wins; what overrides; a recent borderline case. +- Questions that may help: what happens when two lines want the crew at once. +- Record in the IR: resources and policies. +- Transform to PN: a shared token or equivalent. Not during the interview. +- Caveats: do not infer the rule from a schedule. +- Checks: the practiced rule is recorded, or marked unknown. + +#### Threshold trigger + +- Notice when: something proceeds because a level, count, or clock crossed a line. +- Information needed: the observable; who or what flips it; what it starts or stops. +- Questions that may help: what do you actually look at; what would be unacceptable. +- Record in the IR: triggers and thresholds. +- Transform to PN: a guard or a continuous variable with a crossing. Not during the interview. +- Caveats: a continuous quantity that triggers nothing usually does not belong. +- Checks: the trigger is observable in their world. + +#### Mode change + +- Notice when: setup, changeover, restart, warm-up, handover. +- Information needed: what is lost in the change; whether loss depends on direction. +- Questions that may help: last changeover; what you cannot run next. +- Record in the IR: activities and policies. +- Transform to PN: a timed or costly transition between modes. +- Caveats: ask before recording "not applicable". +- Checks: loss components named or marked unknown. + +#### Grouped movement + +- Notice when: work moves in batches, runs, lots, or loads. +- Information needed: what the group is; whether it must stay together; what a split costs. +- Record in the IR: flow and policies. + +### Caveats and rabbit holes + +provenance: mixed + +- Schema-shaped questioning: following IR headings instead of their thread. Coverage looks orderly; tacit distinctions stay hidden. +- Building the net in conversation. Places and transitions buy nothing and cost their vocabulary. +- Structure before any objective is on record. +- Treating a document as practice. +- Whole-model restatement as progress. Local restatement for correction; one read-back at close. +- Asking them to invent weights they do not use. + +### Failure modes + +provenance: universal + +- Silent hardening: a hedge becomes a precise value without a clarification turn. +- Invented content: a load-bearing element with no words from them and no assumption mark. +- Never-asked coverage blindness: a needed topic never addressed. +- Opening overload. +- Unresolved ambiguity bypassed into one precise claim. +- Unlicensed influence: assent to your phrasing treated as their content. +- Premature accommodation: a burden cue ends the interview with holes unnamed. +- Deferral without a deposit. diff --git a/apps/brunch-agent/src/skills/sdcpn-modelling/ir-template.md b/apps/brunch-agent/src/skills/sdcpn-modelling/ir-template.md new file mode 100644 index 00000000000..4a5ec6be938 --- /dev/null +++ b/apps/brunch-agent/src/skills/sdcpn-modelling/ir-template.md @@ -0,0 +1,75 @@ +# Runbook IR template + +provenance: mixed — section homes are structural; contents stay prose. + +This is the workpiece. Fill it during elicitation. Construction consumes this document, not the transcript. Update one section without rewriting the whole file when you can; when you emit, emit the full current document. + +Do not read these headings aloud as a questionnaire. Follow the expert's thread; file what you learn here. + +Mark every unsettled item in place: + +- **Unknown** — asked, the expert does not know. +- **Not yet asked** — relevant, not yet reached. +- **Assumed** — you supplied it; say why and how to check. +- **Conflict** — two accounts disagree; keep both. +- **Omitted** — deliberately left out, and why the objective permits it. +- **Loss** — something the net cannot represent. + +```markdown +# Runbook IR + +## Purpose and outcome + +### What the model must answer + +### Who it is for + +### What it must not claim + +## Posture + +### Appetite, time, and accuracy + +### Boundary and horizon + +## Goals, constraints, measures, and thresholds + +## Process boundary, triggers, and prerequisites + +## Participants, locations, and resources + +## Activities, inputs, outputs, and resource usage + +## Flow, branching, retries, failures, and recovery + +## Time, quantities, and stochastic behavior + +## Policies, exceptions, and practiced rules + +## Validation criteria + +## Situation notes + +Repeat as needed. Each note: + +### + +#### Notice when + +#### What we know + +#### Open questions + +#### Record for construction + +## Unknowns, assumptions, conflicts, and omissions + +## Projection losses +``` + +## Maintenance + +- Prefer the expert's words for names of things. +- A restatement you offered is not their statement until they settle the wording. +- When a later answer corrects an earlier one, replace the earlier content and note the supersession in the conflicts/omissions section rather than leaving two competing facts in the same heading. +- Empty sections stay present with `Not yet asked` or `Omitted` so construction can see the hole. diff --git a/apps/brunch-agent/src/skills/sdcpn-modelling/pn-construction.md b/apps/brunch-agent/src/skills/sdcpn-modelling/pn-construction.md new file mode 100644 index 00000000000..a983ebb0291 --- /dev/null +++ b/apps/brunch-agent/src/skills/sdcpn-modelling/pn-construction.md @@ -0,0 +1,93 @@ +# PN construction + +provenance: sdcpn + +Read this only when constructing or checking a net. Consume the filled runbook IR. Do not treat the transcript as the primary model. + +## Mapping principles + +- Things that wait, hold, or are available become places. +- Things that happen become transitions. +- Order, branching, and triggers become arcs and, where needed, guards. +- A type of thing the process treats differently may become a colour; only when the IR says the distinction changes what happens. +- Continuous change while nothing discrete happens may become dynamics on a place. +- Shared resources become tokens that are reserved and released, not consumed for good, unless the IR says they are used up. + +Missing canvas positions are acceptable. Prefer a net the parser accepts over a pretty layout. + +When Petrinaut construction tools are mounted, their generated schemas are the +only authority for payload fields: + +1. Call `getLatestNetDefinition` before constructing. +2. Add only IR-supported token types and tunable parameters with `addType` and + `addParameter`. +3. Add places and transitions with `addPlace` and `addTransition`. Establish + their stable IDs before connecting them. +4. Add every connection with `addArc`. Arc weights are positive token + multiplicities; a zero-weight branch is not an exclusive mode. +5. Call `getLatestNetDefinition` after each dependent stage and once at the end. + Correct rejected calls in the same conversation. + +Do not emit a `pn-json` block or reproduce the resulting definition as +free-form JSON. The validated tool calls and the client's final definition are +the construction artifact. + +Name every inference. If the IR does not support a place, transition, or arc, do not invent a silent default — omit it and list the loss, or mark the default in the delivery. + +## Reusable construction patterns + +### Timed work + +When the IR records a step that occupies time: + +1. A start transition that may sample duration onto a token field. +2. An in-progress place (dynamics may count down remaining time). +3. A done transition that waits until remaining time is gone. + +If the IR only has a typical duration and no tail, keep a constant or a named parameter and say so. + +### Branching or probabilistic outcome + +A start that records a sampled or decided outcome; then two (or more) completions with exclusive conditions. If the IR has no rate, do not invent 50/50 — use a named parameter or omit the probability and list the loss. + +### Contended resource + +A place holding the free instances. The work's start consumes (reserves) one; the work's end returns it, possibly worn. The practiced contention rule becomes a guard or a priority if the IR stated one; otherwise name the missing rule as a loss. + +### Threshold trigger + +A place carrying the quantity; a transition that fires when the IR's observable is crossed; another that resets it if the IR named a reset. If nothing is triggered, do not add a floating continuous variable. + +### Mode change + +A transition between two availability or setup places. Put directional loss on that transition if the IR recorded it. + +### Grouped movement + +A formation transition that waits for a count or a clock; a place for the formed group; a split cost if the IR said splitting is expensive. + +## Inference and approximation + +Allowed if named: + +- collapsing several named micro-steps into one transition when the objective does not depend on the internals; +- treating an unstated return of a reserved resource as "released as it arrived"; +- using a parameter for an unknown rate. + +Not allowed: + +- filling an empty IR section from general knowledge of plants or logistics; +- averaging two conflicting accounts; +- turning "unknown" into a typical textbook distribution. + +## Projection loss + +The net cannot honestly hold: qualitative objectives without a metric, unwritten political weights, data bindings not yet connected, and any practiced rule whose condition the expert could not name. Keep those in the IR's loss section and name them in the construction delivery. + +## Worked examples + +Typology-shaped only. + +**Timed work, no plant.** IR says "inspection takes about twenty minutes, sometimes an hour if the lab is backed up." Construction: start / in-progress / finish; duration a spread or a typical-plus-tail parameter; lab backup named as a contended resource if the IR recorded the lab, otherwise a loss. + +**Contended crew.** IR says two jobs can want the same two-person crew, and when that happens one waits. Construction: a place with two tokens; both job-starts reserve; no invented priority if none was stated. diff --git a/apps/brunch-agent/src/tools/petrinaut-construction.ts b/apps/brunch-agent/src/tools/petrinaut-construction.ts new file mode 100644 index 00000000000..87a6931bbda --- /dev/null +++ b/apps/brunch-agent/src/tools/petrinaut-construction.ts @@ -0,0 +1,96 @@ +import { defineTool } from "@flue/runtime"; +import * as v from "valibot"; + +import { petrinautAiTools } from "@hashintel/petrinaut-core/ai"; + +import { AWAITING_CLIENT } from "../client-tool.ts"; + +export const VALIDATED_CONSTRUCTION_MODE = "validated-construction"; + +export const PETRINAUT_CONSTRUCTION_TOOL_NAMES = [ + "getLatestNetDefinition", + "addType", + "addParameter", + "addPlace", + "addTransition", + "addArc", +] as const satisfies readonly (keyof typeof petrinautAiTools)[]; + +export type PetrinautConstructionToolName = + (typeof PETRINAUT_CONSTRUCTION_TOOL_NAMES)[number]; + +const issuePathFrom = ( + input: Record, + path: readonly PropertyKey[], +): [v.IssuePathItem, ...v.IssuePathItem[]] | undefined => { + if (path.length === 0) return undefined; + let current: unknown = input; + return path.map((key) => { + const parent = current; + const value = + typeof parent === "object" && parent !== null + ? (parent as Record)[key] + : undefined; + current = value; + return { + type: "unknown" as const, + origin: "value" as const, + input: parent, + key, + value, + }; + }) as [v.IssuePathItem, ...v.IssuePathItem[]]; +}; + +const canonicalInputFor = (toolName: PetrinautConstructionToolName) => { + const canonicalTool = petrinautAiTools[toolName]; + const jsonSchema = canonicalTool.inputSchema.toJSONSchema(); + + return { + description: [ + canonicalTool.description, + "Canonical Petrinaut input JSON Schema:", + JSON.stringify(jsonSchema), + ].join("\n"), + schema: v.pipe( + v.looseObject({}), + v.rawTransform((context) => { + const parsed = canonicalTool.inputSchema.safeParse( + context.dataset.value, + ); + if (parsed.success) return parsed.data; + + for (const issue of parsed.error.issues) { + context.addIssue({ + message: issue.message, + path: issuePathFrom(context.dataset.value, issue.path), + }); + } + return context.NEVER; + }), + ), + }; +}; + +const awaitingClientOutput = v.object({ + awaiting: v.literal(AWAITING_CLIENT), +}); + +const definePetrinautConstructionTool = ( + toolName: PetrinautConstructionToolName, +) => { + const canonicalInput = canonicalInputFor(toolName); + return defineTool({ + name: toolName, + description: canonicalInput.description, + input: canonicalInput.schema, + output: awaitingClientOutput, + run() { + return { output: { awaiting: AWAITING_CLIENT }, terminate: true }; + }, + }); +}; + +export const petrinautConstructionTools = PETRINAUT_CONSTRUCTION_TOOL_NAMES.map( + definePetrinautConstructionTool, +); diff --git a/libs/@hashintel/brunch-agent/packages/core/test/architecture/boundaries.test.ts b/apps/brunch-agent/test/architecture/boundaries.integration.ts similarity index 96% rename from libs/@hashintel/brunch-agent/packages/core/test/architecture/boundaries.test.ts rename to apps/brunch-agent/test/architecture/boundaries.integration.ts index 749fbaccecd..76d2921e820 100644 --- a/libs/@hashintel/brunch-agent/packages/core/test/architecture/boundaries.test.ts +++ b/apps/brunch-agent/test/architecture/boundaries.integration.ts @@ -492,6 +492,12 @@ describe("the HASH smoke is runnable without a model key or a network (spec §12 "Types Flue conversation-stream chunks so the AI SDK projector can be unit-tested; the import is type-only — no provider key, no socket, no model call, no runtime boot.", "apps/brunch-agent/test/petrinaut-chat.integration.ts": "Boots the plain Flue chat agent on Flue's node runtime with pi-ai's faux provider, drives the committed /api/chat door over app.fetch, and proves streamed reasoning/text, one server tool, one stub skill activation, one read-only client-tool resume, GET history ownership, SQLite restart, and harness-side idempotent apply-sweep into a capture store keyed by Flue conversation identity — no provider key, no socket, no extraction model call. Run as a child process by petrinaut-chat.test.ts.", + "apps/brunch-agent/test/runbook-artifacts.test.ts": + "Types Flue's public conversation snapshot so runbook artifact recovery can be unit-tested; the import is type-only — no provider key, no socket, no model call, no runtime boot.", + "apps/brunch-agent/test/runbook-elicitation-faux-provider.ts": + "Defines the scripted pi-ai faux provider loaded only by the hermetic prospective-runner test — no provider key, no socket, and no network model call.", + "apps/brunch-agent/test/runbook-headless.integration.ts": + "Boots the built Flue ChatAgent with pi-ai's faux provider and a headless Petrinaut client to prove validated construct-only tool flow without a provider key, socket, or network model call.", "apps/brunch-agent/test/turn-timing.test.ts": "Types recorded Flue observations and model requests so the condition-5 purpose splitter can be unit-tested; the import is type-only — no provider key, no socket, no model call, no runtime boot.", }; diff --git a/libs/@hashintel/brunch-agent/packages/core/test/architecture/workspace.test.ts b/apps/brunch-agent/test/architecture/workspace.test.ts similarity index 100% rename from libs/@hashintel/brunch-agent/packages/core/test/architecture/workspace.test.ts rename to apps/brunch-agent/test/architecture/workspace.test.ts diff --git a/libs/@hashintel/brunch-agent/packages/core/test/architecture/workspace.ts b/apps/brunch-agent/test/architecture/workspace.ts similarity index 99% rename from libs/@hashintel/brunch-agent/packages/core/test/architecture/workspace.ts rename to apps/brunch-agent/test/architecture/workspace.ts index 0a2952dd77c..35633612110 100644 --- a/libs/@hashintel/brunch-agent/packages/core/test/architecture/workspace.ts +++ b/apps/brunch-agent/test/architecture/workspace.ts @@ -17,7 +17,7 @@ import { fileURLToPath } from "node:url"; /** Resolved through `fileURLToPath` — a raw `URL.pathname` is percent-encoded. */ export const HASH_ROOT = fileURLToPath( - new URL("../../../../../../..", import.meta.url), + new URL("../../../..", import.meta.url), ).replace(/[/\\]$/, ""); export const CONTEXT_ROOT = join(HASH_ROOT, "libs/@hashintel/brunch-agent"); export const REPO_ROOT = CONTEXT_ROOT; diff --git a/apps/brunch-agent/test/build-artifact.test.ts b/apps/brunch-agent/test/build-artifact.test.ts index c668c722338..14a8775ba43 100644 --- a/apps/brunch-agent/test/build-artifact.test.ts +++ b/apps/brunch-agent/test/build-artifact.test.ts @@ -105,6 +105,15 @@ describe("the emitted server bundle", () => { expect(bundle).toContain(".data-wipe-me"); // db.ts's default store path }); + test("packages the authored skill without the retired filesystem loader", () => { + expect(bundle).toContain("createSkillReference"); + expect(bundle).toContain("skill:sdcpn-modelling:"); + expect(bundle).toContain("sdcpn-modelling"); + expect(bundle).not.toContain("splitSkillMarkdown"); + expect(bundle).not.toContain("skillFileUrl"); + expect(bundle).not.toContain("./sdcpn-modelling/SKILL.md"); + }); + test("carries no model key", () => { const modelKey = new RegExp( `${"ANTHROPIC"}_${"API"}_${"KEY"}\\s*[:=]\\s*['"][^'"]+['"]`, diff --git a/apps/brunch-agent/test/headless-petrinaut-client.test.ts b/apps/brunch-agent/test/headless-petrinaut-client.test.ts new file mode 100644 index 00000000000..04ac82d2a72 --- /dev/null +++ b/apps/brunch-agent/test/headless-petrinaut-client.test.ts @@ -0,0 +1,128 @@ +import { describe, expect, test } from "vitest"; + +import { createHeadlessPetrinautClient } from "../src/headless-petrinaut-client"; + +describe("the headless Petrinaut client", () => { + test("constructs a parser-accepted document through the bounded callbacks", async () => { + const client = createHeadlessPetrinautClient( + "Validated construction proof", + ); + const calls = [ + { + toolCallId: "type", + toolName: "addType", + input: { + id: "order_type", + name: "Order", + iconSlug: "circle", + displayColor: "#808080", + elements: [], + }, + }, + { + toolCallId: "parameter", + toolName: "addParameter", + input: { + id: "washdown_hours", + name: "Washdown hours", + variableName: "washdown_hours", + type: "real", + defaultValue: "3", + }, + }, + { + toolCallId: "place", + toolName: "addPlace", + input: { + id: "line_idle", + name: "LineIdle", + colorId: null, + dynamicsEnabled: false, + differentialEquationId: null, + x: 0, + y: 0, + }, + }, + { + toolCallId: "transition", + toolName: "addTransition", + input: { + id: "start_run", + name: "Start run", + inputArcs: [], + outputArcs: [], + lambdaType: "predicate", + lambdaCode: "return true;", + transitionKernelCode: "", + x: 0, + y: 0, + }, + }, + { + toolCallId: "input-arc", + toolName: "addArc", + input: { + transitionId: "start_run", + arcDirection: "input", + placeId: "line_idle", + weight: 1, + }, + }, + { + toolCallId: "output-arc", + toolName: "addArc", + input: { + transitionId: "start_run", + arcDirection: "output", + placeId: "line_idle", + weight: 1, + }, + }, + ]; + + try { + for (const call of calls) { + // oxlint-disable-next-line no-await-in-loop -- each mutation depends on the prior document state. + await expect(client.execute(call)).resolves.toMatchObject({ + output: { applied: true }, + }); + } + + expect(client.definition()).toMatchObject({ + types: [{ id: "order_type" }], + parameters: [{ id: "washdown_hours" }], + places: [{ id: "line_idle" }], + transitions: [ + { + id: "start_run", + inputArcs: [{ placeId: "line_idle", weight: 1 }], + outputArcs: [{ placeId: "line_idle", weight: 1 }], + }, + ], + }); + expect(client.document().title).toBe("Validated construction proof"); + expect(client.parse()).toMatchObject({ ok: true }); + } finally { + client.dispose(); + } + }); + + test("refuses tools outside the side-quest subset", async () => { + const client = createHeadlessPetrinautClient( + "Validated construction proof", + ); + try { + const result = await client.execute({ + toolCallId: "remove", + toolName: "removePlace", + input: { placeId: "line_idle" }, + }); + expect(result.output).toMatchObject({ applied: false }); + expect("error" in result.output ? result.output.error : "").toContain( + "does not allow removePlace", + ); + } finally { + client.dispose(); + } + }); +}); diff --git a/apps/brunch-agent/test/petrinaut-chat-result.ts b/apps/brunch-agent/test/petrinaut-chat-result.ts index eaad4a673a1..761d4fade07 100644 --- a/apps/brunch-agent/test/petrinaut-chat-result.ts +++ b/apps/brunch-agent/test/petrinaut-chat-result.ts @@ -37,6 +37,10 @@ export interface PetrinautChatResult { UIMessageChunk, { type: "tool-input-available" } > | null; + readonly readSkillResourceCall: Extract< + UIMessageChunk, + { type: "tool-input-available" } + > | null; readonly interviewerToolNames: readonly string[]; readonly captureUserText: string; readonly captureIds: readonly string[]; diff --git a/apps/brunch-agent/test/petrinaut-chat.integration.ts b/apps/brunch-agent/test/petrinaut-chat.integration.ts index 6b69f80239f..20f70775a09 100644 --- a/apps/brunch-agent/test/petrinaut-chat.integration.ts +++ b/apps/brunch-agent/test/petrinaut-chat.integration.ts @@ -10,15 +10,9 @@ import { fauxThinking, fauxToolCall, } from "@earendil-works/pi-ai"; -import { sqlite, start } from "@flue/runtime/node"; +import { setProvider } from "@flue/runtime"; import { createFlueClient, FlueApiError } from "@flue/sdk"; -import { - ACTIVATE_SKILL_TOOL_NAME, - CHAT_MODEL_ID, - ChatAgent, - STUB_SKILL_NAME, -} from "../src/agents/chat-agent.ts"; import { applyCaptureSweep } from "../src/capture-sweep.ts"; import { CLIENT_TOOL_RESULT_SIGNAL } from "../src/client-tool.ts"; import { @@ -26,6 +20,7 @@ import { flueConversationIdFrom, } from "../src/conversation-identity.ts"; import { formatFlueTranscript } from "../src/flue-transcript.ts"; +import { loadBuiltBrunchApplication } from "../src/load-built-application.ts"; import { CHAT_AGENT_ROUTE } from "../src/routes.ts"; import { PING_TOOL_NAME } from "../src/tools/ping.ts"; import { READ_PETRINAUT_DOC_TOOL_NAME } from "../src/tools/read-petrinaut-doc.ts"; @@ -36,6 +31,11 @@ import type { } from "./petrinaut-chat-result"; import type { UIMessageChunk } from "ai"; +const ACTIVATE_SKILL_TOOL_NAME = "activate_skill"; +const CHAT_MODEL_ID = "claude-haiku-4-5"; +const RUNBOOK_SKILL_NAME = "sdcpn-modelling"; +const READ_SKILL_RESOURCE_TOOL_NAME = "read_skill_resource"; + const principalKey = "principal-mission-1"; const conversationId = "conversation-mission-1"; const identity = { principalKey, conversationId }; @@ -47,6 +47,8 @@ const dbFile = dbPath.endsWith(".db") ? dbPath : join(dbPath, "conversations.db"); +process.env.BRUNCH_CHAT_MODEL = CHAT_MODEL_ID; +process.env.BRUNCH_DEV_DB_PATH = dbFile; process.env.BRUNCH_TRANSPORT_AISDK_INSPECT ??= "1"; const chunksFrom = (body: string): UIMessageChunk[] => @@ -73,15 +75,11 @@ const faux = fauxProvider({ provider: "anthropic", models: [{ id: CHAT_MODEL_ID, reasoning: true }], }); - -const flue = await start({ - agents: [ChatAgent], - providers: [faux.provider], - db: sqlite(dbFile), -}); +setProvider(faux.provider); +const application = await loadBuiltBrunchApplication(); try { - const { default: app } = await import("../src/app.ts"); + const app = application; const appTransport: typeof fetch = async (input, init) => app.fetch(input instanceof Request ? input : new Request(input, init)); const historyClient = createFlueClient({ @@ -114,18 +112,50 @@ try { }; process.stdout.write(`PETRINAUT_RESUME_RESULT ${JSON.stringify(result)}\n`); } else { + const packagedSkillResourcePathFrom = ( + context: unknown, + fileName: string, + ): string => { + const serialized = JSON.stringify(context); + const match = serialized.match( + new RegExp( + `/\\.flue/packaged-skills/[^"\\s\\\\]+/${fileName.replace(".", "\\.")}`, + ), + ); + if (match === null) { + throw new Error( + `activate_skill briefing did not advertise ${fileName}`, + ); + } + return match[0]; + }; + faux.setResponses([ fauxAssistantMessage( [ - fauxThinking("Load the mount confirmation skill."), + fauxThinking("Load the modelling runbook skill."), fauxToolCall( ACTIVATE_SKILL_TOOL_NAME, - { name: STUB_SKILL_NAME }, + { name: RUNBOOK_SKILL_NAME }, { id: "tool-skill-1" }, ), ], { stopReason: "toolUse" }, ), + (context) => + fauxAssistantMessage( + [ + fauxThinking("Read elicitation teaching from the skill package."), + fauxToolCall( + READ_SKILL_RESOURCE_TOOL_NAME, + { + path: packagedSkillResourcePathFrom(context, "elicitation.md"), + }, + { id: "tool-resource-1" }, + ), + ], + { stopReason: "toolUse" }, + ), fauxAssistantMessage( [ fauxThinking("Confirm the server path, then read the guide."), @@ -209,6 +239,14 @@ try { chunk.type === "tool-input-available" && chunk.toolName === ACTIVATE_SKILL_TOOL_NAME, ) ?? null; + const readSkillResourceCall = + initialChunks.find( + ( + chunk, + ): chunk is Extract => + chunk.type === "tool-input-available" && + chunk.toolName === READ_SKILL_RESOURCE_TOOL_NAME, + ) ?? null; const pingOutputChunk = initialChunks.find( (chunk) => chunk.type === "tool-output-available" && @@ -310,8 +348,16 @@ try { message.purpose === "dispatch" && message.signal?.tagName === CLIENT_TOOL_RESULT_SIGNAL, ).length; - const firstSweep = await applyCaptureSweep(identity, userEntryIds); - const secondSweep = await applyCaptureSweep(identity, userEntryIds); + const firstSweep = await applyCaptureSweep( + identity, + userEntryIds, + appTransport, + ); + const secondSweep = await applyCaptureSweep( + identity, + userEntryIds, + appTransport, + ); const interviewerToolNames = [ ...new Set( snapshot.messages.flatMap((message) => @@ -422,6 +468,7 @@ try { instanceId, dbPath: dbFile, activateSkillCall, + readSkillResourceCall, interviewerToolNames, captureUserText: userTextFromHistory( snapshot.messages.map((message) => ({ @@ -438,5 +485,5 @@ try { process.stdout.write(`PETRINAUT_CHAT_RESULT ${JSON.stringify(result)}\n`); } } finally { - await flue.stop(); + await application.stop(); } diff --git a/apps/brunch-agent/test/petrinaut-chat.test.ts b/apps/brunch-agent/test/petrinaut-chat.test.ts index aa5ac79bff7..b252a487cc4 100644 --- a/apps/brunch-agent/test/petrinaut-chat.test.ts +++ b/apps/brunch-agent/test/petrinaut-chat.test.ts @@ -99,19 +99,38 @@ test("the committed /api/chat door streams a plain Flue agent through server and expect(result.transcript).toContain("tool ping"); expect(result.transcript).toContain("tool readPetrinautDoc"); expect(result.transcript).toContain("tool activate_skill"); + expect(result.transcript).toContain("tool read_skill_resource"); expect(result.transcript).toContain( "The assistant can read its own documentation pages.", ); expect(result.activateSkillCall).toMatchObject({ type: "tool-input-available", toolName: "activate_skill", - input: { name: "confirm-path" }, + input: { name: "sdcpn-modelling" }, }); + expect(result.readSkillResourceCall).toMatchObject({ + type: "tool-input-available", + toolName: "read_skill_resource", + }); + expect(JSON.stringify(result.readSkillResourceCall?.input ?? {})).toContain( + "elicitation.md", + ); expect(result.interviewerToolNames).toContain("activate_skill"); + expect(result.interviewerToolNames).toContain("read_skill_resource"); expect(result.interviewerToolNames).toContain("ping"); expect(result.interviewerToolNames).toContain("readPetrinautDoc"); expect(result.interviewerToolNames).not.toContain("sweep"); expect(result.interviewerToolNames).not.toContain("brunch_sweep"); + expect(result.interviewerToolNames).not.toEqual( + expect.arrayContaining([ + "getLatestNetDefinition", + "addType", + "addParameter", + "addPlace", + "addTransition", + "addArc", + ]), + ); expect(result.captureIds.length).toBe(1); expect(result.captureExcerpts).toEqual([ "Run the FE-1435 transport probe.", @@ -181,6 +200,7 @@ test("the committed /api/chat door streams a plain Flue agent through server and expect(resumeResult.transcript).toContain("tool ping"); expect(resumeResult.transcript).toContain("tool readPetrinautDoc"); expect(resumeResult.transcript).toContain("tool activate_skill"); + expect(resumeResult.transcript).toContain("tool read_skill_resource"); } finally { await rm(dbDirectory, { recursive: true, force: true }); } diff --git a/apps/brunch-agent/test/petrinaut-construction-tools.test.ts b/apps/brunch-agent/test/petrinaut-construction-tools.test.ts new file mode 100644 index 00000000000..1ca9cb0e53a --- /dev/null +++ b/apps/brunch-agent/test/petrinaut-construction-tools.test.ts @@ -0,0 +1,81 @@ +import * as v from "valibot"; +import { describe, expect, test } from "vitest"; + +import { petrinautAiTools } from "@hashintel/petrinaut-core/ai"; + +import { + PETRINAUT_CONSTRUCTION_TOOL_NAMES, + petrinautConstructionTools, +} from "../src/tools/petrinaut-construction"; + +const toolByName = (toolName: string) => { + const constructionTool = petrinautConstructionTools.find( + (candidateTool) => candidateTool.name === toolName, + ); + if (!constructionTool) + throw new Error(`Missing construction tool ${toolName}`); + return constructionTool; +}; + +describe("Petrinaut construction tools", () => { + test("exposes exactly the bounded canonical subset", () => { + expect(petrinautConstructionTools.map((tool) => tool.name)).toEqual([ + ...PETRINAUT_CONSTRUCTION_TOOL_NAMES, + ]); + }); + + test("mechanically carries the canonical input contract", () => { + for (const toolName of PETRINAUT_CONSTRUCTION_TOOL_NAMES) { + const constructionTool = toolByName(toolName); + expect(constructionTool.description).toContain( + petrinautAiTools[toolName].description, + ); + expect(constructionTool.description).toContain( + JSON.stringify(petrinautAiTools[toolName].inputSchema.toJSONSchema()), + ); + } + }); + + test("delegates accepted and rejected inputs to Petrinaut's Zod schemas", () => { + const addArc = toolByName("addArc"); + const invalidArc = { + transitionId: "transition", + arcDirection: "input", + placeId: "place", + weight: 0, + targetSubnetId: null, + }; + const validArc = { ...invalidArc, weight: 1 }; + + expect(v.safeParse(addArc.input!, invalidArc).success).toBe( + petrinautAiTools.addArc.inputSchema.safeParse(invalidArc).success, + ); + expect(v.safeParse(addArc.input!, validArc).success).toBe( + petrinautAiTools.addArc.inputSchema.safeParse(validArc).success, + ); + }); + + test("retains nested values in canonical validation paths", () => { + const addType = toolByName("addType"); + const invalidElement = { + elementId: "speed", + name: "speed", + type: "not-a-type", + }; + const invalidType = { + id: "vehicle", + name: "Vehicle", + iconSlug: "circle", + displayColor: "#808080", + elements: [invalidElement], + }; + const result = v.safeParse(addType.input!, invalidType); + if (result.success) throw new Error("Expected nested type rejection"); + + expect(result.issues[0].path).toMatchObject([ + { input: invalidType, key: "elements", value: invalidType.elements }, + { input: invalidType.elements, key: 0, value: invalidElement }, + { input: invalidElement, key: "type", value: "not-a-type" }, + ]); + }); +}); diff --git a/apps/brunch-agent/test/runbook-artifacts.test.ts b/apps/brunch-agent/test/runbook-artifacts.test.ts new file mode 100644 index 00000000000..410ff7357b9 --- /dev/null +++ b/apps/brunch-agent/test/runbook-artifacts.test.ts @@ -0,0 +1,97 @@ +import { describe, expect, test } from "vitest"; + +import { + latestRunbookIrBlock, + recoverRunbookIr, + RUNBOOK_IR_FENCE, + skillResourcePathsFrom, +} from "../src/runbook-artifacts.ts"; + +import type { FlueConversationSnapshot } from "@flue/sdk"; + +const snapshotWithAssistantText = (text: string): FlueConversationSnapshot => + ({ + messages: [ + { + id: "a1", + role: "assistant", + purpose: "assistant", + display: "visible", + parts: [{ type: "text", text, state: "done" }], + }, + ], + }) as FlueConversationSnapshot; + +describe("runbook artifact recovery", () => { + test("takes the last fenced IR block", () => { + const text = [ + "```" + RUNBOOK_IR_FENCE, + "# first", + "```", + "later", + "```" + RUNBOOK_IR_FENCE, + "# second", + "```", + ].join("\n"); + expect(latestRunbookIrBlock(text)).toBe("# second"); + }); + + test("recovers an IR from assistant history", () => { + const snapshot = snapshotWithAssistantText( + [ + "```" + RUNBOOK_IR_FENCE, + "# Runbook IR", + "## Purpose and outcome", + "```", + ].join("\n"), + ); + expect(recoverRunbookIr(snapshot)).toContain("# Runbook IR"); + }); + + test("collects only successfully read skill resource paths", () => { + const snapshot = { + messages: [ + { + id: "a1", + role: "assistant", + purpose: "assistant", + display: "visible", + parts: [ + { + type: "dynamic-tool", + toolCallId: "t1", + toolName: "read_skill_resource", + state: "output-available", + input: { + path: "/.flue/packaged-skills/skill:sdcpn-modelling:abc/elicitation.md", + }, + output: "ok", + }, + { + type: "dynamic-tool", + toolCallId: "t2", + toolName: "read_skill_resource", + state: "output-error", + input: { + path: "/.flue/packaged-skills/skill:sdcpn-modelling:abc/missing.md", + }, + errorText: "not found", + }, + { + type: "dynamic-tool", + toolCallId: "t3", + toolName: "read_skill_resource", + state: "input-available", + input: { + path: "/.flue/packaged-skills/skill:sdcpn-modelling:abc/pending.md", + }, + }, + ], + }, + ], + } as FlueConversationSnapshot; + expect(skillResourcePathsFrom(snapshot)).toEqual([ + "/.flue/packaged-skills/skill:sdcpn-modelling:abc/elicitation.md", + ]); + }); +}); diff --git a/apps/brunch-agent/test/runbook-elicitation-faux-expert.ts b/apps/brunch-agent/test/runbook-elicitation-faux-expert.ts new file mode 100644 index 00000000000..fdf1c1de6a2 --- /dev/null +++ b/apps/brunch-agent/test/runbook-elicitation-faux-expert.ts @@ -0,0 +1,31 @@ +const replies = [ + "Last Tuesday Line 1 stopped milling because the holding tank before filling was full.", +]; + +let replyIndex = 0; + +export default { + messages: { + create: () => + Promise.resolve({ + content: + process.env["BRUNCH_RUNBOOK_EMPTY_EXPERT"] === "1" + ? [] + : [ + { + type: "text", + text: + replies[replyIndex++] ?? + "I don't know anything more about that.", + }, + ], + model: "faux-vestera-expert", + usage: { + input_tokens: 10, + output_tokens: 10, + cache_creation_input_tokens: 0, + cache_read_input_tokens: 0, + }, + }), + }, +}; diff --git a/apps/brunch-agent/test/runbook-elicitation-faux-provider.ts b/apps/brunch-agent/test/runbook-elicitation-faux-provider.ts new file mode 100644 index 00000000000..27b6614ead9 --- /dev/null +++ b/apps/brunch-agent/test/runbook-elicitation-faux-provider.ts @@ -0,0 +1,96 @@ +import { + fauxAssistantMessage, + fauxProvider, + fauxText, + fauxToolCall, +} from "@earendil-works/pi-ai"; + +const modelId = process.env["BRUNCH_CHAT_MODEL"] ?? "claude-haiku-4-5"; +const skillName = "sdcpn-modelling"; + +const packagedSkillResourcePathFrom = ( + context: unknown, + fileName: string, +): string => { + const match = JSON.stringify(context).match( + new RegExp( + `/\\.flue/packaged-skills/[^"\\s\\\\]+/${fileName.replace(".", "\\.")}`, + ), + ); + if (match === null) { + throw new Error(`activate_skill briefing did not advertise ${fileName}`); + } + return match[0]; +}; + +const ir = (detail: string): string => + [ + "```runbook-ir", + "# Runbook IR", + "## Purpose and outcome", + "Model weekly coatings-line scheduling decisions.", + "## Activities, inputs, outputs, and resource usage", + detail, + "## Unknowns, assumptions, conflicts, and omissions", + "Unknown: product-specific stage times.", + "```", + ].join("\n"); + +const faux = fauxProvider({ + provider: "anthropic", + models: [{ id: modelId, reasoning: true }], +}); + +faux.setResponses([ + fauxAssistantMessage( + [ + fauxToolCall( + "activate_skill", + { name: skillName }, + { id: "activate-skill" }, + ), + ], + { stopReason: "toolUse" }, + ), + (context: unknown) => + fauxAssistantMessage( + [ + fauxToolCall( + "read_skill_resource", + { + path: packagedSkillResourcePathFrom(context, "elicitation.md"), + }, + { id: "read-elicitation" }, + ), + ], + { stopReason: "toolUse" }, + ), + (context: unknown) => + fauxAssistantMessage( + [ + fauxToolCall( + "read_skill_resource", + { + path: packagedSkillResourcePathFrom(context, "ir-template.md"), + }, + { id: "read-ir-template" }, + ), + ], + { stopReason: "toolUse" }, + ), + fauxAssistantMessage([ + fauxText( + `Walk me through the last scheduling decision that surprised you.\n\n${ir("Not yet asked.")}`, + ), + ]), + fauxAssistantMessage([ + fauxText( + `What caused Line 1 to wait in that case?\n\n${ir("Line 1 waited between milling and filling.")}`, + ), + ]), + fauxAssistantMessage([ + fauxText(ir("Line 1 waited when its mill-to-fill holding tank backed up.")), + ]), +]); + +export default faux.provider; diff --git a/apps/brunch-agent/test/runbook-elicitation.test.ts b/apps/brunch-agent/test/runbook-elicitation.test.ts new file mode 100644 index 00000000000..44aca868307 --- /dev/null +++ b/apps/brunch-agent/test/runbook-elicitation.test.ts @@ -0,0 +1,154 @@ +import { mkdtemp, readFile, readdir, rm } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; + +import { expect, test } from "vitest"; + +import { runNodeScript } from "./run-node-script"; + +const testDirectory = import.meta.dirname; + +test("the prospective runner records a recoverable IR without construction or capture machinery", async () => { + const outputDirectory = await mkdtemp( + join(tmpdir(), "brunch-runbook-elicitation-"), + ); + try { + const { exitCode, stdout, stderr } = await runNodeScript( + join(testDirectory, "../src/runbook-elicitation-run.ts"), + join(testDirectory, "../../.."), + { + BRUNCH_CHAT_MODEL: "claude-haiku-4-5", + BRUNCH_RUNBOOK_EXPERT_MODEL: "faux-vestera-expert", + BRUNCH_RUNBOOK_HARD_STOP: "2", + BRUNCH_RUNBOOK_OUTPUT_DIR: outputDirectory, + BRUNCH_RUNBOOK_ALLOW_DIRTY_INSTRUMENT: "1", + BRUNCH_RUNBOOK_ANTHROPIC_MODULE: join( + testDirectory, + "runbook-elicitation-faux-expert.ts", + ), + BRUNCH_RUNBOOK_INTERVIEWER_PROVIDER_MODULE: join( + testDirectory, + "runbook-elicitation-faux-provider.ts", + ), + }, + ); + expect(exitCode, stderr || stdout).toBe(0); + const resultLine = stdout + .split("\n") + .find((line) => line.startsWith("RUNBOOK_ELICITATION_RESULT ")); + expect(resultLine, stdout).toBeDefined(); + const result = JSON.parse( + resultLine!.slice("RUNBOOK_ELICITATION_RESULT ".length), + ) as { + artifactBase: string; + hasIr: boolean; + interviewTurns: number; + resourceFilesRead: string[]; + toolNames: string[]; + wroteCaptureStore: boolean; + }; + expect(result.hasIr).toBe(true); + expect(result.interviewTurns).toBe(2); + expect(result.toolNames).toEqual(["activate_skill", "read_skill_resource"]); + expect(result.toolNames).not.toEqual( + expect.arrayContaining([ + "brunch_ask", + "brunch_sweep", + "addPlace", + "addTransition", + ]), + ); + expect(result.resourceFilesRead).toEqual([ + "elicitation.md", + "ir-template.md", + ]); + expect(result.wroteCaptureStore).toBe(false); + + const files = await readdir(outputDirectory); + expect(files).toEqual( + expect.arrayContaining([ + `${result.artifactBase.split("/").at(-1)}.ir.md`, + `${result.artifactBase.split("/").at(-1)}.json`, + `${result.artifactBase.split("/").at(-1)}.md`, + ]), + ); + const record = JSON.parse( + await readFile(`${result.artifactBase}.json`, "utf8"), + ) as { + instrument: { + sourceCommit: string; + fileSha256: Record; + }; + finalizationMessage: string; + }; + expect(record.instrument.sourceCommit).toMatch(/^[0-9a-f]{40}$/u); + expect(record.instrument.fileSha256).toHaveProperty( + "libs/@hashintel/brunch-agent/evaluations/cases/vestera-scheduling/opening-message.md", + ); + expect(record.instrument.fileSha256).toHaveProperty( + "apps/brunch-agent/src/skills/sdcpn-modelling/SKILL.md", + ); + expect(record.finalizationMessage).toContain("not expert evidence"); + } finally { + await rm(outputDirectory, { recursive: true, force: true }); + } +}); + +test("the prospective runner retains evidence when the expert returns no text", async () => { + const outputDirectory = await mkdtemp( + join(tmpdir(), "brunch-runbook-elicitation-failure-"), + ); + try { + const { exitCode, stdout } = await runNodeScript( + join(testDirectory, "../src/runbook-elicitation-run.ts"), + join(testDirectory, "../../.."), + { + BRUNCH_CHAT_MODEL: "claude-haiku-4-5", + BRUNCH_RUNBOOK_EXPERT_MODEL: "faux-vestera-expert", + BRUNCH_RUNBOOK_HARD_STOP: "2", + BRUNCH_RUNBOOK_OUTPUT_DIR: outputDirectory, + BRUNCH_RUNBOOK_ALLOW_DIRTY_INSTRUMENT: "1", + BRUNCH_RUNBOOK_ANTHROPIC_MODULE: join( + testDirectory, + "runbook-elicitation-faux-expert.ts", + ), + BRUNCH_RUNBOOK_INTERVIEWER_PROVIDER_MODULE: join( + testDirectory, + "runbook-elicitation-faux-provider.ts", + ), + BRUNCH_RUNBOOK_EMPTY_EXPERT: "1", + }, + ); + expect(exitCode).toBe(1); + const resultLine = stdout + .split("\n") + .find((line) => line.startsWith("RUNBOOK_ELICITATION_RESULT ")); + expect(resultLine, stdout).toBeDefined(); + const result = JSON.parse( + resultLine!.slice("RUNBOOK_ELICITATION_RESULT ".length), + ) as { + artifactBase: string; + failure: string; + }; + expect(result.failure).toBe("The simulated expert returned no text"); + expect(await readdir(outputDirectory)).toEqual( + expect.arrayContaining([ + `${result.artifactBase.split("/").at(-1)}.ir.md`, + `${result.artifactBase.split("/").at(-1)}.json`, + `${result.artifactBase.split("/").at(-1)}.md`, + ]), + ); + const record = JSON.parse( + await readFile(`${result.artifactBase}.json`, "utf8"), + ) as { + failure: string; + stopReason: string; + }; + expect(record).toMatchObject({ + failure: "The simulated expert returned no text", + stopReason: "expert-error", + }); + } finally { + await rm(outputDirectory, { recursive: true, force: true }); + } +}); diff --git a/apps/brunch-agent/test/runbook-headless.integration.ts b/apps/brunch-agent/test/runbook-headless.integration.ts new file mode 100644 index 00000000000..dbef41dd0c9 --- /dev/null +++ b/apps/brunch-agent/test/runbook-headless.integration.ts @@ -0,0 +1,362 @@ +import { readFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { fileURLToPath } from "node:url"; + +import { + fauxAssistantMessage, + fauxProvider, + fauxText, + fauxToolCall, +} from "@earendil-works/pi-ai"; +import { setProvider } from "@flue/runtime"; +import { createFlueClient } from "@flue/sdk"; + +import { + CLIENT_TOOL_RESULT_SIGNAL, + isAwaitingClient, +} from "../src/client-tool.ts"; +import { + agentOwnershipHeaders, + flueConversationIdFrom, +} from "../src/conversation-identity.ts"; +import { + createHeadlessPetrinautClient, + isPetrinautConstructionToolName, +} from "../src/headless-petrinaut-client.ts"; +import { loadBuiltBrunchApplication } from "../src/load-built-application.ts"; +import { CHAT_AGENT_ROUTE } from "../src/routes.ts"; +import { + interviewerToolNamesFrom, + skillResourcePathsFrom, +} from "../src/runbook-artifacts.ts"; +import { VALIDATED_CONSTRUCTION_MODE } from "../src/tools/petrinaut-construction.ts"; + +const CHAT_MODEL_ID = "claude-haiku-4-5"; +const RUNBOOK_SKILL_NAME = "sdcpn-modelling"; +const READ_SKILL_RESOURCE_TOOL_NAME = "read_skill_resource"; +const RUNBOOK_RESOURCE_FILES = [ + "elicitation.md", + "ir-template.md", + "pn-construction.md", + "checks.md", +] as const; + +process.env.BRUNCH_CHAT_MODEL = CHAT_MODEL_ID; +process.env.BRUNCH_DEV_DB_PATH = + process.env.BRUNCH_CHAT_DB_PATH ?? + join(tmpdir(), `brunch-runbook-${crypto.randomUUID()}.db`); + +const irPath = fileURLToPath( + new URL( + "../../../libs/@hashintel/brunch-agent/docs/evidence/evaluations/vestera-runbook-headless/runbook-headless-2026-08-28T11-03-53-683Z.ir.md", + import.meta.url, + ), +); +const filledIr = await readFile(irPath, "utf8"); + +const packagedSkillResourcePathFrom = ( + context: unknown, + fileName: string, +): string => { + const serialized = JSON.stringify(context); + const match = serialized.match( + new RegExp( + `/\\.flue/packaged-skills/[^"\\s\\\\]+/${fileName.replace(".", "\\.")}`, + ), + ); + if (match === null) { + throw new Error(`activate_skill briefing did not advertise ${fileName}`); + } + return match[0]; +}; + +const faux = fauxProvider({ + provider: "anthropic", + models: [{ id: CHAT_MODEL_ID, reasoning: true }], +}); +setProvider(faux.provider); + +faux.setResponses([ + fauxAssistantMessage( + [ + fauxToolCall( + "activate_skill", + { name: RUNBOOK_SKILL_NAME }, + { id: "activate-skill" }, + ), + ], + { stopReason: "toolUse" }, + ), + ...RUNBOOK_RESOURCE_FILES.map( + (resourceFile) => (context: unknown) => + fauxAssistantMessage( + [ + fauxToolCall( + READ_SKILL_RESOURCE_TOOL_NAME, + { path: packagedSkillResourcePathFrom(context, resourceFile) }, + { id: `read-${resourceFile}` }, + ), + ], + { stopReason: "toolUse" }, + ), + ), + fauxAssistantMessage( + [ + fauxToolCall( + "getLatestNetDefinition", + {}, + { id: "get-definition-before" }, + ), + ], + { stopReason: "toolUse" }, + ), + fauxAssistantMessage( + [ + fauxToolCall( + "addType", + { + id: "order_type", + name: "Order", + iconSlug: "circle", + displayColor: "#808080", + elements: [], + }, + { id: "add-type" }, + ), + ], + { stopReason: "toolUse" }, + ), + fauxAssistantMessage( + [ + fauxToolCall( + "addParameter", + { + id: "washdown_hours", + name: "Washdown hours", + variableName: "washdown_hours", + type: "real", + defaultValue: "3", + }, + { id: "add-parameter" }, + ), + ], + { stopReason: "toolUse" }, + ), + fauxAssistantMessage( + [ + fauxToolCall( + "addPlace", + { + id: "line_idle", + name: "LineIdle", + colorId: null, + dynamicsEnabled: false, + differentialEquationId: null, + x: 0, + y: 0, + }, + { id: "add-place" }, + ), + ], + { stopReason: "toolUse" }, + ), + fauxAssistantMessage( + [ + fauxToolCall( + "addTransition", + { + id: "start_run", + name: "Start run", + inputArcs: [], + outputArcs: [], + lambdaType: "predicate", + lambdaCode: "return true;", + transitionKernelCode: "", + x: 0, + y: 0, + }, + { id: "add-transition" }, + ), + ], + { stopReason: "toolUse" }, + ), + fauxAssistantMessage( + [ + fauxToolCall( + "addArc", + { + transitionId: "start_run", + arcDirection: "input", + placeId: "line_idle", + weight: 0, + }, + { id: "add-invalid-arc" }, + ), + ], + { stopReason: "toolUse" }, + ), + fauxAssistantMessage( + [ + fauxToolCall( + "addArc", + { + transitionId: "start_run", + arcDirection: "input", + placeId: "line_idle", + weight: 1, + }, + { id: "add-corrected-arc" }, + ), + ], + { stopReason: "toolUse" }, + ), + fauxAssistantMessage( + [ + fauxToolCall( + "addArc", + { + transitionId: "start_run", + arcDirection: "output", + placeId: "line_idle", + weight: 1, + }, + { id: "add-output-arc" }, + ), + ], + { stopReason: "toolUse" }, + ), + fauxAssistantMessage( + [ + fauxToolCall( + "getLatestNetDefinition", + {}, + { id: "get-definition-after" }, + ), + ], + { stopReason: "toolUse" }, + ), + fauxAssistantMessage([ + fauxText( + "Construction complete. Assumption: one line state is representative. Unknowns and the IR's commercial and breakdown losses remain unresolved.", + ), + ]), +]); + +const identity = { + principalKey: "principal-runbook-headless", + conversationId: "conversation-runbook-headless", +}; +const instanceId = flueConversationIdFrom(identity); +const petrinautClient = createHeadlessPetrinautClient( + "Validated construction proof", +); +const application = await loadBuiltBrunchApplication(); + +try { + const appTransport: typeof fetch = async (input, init) => + application.fetch( + input instanceof Request ? input : new Request(input, init), + ); + const client = createFlueClient({ + url: `http://brunch.local/agents/${CHAT_AGENT_ROUTE}/${instanceId}`, + fetch: appTransport, + headers: agentOwnershipHeaders(identity), + }); + const firstAdmission = await client.send({ + initialData: { mode: VALIDATED_CONSTRUCTION_MODE }, + message: { + kind: "user", + body: [ + "Construct from this filled IR without interviewing or emitting pn-json.", + filledIr, + ].join("\n\n"), + }, + }); + await client.wait(firstAdmission); + + const completedCallIds = new Set(); + const serviceClientCalls = async (clientRound: number): Promise => { + if (clientRound >= 20) return clientRound; + const snapshot = await client.history(); + const pendingCalls = snapshot.messages.flatMap((message) => + message.parts.flatMap((part) => { + if (part.type !== "dynamic-tool") return []; + if (!isPetrinautConstructionToolName(part.toolName)) return []; + if (completedCallIds.has(part.toolCallId)) return []; + if ( + part.state !== "output-available" || + !isAwaitingClient(part.output) + ) { + return []; + } + return [ + { + toolCallId: part.toolCallId, + toolName: part.toolName, + input: part.input, + }, + ]; + }), + ); + if (pendingCalls.length === 0) return clientRound; + + const results = await Promise.all( + pendingCalls.map((pendingCall) => petrinautClient.execute(pendingCall)), + ); + for (const result of results) completedCallIds.add(result.toolCallId); + const admission = await client.send({ + message: { + kind: "signal", + type: CLIENT_TOOL_RESULT_SIGNAL, + tagName: CLIENT_TOOL_RESULT_SIGNAL, + body: JSON.stringify(results), + }, + }); + await client.wait(admission); + return serviceClientCalls(clientRound + 1); + }; + await serviceClientCalls(0); + + const snapshot = await client.history(); + const validationRejections = snapshot.messages.flatMap((message) => + message.parts.flatMap((part) => + part.type === "dynamic-tool" && + part.toolName === "addArc" && + part.state === "output-error" + ? [part.errorText] + : [], + ), + ); + const resourcePaths = skillResourcePathsFrom(snapshot); + const assistantText = snapshot.messages + .flatMap((message) => message.parts) + .flatMap((part) => (part.type === "text" ? [part.text] : [])) + .join("\n"); + const parsed = petrinautClient.parse(); + const definition = petrinautClient.definition(); + + process.stdout.write( + `RUNBOOK_HEADLESS_HERMETIC ${JSON.stringify({ + sourceIrUsed: filledIr.includes("VW-02 dark tint restriction"), + parseOk: parsed.ok, + placeCount: definition.places.length, + transitionCount: definition.transitions.length, + toolNames: interviewerToolNamesFrom(snapshot), + resourceFilesRead: RUNBOOK_RESOURCE_FILES.filter((resourceFile) => + resourcePaths.some((resourcePath) => + resourcePath.endsWith(resourceFile), + ), + ), + validationRejections, + emittedFreeFormPnJson: assistantText.includes("```pn-json"), + userMessages: snapshot.messages.filter( + (message) => message.purpose === "user", + ).length, + wroteCaptureStore: false, + })}\n`, + ); +} finally { + petrinautClient.dispose(); + await application.stop(); +} diff --git a/apps/brunch-agent/test/runbook-headless.test.ts b/apps/brunch-agent/test/runbook-headless.test.ts new file mode 100644 index 00000000000..861f0865d74 --- /dev/null +++ b/apps/brunch-agent/test/runbook-headless.test.ts @@ -0,0 +1,73 @@ +import { mkdtemp, rm } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; + +import { expect, test } from "vitest"; + +import { runNodeScript } from "./run-node-script"; + +const testDirectory = import.meta.dirname; + +test("the built ChatAgent constructs a validated net from the saved IR", async () => { + const dbDirectory = await mkdtemp(join(tmpdir(), "brunch-runbook-")); + try { + const { exitCode, stdout, stderr } = await runNodeScript( + join(testDirectory, "runbook-headless.integration.ts"), + join(testDirectory, "../../.."), + { BRUNCH_CHAT_DB_PATH: join(dbDirectory, "conversations.db") }, + ); + expect(exitCode, stderr || stdout).toBe(0); + const resultLine = stdout + .split("\n") + .find((line) => line.startsWith("RUNBOOK_HEADLESS_HERMETIC ")); + expect(resultLine, stdout).toBeDefined(); + const result = JSON.parse( + resultLine!.slice("RUNBOOK_HEADLESS_HERMETIC ".length), + ) as { + sourceIrUsed: boolean; + parseOk: boolean; + placeCount: number; + transitionCount: number; + toolNames: string[]; + resourceFilesRead: string[]; + validationRejections: string[]; + emittedFreeFormPnJson: boolean; + userMessages: number; + wroteCaptureStore: boolean; + }; + expect(result.sourceIrUsed).toBe(true); + expect(result.parseOk).toBe(true); + expect(result.placeCount).toBeGreaterThan(0); + expect(result.transitionCount).toBeGreaterThan(0); + expect(result.toolNames).toContain("activate_skill"); + expect(result.toolNames).toContain("read_skill_resource"); + expect(result.toolNames).toEqual( + expect.arrayContaining([ + "getLatestNetDefinition", + "addType", + "addParameter", + "addPlace", + "addTransition", + "addArc", + ]), + ); + expect(result.toolNames).not.toContain("sweep"); + expect(result.toolNames).not.toContain("brunch_sweep"); + expect(result.toolNames).not.toContain("brunch_ask"); + expect(result.resourceFilesRead).toEqual([ + "elicitation.md", + "ir-template.md", + "pn-construction.md", + "checks.md", + ]); + expect(result.validationRejections).toHaveLength(1); + expect(result.validationRejections[0]).toContain( + "expected number to be >0", + ); + expect(result.emittedFreeFormPnJson).toBe(false); + expect(result.userMessages).toBe(1); + expect(result.wroteCaptureStore).toBe(false); + } finally { + await rm(dbDirectory, { recursive: true, force: true }); + } +}); diff --git a/apps/brunch-agent/test/sdcpn-inbox-parse.test.ts b/apps/brunch-agent/test/sdcpn-inbox-parse.test.ts new file mode 100644 index 00000000000..aa63b1e60ac --- /dev/null +++ b/apps/brunch-agent/test/sdcpn-inbox-parse.test.ts @@ -0,0 +1,31 @@ +import { readdirSync, readFileSync } from "node:fs"; +import { join } from "node:path"; +import { fileURLToPath } from "node:url"; + +import { describe, expect, test } from "vitest"; + +import { parseSDCPNFile } from "@hashintel/petrinaut-core"; + +const fixtureDirectory = fileURLToPath( + new URL( + "../../../libs/@hashintel/brunch-agent/docs/inbox/sdcpn-examples-to-validate/", + import.meta.url, + ), +); + +describe("inbox SDCPN examples", () => { + const fixtureNames = readdirSync(fixtureDirectory) + .filter((fileName) => fileName.endsWith(".json")) + .sort(); + + test("are present", () => { + expect(fixtureNames.length).toBeGreaterThan(0); + }); + + test.each(fixtureNames)("%s parses with parseSDCPNFile", (fileName) => { + const parsed = parseSDCPNFile( + JSON.parse(readFileSync(join(fixtureDirectory, fileName), "utf8")), + ); + expect(parsed.ok, fileName).toBe(true); + }); +}); diff --git a/apps/brunch-agent/test/sdcpn-modelling-skill.test.ts b/apps/brunch-agent/test/sdcpn-modelling-skill.test.ts new file mode 100644 index 00000000000..224a52c4eaf --- /dev/null +++ b/apps/brunch-agent/test/sdcpn-modelling-skill.test.ts @@ -0,0 +1,38 @@ +import { readFileSync } from "node:fs"; + +import { describe, expect, test } from "vitest"; + +const skillDirectory = new URL( + "../src/skills/sdcpn-modelling/", + import.meta.url, +); +const resourceFiles = [ + "elicitation.md", + "ir-template.md", + "pn-construction.md", + "checks.md", +] as const; + +const readSkillFile = (fileName: string): string => + readFileSync(new URL(fileName, skillDirectory), "utf8"); + +describe("the authored sdcpn-modelling skill", () => { + test("has spec-valid routing frontmatter and four supporting resources", () => { + const skill = readSkillFile("SKILL.md"); + expect(skill).toMatch(/^---\nname: sdcpn-modelling\n/u); + expect(skill).toMatch(/^description: .+Use when .+\n/mu); + expect(skill).toContain("# Lifecycle"); + expect(resourceFiles.map(readSkillFile)).toHaveLength(4); + }); + + test("keeps reusable teaching separate from the scenario and payload contract", () => { + const elicitation = readSkillFile("elicitation.md"); + const construction = readSkillFile("pn-construction.md"); + expect(elicitation).toContain("provenance: universal"); + expect(elicitation).not.toMatch(/Vestera|truck fleet|semiconductor/iu); + expect(construction).toContain("Timed work"); + expect(construction).toContain("getLatestNetDefinition"); + expect(construction).not.toContain("```json"); + expect(construction).not.toContain("```pn-json"); + }); +}); diff --git a/apps/brunch-agent/test/turn-timing.test.ts b/apps/brunch-agent/test/turn-timing.test.ts index 1c6e32462fb..35c65f43629 100644 --- a/apps/brunch-agent/test/turn-timing.test.ts +++ b/apps/brunch-agent/test/turn-timing.test.ts @@ -3,7 +3,7 @@ import { expect, test } from "vitest"; import { createTurnTimingRecorder, type TurnTimingPurpose, -} from "../../../libs/@hashintel/brunch-agent/evaluations/protocols/process-model-elicitation/baseline/turn-timing.ts"; +} from "../../../libs/@hashintel/brunch-agent/evaluations/protocols/legacy-baseline/turn-timing.ts"; import type { FlueObservation, ModelRequest } from "@flue/runtime"; diff --git a/apps/brunch-agent/turbo.json b/apps/brunch-agent/turbo.json index d411a23eb3d..4db37dc9c52 100644 --- a/apps/brunch-agent/turbo.json +++ b/apps/brunch-agent/turbo.json @@ -23,8 +23,20 @@ "passThroughEnv": ["BRUNCH_CHAT_ORIGIN", "PETRINAUT_WEBSITE_ROOT"] }, "test:unit": { - "dependsOn": ["build", "codegen", "^build"], - "env": ["TEST_COVERAGE"] + "dependsOn": [ + "build", + "codegen", + "^build", + "@hashintel/brunch-agent#build", + "@hashintel/brunch-agent-plugin-gherkin#build", + "@hashintel/brunch-agent-plugin-sdcpn#build" + ], + "env": ["TEST_COVERAGE"], + "inputs": [ + "$TURBO_DEFAULT$", + "$TURBO_ROOT$/libs/@hashintel/brunch-agent/**", + "!$TURBO_ROOT$/libs/@hashintel/brunch-agent/**/{.flue,.turbo,dist,node_modules}/**" + ] } } } diff --git a/libs/@hashintel/brunch-agent/AGENTS.md b/libs/@hashintel/brunch-agent/AGENTS.md index 4b77a051ae2..e09a70a9a67 100644 --- a/libs/@hashintel/brunch-agent/AGENTS.md +++ b/libs/@hashintel/brunch-agent/AGENTS.md @@ -26,7 +26,7 @@ from this file. When work starts on a branch, state these six things in [`MISSION.md`](MISSION.md) and copy them into the branch/PR description. Do not create additional planning or control documents, except the -next-concerns scratchpad below. +next-concerns draft and bounded side quest described below. - **Imperative** — what must become true, and why now. - **Throughline** — the real entrypoint or boundary being changed. @@ -39,22 +39,58 @@ next-concerns scratchpad below. Running the path may lengthen this list; that is calibration, not regression. - **Stop or reorient** — evidence that invalidates or changes the route. -### One live mission, next-concerns scratchpad +The six sections are the contract. Missions have also carried two additions that earned their +keep: a short **Status** header (live / accepted) above the contract, and a closing **Deferred** +section pointing at the draft. Keep both on future missions. -[`MISSION.md`](MISSION.md) is the only execution authority. Agents and humans implement against it. +### One live mission, next-concerns draft -[`MISSION.next.md`](MISSION.next.md) is the scratchpad for discussing all next concerns. It may -hold a longer horizon than a single mission. It is not a mission: do not implement it, do not -treat it as a second concurrent mission, and do not declare its focus until planning is resolved. +[`MISSION.md`](MISSION.md) is the only execution authority. Agents and humans implement against it. -A current mission's **Deferred** items belong in that scratchpad as well. Do not silently drop or +[`SIDE_QUEST.md`](SIDE_QUEST.md), when present, is one temporary, user-authorized experiment or +remediation inside the live mission. It is legitimate only when evidence from that mission has +exposed a bounded set of concrete residual failures whose investigation helps close the mission or +informs named later clusters. It must state its relationship to the live mission, imperative, +throughlines, proof, constraints, stop conditions, and budget for each paid activity. It must not +supersede or contradict `MISSION.md`, broaden into speculative future work, create a second live +mission, or coexist with another active side quest. Record its outcome in mission evidence and any +affected `MISSION.next.md` clusters, then remove the active side-quest file before archiving the +mission. + +[`MISSION.next.md`](MISSION.next.md) is the self-contained canonical capture repository for +upcoming work: the draft missions and the comprehensive record of ideas, observations, questions, +and named mechanisms already raised. It may hold a longer horizon than a single mission. It is not +a mission: do not implement it, do not treat it as a second concurrent mission, and do not declare +its focus until a cluster is cut. + +Update the draft during planning, design, grilling, or other mission elicitation while the context +is active. Keep every hypothesis, observation, question, and named mechanism at conversational +fidelity. A heading plus a one-line label is not a record of a design. Rejected alternatives and +the reason they lost belong next to the locked choice. Ungrilled fog stays marked unasked. The +draft must stand on its own; do not rely on or link to a transcript as a substitute for capturing +that content here. + +When regrouping or cutting, compare the draft before and after. Every item must either move into +the live mission or remain in the draft at the same fidelity. Once an elicitation session is over, +the draft — not an external transcript — is the source for future cuts. + +Group plausible future missions as ordered, numbered `# Mission N — …` headings. Spikes that are +not missions, standing lock / out-of-scope decisions, and a live mission's leftover / Deferred +items get their own unnumbered headings — they are not fake missions. Do not pre-fill Imperative, +Throughline, Proof, or Status on a cluster; those sections are the cut into `MISSION.md`. Record +Constraints, Fog-line, and Stop or reorient on a cluster only when the conversation already earned +them. + +A current mission's **Deferred** items belong in that draft as well. Do not silently drop or supersede them when adding other concerns. When the current mission is accepted and the next focus is resolved: 1. Move `MISSION.md` to `docs/mission-archive/{n}-{slug}.md`. -2. Cut a single focused `MISSION.md` from the scratchpad (the six-section contract above). -3. Leave everything that did not make the cut in `MISSION.next.md`. +2. Cut a single focused `MISSION.md` from one cluster (the six-section contract above). +3. Leave everything that did not make the cut in `MISSION.next.md` at the same fidelity — still + hypotheses, still rejected alternatives, still named mechanisms. A cut is a copy of one cluster + into authority, not a summary of the remainder. Do not promote `MISSION.next.md` wholesale. Do not keep two live missions. Do not delete a closed mission; the archive is evidence of what was proven, not marching orders. Re-earn before building diff --git a/libs/@hashintel/brunch-agent/CONTEXT.md b/libs/@hashintel/brunch-agent/CONTEXT.md index 5c95f944ec9..d25e2356bc6 100644 --- a/libs/@hashintel/brunch-agent/CONTEXT.md +++ b/libs/@hashintel/brunch-agent/CONTEXT.md @@ -94,6 +94,12 @@ captures, never a persistence surface. Defining a plugin's IR means writing its must-know tables. _Avoid_: knowledge store, domain model (as a stored unit), staging area. +**Runbook IR** — Mission 3's structurally typed Markdown workpiece: filled during elicitation and +consumed during PN construction, with explicit unknowns, assumptions, conflicts, omissions, and +losses. It is an experiment in an intermediate representation, not yet the typed three-register +**IR** above: it is not folded from captures, does not require kinds/slots/grades, and is not a new +persistence surface. + **Capture envelope** — the domain-free wrapper around an opaque plugin payload: harness-minted id, evidence spans, epistemic status, confidence, value-xor-absence, alternatives grouping, one `supersedes` link. The hourglass waist. Status (`active | superseded | retracted`) derives at read @@ -142,17 +148,25 @@ whether "not applicable" is accepted, and why the model needs it. Kind-level onl trigger (declared kind, optionally one unsatisfied demanded slot), `when` text, and an `ask` question. Never names a domain. -**Runbook** — the `kickoff`, `trajectory`, and `close` keys for one **job** (`construct`, -`review and revise`); harness default interleaved with the plugin's cell. `kickoff` produces a -posture; `close` is honest stopping, never the decision to stop. - -**Key** — one fixed, harness-owned heading of plugin authoring: the harness defines, teaches, and -ships a default; a plugin specialises it in a cell. Four groups: contract data, guidance, runbook, -machinery. Rendered key → harness default → plugin cell. - -**Repertoire** — the harness's own filling of every guidance and runbook key, shipped behind -core's guarded `./prompts` subpath, rendered by bindings, never imported by a plugin. Admitted by -evidence, not plausibility. +**Runbook** — the structurally typed, human-readable definition for eliciting and constructing +one **target formalism**. It pairs universal repertoire teaching with formalism-specific purpose, +investigation typologies, guidance, an IR template, transformation knowledge, completion, and +checks in a nested Markdown hierarchy. `kickoff`, `trajectory`, and `close` are lifecycle regions +inside the runbook, not its whole definition; the existing YAML field named `runbooks` keeps that +narrower code-level meaning. Structural headings do not require captures or IR contents to use +closed semantic types. Mission 3 delivers the first runbook through one Flue skill and disclosed +resources; that packaging is not part of the term's definition. + +**Key** — one fixed, harness-owned heading of the YAML plugin/repertoire precursor: the harness +defines, teaches, and ships a default; a plugin specialises it in a cell. Four groups: contract +data, guidance, runbook, machinery. Rendered key → harness default → plugin cell. Mission 3 mines +this authoring structure as evidence; it does not restore the renderer as the runbook architecture. + +**Repertoire** — generally applicable elicitation concepts, directives, procedures, judgment +activations, caveats, and failure knowledge: how an expert-knowledge interview goes well regardless +of target formalism. The current YAML is the harness's evidence-admitted filling of guidance and +lifecycle keys behind core's guarded `./prompts` subpath. A rendered runbook may incorporate this +teaching without using that runtime. **Mechanism type** — how a guidance key works on the interviewer: a **license** permits what the model would hedge on; a **technique** is a form of question or move; an **attention** key names diff --git a/libs/@hashintel/brunch-agent/MISSION.md b/libs/@hashintel/brunch-agent/MISSION.md index 1bd9fb64ba8..7f9e316eb9d 100644 --- a/libs/@hashintel/brunch-agent/MISSION.md +++ b/libs/@hashintel/brunch-agent/MISSION.md @@ -1,111 +1,199 @@ -# Mission 3 — runbook, template, headless PN +# Mission 3 — structurally typed runbook to headless PN ## Status -Live. This file is execution authority. +Closed on 2026-08-31 with a split result. The real Flue runbook and elicitation-to-workpiece leg is accepted as the Mission 3 control. The real-model semantic construction leg is falsified on the exercised provider-visible schema route and moves forward only as successor risk; it is not retroactive success. -Later concerns are clustered in [`MISSION.next.md`](MISSION.next.md). That file is a scratchpad, -not a mission; do not implement it. Host-trunk work, Petrinaut read/write tools, typed IR maps, -observer-triggered sweeps, and any join to Mission 2's capture store are not this mission. +Close evidence: + +- [`docs/evidence/proofs/implementations/fe-1525-headless-runbook-pn.md`](docs/evidence/proofs/implementations/fe-1525-headless-runbook-pn.md) records the production runbook path, historical runs, hermetic construction, and paid construction failure. +- [`docs/evidence/evaluations/vestera-prospective-baseline-v1/campaign-adjudication.md`](docs/evidence/evaluations/vestera-prospective-baseline-v1/campaign-adjudication.md) records the frozen prospective control: three paid invocations, one invalid runtime member, two recoverable and independently graded workpieces, no hard-failure gates in the valid members, omniscient range `66.3–80.0 / 100`, and cold utility range `3.3–3.5 / 4` with conditional downstream readiness. + +Proof items 1–5 and 8 are established through the real production agent path. Item 7 is established for the elicitation workpiece as an explicitly partial, epistemically marked artifact. Item 6 is not established for real-model construction: the construct-only paid run made nine malformed nested `addType.elements` calls and produced a vacuously parser-valid empty net. The hermetic non-empty fixture proves packaging and callback validation, not model semantic fidelity. Construction-discovered return to elicitation also remains unexercised. + +Required design input: +[`docs/specs/structurally-typed-elicitation-runbooks.md`](docs/specs/structurally-typed-elicitation-runbooks.md). +The specification defines the meaning and first architecture of the runbook; this mission decides +what to build and prove. If the real path contradicts the design, stop and surface the evidence +rather than satisfying the document by construction. + +Later concerns are clustered in [`MISSION.next.md`](MISSION.next.md). That file is the canonical +draft of upcoming work, not a mission; do not implement it. Host-continuity work, Petrinaut +read/write tools, typed IR maps, observer-triggered sweeps, and any join to Mission 2's capture +store are not this mission. + +A now-retired side quest authorized one bounded Mission 3 remediation: a construct-only headless +conversation may mount the minimal Petrinaut mutation subset needed to replace free-form net JSON +with validated construction. Packaging and hermetic callback execution passed, but the single +real-model run failed to construct a non-empty net because the provider-facing open-object schema +lost the canonical array shape. The evidence is recorded in the implementation proof and carried +into Mission 5. Those tools stay absent from ordinary interview and `/api/chat` panel +conversations; live-net integration and the typed map remain Mission 5. ## Imperative -Prove that a comprehensive runbook and IR template can teach a model through the live Flue -chat door, that the filled template can be driven headless (no GUI), and that the filled -document contains enough to generate a Petri net Petrinaut will accept. Condition 5's runner -is broken on this app (deleted elicitor imports); restore the JS-API drive pattern, not a TUI -and not the old SDCPN elicitor. This is a prompting experiment. It does not improve capture -quality, and it does not fold Mission 2's ledger. +Prove that one production Flue `ChatAgent` can use a **structurally typed elicitation runbook** to +conduct or replay an interview, maintain a structured-but-not-strictly-semantically-typed +**runbook IR**, and use that workpiece to generate Petri-net JSON that Petrinaut accepts. Bare IR +below means this Markdown workpiece, not the existing typed three-register IR. -## Throughline +The runbook combines two authored knowledge layers: universal elicitation teaching (the useful +content behind the repertoire) and SDCPN target-formalism guidance (what to investigate, notice, +deepen, preserve, transform, and check). Deliver them through one Flue skill with progressively +disclosed resources, not through a large undifferentiated system prompt, a skill catalog, or the +old plugin runtime. -One headless pass on the Mission 1 door, with a runbook and IR template mounted on the -production `ChatAgent`: +Condition 5's typed-capture path demanded too much in-loop semantic judgment and produced ordinary +question turns on the order of minutes. This mission recovers its researched teaching and authoring +discipline while testing structural typing as the sufficient first lever. It does not improve +capture quality and does not fold Mission 2's ledger. -`createFlueClient → send → wait → history() → filled IR template → structured (not strictly typed) IR → PN JSON → petrinaut-core parse/validate` +## Throughline -Generate the net without canvas mutation tools. Manual load into the app is enough to score -whether the template contained enough to draw. Template fill is not a sweep: sweep means -capture-store apply. Do not join this path to Mission 2's store. +One headless pass through the Mission 1 production door: -## Proof +`createFlueClient → send initial modelling request → ChatAgent activates the runbook skill and reads elicitation + IR resources → (driver send → wait → history()) × interview turns → recover filled IR → driver sends construct-from-IR request → ChatAgent reads PN-construction + check resources and returns PN JSON → wait → history() → petrinaut-core parse/validate` -This proof establishes that a headless teaching loop can fill a template and yield a -validatable Petri net. It does not establish a typed map, canvas write tools, capture -improvement, session-as-net, or two brains. +Skill activation and resource reads occur inside model turns initiated by `send`; the headless driver +does not invoke them before dispatch. The same agent owns elicitation, IR maintenance, +construction, and validation. These are lifecycle +phases, not separate agents. Construction may expose a gap and route the same agent back to +elicitation; the runbook describes the return without inventing a workflow engine. -From the real brunch-agent entrypoint (same `ChatAgent` / `/api/chat` door as Missions 1–2), -one production-path test or documented JS-API script observes all of the following: +During elicitation, work in the expert's vocabulary and maintain the IR. Read PN-construction +material only when constructing or checking the net. The IR is the seam: generation consumes it +rather than treating the transcript as the model. Manual loading into Petrinaut remains enough to +inspect the drawing. -1. A headless client drives the live agent with `createFlueClient` → `send` → `wait` → - `history()` (the Flue routing-table loop, not a PTY/TUI). -2. A comprehensive runbook and IR template are mounted on that agent (system prompt, skill - body, supporting file — placement is fog; bundling in the skill is allowed). -3. The conversation fills the template; the filled document is recoverable from that - conversation's outputs without opening the Petrinaut GUI. -4. Inference from that filled document produces PN JSON that `parseSDCPNFile` (or the current - petrinaut-core import equivalent) accepts. Missing canvas positions are allowed if the - parser already treats them as recoverable. -5. The interviewer never called a sweep tool; the capture store was not written as part of - producing the net. +## Proof -Prefer that one throughline over a broad suite. A human panel run is not required; manual -load of the JSON into the app is enough to inspect the drawing. +This proof establishes that a structurally typed runbook package can teach one agent through the +real Flue path and yield a validatable Petri net. It does not establish a final heading catalogue, +a typed capture/IR system, canvas write tools, session-as-net, two brains, or an automated +repertoire-to-runbook compiler. + +From the real brunch-agent entrypoint (the same `ChatAgent` / `/api/chat` door as Missions 1–2), +one production-path test or documented headless script observes all of the following: + +1. The agent has one mounted runbook skill. Its concise catalog description and always-on + instruction route the modelling lifecycle without embedding the full runbook in the system + prompt. +2. Skill activation yields the shared lifecycle procedure. Flue's native skill-resource surface + makes elicitation teaching, the IR template, PN-construction guidance, and checks readable + without a bespoke loader. +3. The elicitation material visibly combines universal teaching and SDCPN target-formalism content + under the structural responsibilities fixed by the design specification. Authored runbook + content names situation typologies, not facts from the fixed operational scenario; those facts + appear only in conversation inputs, the filled IR, and evidence expectations. +4. Interviewing uses the expert's vocabulary rather than places, transitions, arcs, or colours; + construction guidance is not required to frame ordinary elicitation questions. +5. The conversation fills a recoverable Markdown IR whose structure, unknowns, assumptions, + conflicts, and omissions are legible without opening the Petrinaut GUI. +6. The construction phase consumes that IR, reads the construction/check material, and produces PN + JSON that `parseSDCPNFile` (or the current Petrinaut-core import equivalent) accepts. Missing + canvas positions are allowed if the parser already treats them as recoverable. +7. The result names consequential inference, approximation, defaulting, omission, and + unrepresentable material rather than silently hardening it. +8. The interviewer never calls a sweep tool and producing the IR/net does not write Mission 2's + capture store. + +Prefer that one throughline over a broad suite. Record the resource path taken and where the first +runbook helped, failed, or created attention strain. A fluent conversation by itself is not proof. ## Constraints -- Mission 1's chat door stays the door: Petrinaut panel → `transport-aisdk` → Flue - `ChatAgent`. Do not rewrite the panel onto `@flue/react`. The adapter still must not depend - on core, binding, or plugins. -- Restore the drive pattern from condition 5's runner (`createFlueClient` over the app - router). Do not revive that runner's SDCPN elicitor, `brunch_ask`, sweep, fold, or - completion accounting as the teaching vehicle. -- Do not re-enter plugin-gherkin, plugin-sdcpn, repertoire, kinds, slots, fold, completion, - issues, or correction in order to author the template. -- Template fill is not a sweep. Do not call `applyCaptureSweep` or otherwise join Mission 2's - ledger unless a later cut says so. The template is a teaching artifact, not ADR-0003 - register-2 derived from captures. -- No Petrinaut canvas mutation tools. No typed FE map. Generation may be structured without - being strictly typed. +- Consume + [`docs/specs/structurally-typed-elicitation-runbooks.md`](docs/specs/structurally-typed-elicitation-runbooks.md): + broad runbook definition, structural-before-semantic typing, universal + target-formalism + authorship, one-agent lifecycle, one skill, and lazy phase-specific reference. +- Mission 1's chat door stays the door: Petrinaut panel → `transport-aisdk` → Flue `ChatAgent`. + Do not rewrite the panel onto `@flue/react`. The adapter still must not depend on core, binding, + or plugins. +- Use Flue's documented happy paths: `useSkill`, `activate_skill`, packaged supporting resources, + `read_skill_resource`, existing tool mounting, and the JS-API drive pattern. No custom prompt or + resource loader. +- Author the first rendered runbook directly in Markdown. Do not build a key renderer, compiler, + projection engine, or generic schema framework before a second real consumer creates strain. +- Keep the system instruction to identity, routing, lifecycle invariants, transport facts, and the + requirement to activate the runbook. Bulky teaching belongs in the skill package. +- One model-facing agent and one runbook skill. No skill catalog, subagent topology, workflow + engine, TUI, or second server. +- Keep elicitation and construction in separate information regions. Conversation follows the + expert's thread; IR headings do not become an opening questionnaire; PN internals remain + construction vocabulary. +- Author runbook resources at the target-formalism level. Concrete scenario facts belong only to + the run input and filled IR; do not bake a truck fleet, semiconductor fab, or any other test case + into reusable teaching. +- Structural typing may fix headings, nesting, repeated entry shapes, completion criteria, checks, + unknowns, and losses. Do not require closed kinds, slots, proposal types, precision grades, + firing predicates, fold rules, or typed completion algebra. +- Restore the useful drive pattern from condition 5 (`createFlueClient` over the app router), not + its SDCPN elicitor, `brunch_ask`, sweep, fold, or completion accounting. +- The IR template is a teaching workpiece, not ADR-0003 register 2 and not a projection of captures. + Do not call `applyCaptureSweep` or join Mission 2's store. +- No Petrinaut canvas mutation tools on ordinary interview or `/api/chat` panel conversations and + no typed FE map. The active side quest alone may mount its six-tool subset on a construct-only + headless conversation; generation still uses inference from structured prose. - The app may import `@hashintel/petrinaut-core` to parse/validate PN JSON. It must not import `@hashintel/petrinaut` UI. -- Update runbook/docs only where exercised behavior changes. +- Update user-facing docs only where exercised behavior changes. ## Fog-line -Do not design past these questions before running the simplest path that can answer them: - -- Where the runbook and IR template live (system prompt, skill body, supporting file, or a - bundle of those) so the model actually uses them under `send`/`wait`. -- What "structured but not strictly typed IR" looks like at the real boundary — a JSON - document the script consumes, a skill output, or a last-turn artifact — without inventing a - three-register revival. -- How much of condition 5's runner to restore versus a thinner drive script on the current - `ChatAgent`. -- Whether `parseSDCPNFile` is enough "petrinaut validate," or the throughline exposes a - smaller/larger import check. - -Resolve each at the real boundary, record the observed answer in code/tests, and then -re-evaluate. Do not turn them into a plugin SDK revival or a capture↔IR join. +Do not design past these questions before running the smallest path that can answer them: + +- The first exact Markdown heading catalogue and which repeated entries need required child + headings rather than authoring convention alone. +- The smallest sufficient boundary between skill instructions and supporting resources. The + conceptual roles are fixed; exact files move only in response to observed sprawl, missed routing, + or phase contamination. +- The skill's name/description and the smallest always-on instruction that reliably cause + activation and resource routing over a long conversation. +- How the filled Markdown IR is recovered from the real conversation: one last-turn artifact, a + resource-like document returned in output, or another shape already supported by the path. Do + not create a persistence surface to answer this. +- How to exercise the lifecycle's construction-discovered-gap return without manufacturing a + workflow state machine. +- Whether `parseSDCPNFile` is sufficient “Petrinaut accepts,” or the path exposes a smaller/larger + import check. +- Which instructions initially believed universal prove SDCPN-specific, and which SDCPN guidance + earns migration upward. Record the editorial move; do not automate it. + +Resolve each at the real boundary and record the observed answer in code/tests and mission-close +evidence. A longer fog-line after the first run is calibration, not failure. ## Stop or reorient -Stop and surface the evidence before continuing if: - -- producing the net requires writing Mission 2's capture store, or template fill is - implemented as apply-sweep; -- plugin-sdcpn, repertoire, fold, completion, or `brunch_ask` re-enter as the teaching +Stop and surface evidence before continuing if: + +- the runbook is implemented from the old narrow `kickoff` / `trajectory` / `close` definition or + the broader design specification is treated as optional; +- the system prompt becomes the full research/runbook corpus instead of a concise router; +- more skills, another agent, a custom loader, or a workflow engine appear to solve an unobserved + future problem; +- construction material leaks into ordinary interviewing and produces schema-shaped or PN-shaped + questions; +- concrete scenario facts enter authored runbook resources instead of the conversation/IR instance; +- the agent cannot reliably activate the skill or read the relevant phase resource on the real + path; +- the IR cannot support PN generation without rereading the transcript as the primary model; +- a closed kind/slot/proposal/precision/firing/fold/completion system re-enters merely to make the + first template feel rigorous; +- producing the IR or PN requires writing Mission 2's capture store or implementing template fill + as apply-sweep; +- plugin-sdcpn, repertoire runtime, fold, completion, or `brunch_ask` re-enter as the teaching vehicle; - canvas mutation tools appear on the interviewer; -- the drive becomes a TUI or a second server rather than `createFlueClient` against the live - door; +- the drive becomes a TUI or second server instead of `createFlueClient` against the live door; - the adapter grows a dependency on core, binding, or plugins; -- ordinary turns on this path return to condition-5 latency (order-of-minutes) as the - designed shape of a teaching turn. +- ordinary teaching turns return to condition-5 latency (order-of-minutes) as the designed shape. + +A need for one semantic commitment is not permission to restore the whole typed kernel. Name the +specific missing commitment and reorient from that evidence. ## Deferred -Host trunk, typed map and Petrinaut read/write via existing `onToolCall`, capture improvement -(token-threshold observer, typed payloads), and whether capture and runbooks converge, are -clustered in [`MISSION.next.md`](MISSION.next.md). That scratchpad does not supersede this -section. +Mission 4 host continuity, Mission 5 typed map and Petrinaut read/write via existing `onToolCall`, +Mission 6 capture improvement, and whether capture and runbooks converge remain in +[`MISSION.next.md`](MISSION.next.md). Periodic PN generation and programmatic loading also remain +there. That draft does not supersede this mission. diff --git a/libs/@hashintel/brunch-agent/MISSION.next.md b/libs/@hashintel/brunch-agent/MISSION.next.md index cba4464c5b4..9a15cad8dba 100644 --- a/libs/@hashintel/brunch-agent/MISSION.next.md +++ b/libs/@hashintel/brunch-agent/MISSION.next.md @@ -1,80 +1,600 @@ -# Next concerns - -Scratchpad, not a mission. [`MISSION.md`](MISSION.md) remains execution authority. Do not -implement from this file. Clusters are ordered; they are not a second concurrent mission. -When a focus is cut into a new `MISSION.md`, leave everything that did not make the cut here. - -Capture (archived Mission 2) and the live runbook/IR path (Mission 3) are **independent**. -Whether they converge, and if so where, when, and in what form, is an open later question. Do -not wire them in order to tidy the list. - -## Host trunk - -Later cut. Does not need the capture pipe. Does not need a runbook. - -- **Two brains, same panel.** Stock modeller and Brunch selectable without relaunching. Today - the switch is `yarn dev` vs `yarn dev:brunch`. Locked: Brunch is not the new Petrinaut - assistant; panel stays `useChat` / `onToolCall`; do not splice conversations; stock must work - with brunch-agent down; HASH embed stays stock unless opted in. Open: origin sharing, picker - location, mid-net vs start-only switch. -- **Net create/save/load is the session lifecycle.** Working assumption: Petrinaut net id - discriminates one Flue conversation per principal. Prove save/load keeps the same conversation; - a new net id mints a new one. If net ids are unstable, drop the assumption and rekey (Mission 2 - stores by Flue conversation identity until then). -- **Compaction.** Prove panel and transcript reconstruct across a real Flue compaction boundary - (`compaction-vs-durable-history` / FE-1386). Product control to compact or to show a summarized - range only after that pin. - -Voice is a git parent on `kostandin/h-6763-openai-canonical-speech`, not a cluster here. Same -`POST /api/chat` dock Mission 1 named. Resolve UUID-per-net vs `petrinaut-preview:${netId}` and -stolen vs configured `/api/chat` on that stack. Brunch owns no provider audio. - -## Elicitation ladder - -Mission 3 holds the prompting experiment (runbook, template, headless drive, off-canvas PN). -Remaining order after that: typed map plus canvas I/O, then capture improvement. Watch for the -strain threshold (condition 5: typed mapping, in-loop LLM judgment, ~2 min question turns). - -### Typed map and Petrinaut read/write - -FE: minimal typing — what maps to what — so generation can reject a vague shape. Brunch agent -read/write of the live net via existing panel `onToolCall`, not by absorbing the stock modeller -or its 46-tool set. Agent generates a PN on the canvas from an IR. Which tools, when: not -decided. - -### Capture improvement - -Token-threshold observer: arm after N tokens (no model call), fire on next turn settle. Maybe -typed payloads that match the FE map; maybe not, if the runbook path is winning. Subagents / -micro-cognitive tasks: undecided. This is where latency and judgment re-enter on purpose, so -the threshold is visible. - -## Later / parallel - -Not sequenced on the elicitation ladder. May ride along a live mission if the throughline -already has the hook. - -- **Observability / eval / tracing.** Node OTel SDK → HASH collector/Tempo; Flue - `instrument(...)` already in `app.ts` but nothing exports. `dispatch()` does not propagate - `traceparent`. Content capture off until a privacy policy. FE-1505 / FE-1423 stay production - gates. -- **Watch simulated conversations.** Driver: `@flue/sdk` JSON (`send` / `wait` / `history`), - not PTY poll. Human observer: same conversation URL on `:4321` (render `dynamic-tool` / - `data-*` / skill activation). Herdr panes are terminals; at most open that URL or tail the - transcript CLI. `HarnessAgent` is not this surface. -- **AI SDK 7 `HarnessAgent`.** Converse of the current door (resume a harness session by chat - id). Flue already owns that session; `transport-aisdk` is the UI adapter. A Pi/Claude Code - harness would be another substrate — what `binding-flue` isolates — or a Flue replacement. - Undecided. +Self-contained canonical capture repository for upcoming missions: what we currently think we know about what to do next — ideas, observations, questions, and named mechanisms already raised. Not execution authority. Implement against [`MISSION.md`](MISSION.md). Do not promote this file wholesale. Do not keep two live missions in one branch. + +Plausible future missions are the ordered `# Mission N — …` headings. Imperative, Throughline, Proof, and Status wait until a cluster is cut into `MISSION.md`. Constraints, fog, and stop lines appear where prior discussion already earned them. Spikes, standing locks, out-of-scope decisions, and a live mission's leftovers get unnumbered headings — they are not fake missions. Update this file while planning context is active; it must carry full capture fidelity without relying on a transcript. When cutting or regrouping, compare this draft before and after: every item must either move into the live mission or remain here at the same fidelity. + +Live Mission 3 is the runbook / Markdown workpiece / headless construction experiment. Archived Mission 2 is the mechanical capture pipe. The owner has accepted a split close posture: Mission 3's real Flue runbook and elicitation-to-workpiece leg worked technically, while its real-model construction leg failed at the provider-schema boundary. Mission 3 closes after the frozen prospective campaign supplies its three runs, independent grader reports, and human adjudication; those results establish the control rather than retroactively changing the mission's construction result. + +The prospective campaign and the next mission may proceed in parallel because the baseline instrument is frozen and clean. The redesign must not modify the control files or consume partial campaign output as if it were a completed baseline. Do not leave two live missions on one branch: Mission 3 remains authority in its current worktree; a concurrently launched successor needs its own issue, branch/worktree, and `MISSION.md`. + +The Mission 4–7 numbering below is **proposed successor numbering**. Live `MISSION.md` still deposits host continuity under the old Mission 4 label, typed map/read-write under Mission 5, and capture improvement under Mission 6. The proposed spine regroups those concerns around owner-led runbook redesign and FE-1476; until missions are cut, the old labels remain aliases rather than a second execution plan. + +```text +M1 chat (done) M2 mechanical capture (archived) M3 runbook/workpiece (closing) + ├─ frozen prospective control + └─ independent grading + adjudication + +Parallel successor +M4 owner-led, research-informed runbook/workpiece redesign → versioned variant evidence + ↓ informs the prepared artifact and seam +FE-1476 delivery spine +├─ M5 traceable prebuilt workpiece → live SDCPN → provenance answer beats 1–3 +├─ M6 bounded reviewer re-elicitation → revised workpiece → scoped patch beats 4–5 +└─ M7 whole-story rehearsal → optimisation handoff beat 6 + +Other asynchronous evidence tracks, admitted only when their boundary is stable +├─ provider-visible Petrinaut schema path +├─ inferential observer-fold spike +├─ provenance interaction against a fixture manifest +├─ host-choice and session-lifecycle probes where they do not block the first tracer +└─ Chris/Yannis handoff contract +``` + +Parallel means separate issues, branches, or worktrees with their own mission authority, not several live missions in this file. A track is independently grabbable only after its input and output boundary is explicit. The shared contracts must be kept smaller than the work they coordinate; otherwise parallelism multiplies incompatible assumptions. + +# FE-1476 delivery frame + +This is the current milestone frame, not a mission. The delivery window is the end of next week from the 2026-08-31 planning session. The target is the six-beat September review-and-revise story: + +1. Show a completed requirements artifact, pre-built from a prior elicitation; no live first interview. +2. A reviewer who is not the original expert examines the SDCPN projected from that artifact. +3. The reviewer asks why a part of the net was modelled that way and receives its provenance. +4. The reviewer conducts 3–5 turns of targeted re-elicitation against one section. +5. The net changes to match without an unrelated full rebuild. +6. The revised artifact is handed to Chris and Yannis for optimisation experiments. + +“Requirements graph” does not yet name a settled implementation. The current neutral term is **evidence-backed workpiece**: an inspectable/exportable representation between conversation evidence and the SDCPN. It may prove to be versioned assertion clusters, a stronger Markdown workpiece, or another shape exposed by the first real projection tracer. Do not turn the label “graph” into a graph database, closed ontology, or typed fold before the consumer requires one. + +The milestone requires three durable relationships even if their files or stores differ: + +```text +conversation evidence → workpiece meaning +workpiece meaning → projection decision → SDCPN element +workpiece revision → bounded net change +``` + +The known delivery objects are therefore a workpiece, an SDCPN, and a provenance/derivation record connecting them to evidence. The known live operations are a provenance query and a bounded reviewer revision. Whether the provenance record is a standalone manifest or a view over persisted links remains fog. + +## Confidence map + +### High confidence — observed or owner-settled + +- FE-1476's six beats are the milestone target. +- The production door remains Petrinaut panel → AI SDK transport → Flue `ChatAgent`; Brunch is a second assistant, not a replacement for the stock modeller. +- A Flue conversation can activate the packaged runbook skill, read phase-specific resources, and emit a recoverable Markdown workpiece. +- Mission 2 proved an idempotent, model-free capture pipe over real Flue history with source excerpts and empty payloads. +- Petrinaut owns net schemas, mutations, parser behavior, and simulation contracts; Brunch must consume those contracts rather than copy their field shapes. +- A parser-valid empty net is vacuous. Delivery requires non-empty semantic inspection as well as schema acceptance. +- FE-1476 requires a traceable relationship among net elements, workpiece meaning, and conversation evidence; complete production independence between those artifacts cannot satisfy the story. +- Ordinary elicitation turns must not wait on foreground semantic sweep/fold work or return to Condition 5's minute-scale latency. + +### Medium confidence — plausible first mechanisms, not yet proven + +- Stable, versioned, evidence-backed assertion clusters may be a sufficient workpiece without a comprehensive domain ontology. +- A model-assisted projector can consume those clusters and emit both canonical Petrinaut mutations and an auditable derivation record. +- Stable caller-supplied workpiece and net-element identifiers may be enough to keep a selected revision local. +- SDCPN recognition guidance can offer non-authoritative entity/relationship mapping hints without making those hints a required semantic schema. +- Reprojecting or reconsidering a selected region and applying a bounded patch may satisfy “the net changes without a full rebuild”; the exact internal scope permitted by the delivery owner is not yet pinned. +- An asynchronous observer may consolidate settled evidence without blocking the foreground and may feed the revision operation after an explicit flush barrier. + +### Low confidence — must alter the next move + +- The appropriate granularity and identity stability of consolidated assertions. +- Whether observer consolidation preserves prior supported meaning while incorporating corrections, qualifications, and conflicts. +- Whether SDCPN mapping hints help projection more than they bias evidence consolidation. +- Whether the live Flue/provider path can expose Petrinaut's canonical nested schemas to the model. The paid construction run went 0-for-9 on `addType.elements` and yielded an empty net. +- Whether model-assisted projection can revise one linked region without unrelated net churn. +- Whether a reviewer who is not the original expert may directly supersede prior meaning or only propose a revision. +- Whether net create/save/load provides the identity and continuity FE-1476 needs. +- Whether compaction preserves evidence/workpiece recovery; this is important beyond the short rehearsal but not yet earned as a blocker for the first tracer. +- The exact optimisation artifact and scenario contract Chris and Yannis require. + +## Cross-milestone proof obligations + +These obligations belong to the milestone. Each mission should take only the first unproven subset it can discharge through the real boundary. + +1. **Workpiece sufficiency:** A cold reader can identify the model's objective, reconstruct the relevant operational account, distinguish evidence from inference/assumption, and locate consequential unresolved material. +2. **Projection fidelity:** The SDCPN is produced from the workpiece rather than by rereading the transcript as the primary model. Every consequential net region names the workpiece material and projection rationale that produced it. +3. **Evidence provenance:** A reviewer can start from a visible net element and reach the relevant current workpiece assertion or passage and its actual conversation evidence. Normalized model prose is not laundered into a user quotation. +4. **Revision integrity:** New reviewer evidence produces an inspectable workpiece revision that preserves prior supported meaning unless it is explicitly corrected, qualified, split, merged, or retired. +5. **Patch locality:** Applying the selected revision changes the intended net region, retains unrelated stable identities and behavior, and reports any necessary expansion of impact rather than silently rebuilding the world. +6. **Petrinaut acceptance:** The revised net satisfies canonical schemas, is non-empty, survives semantic inspection against the workpiece, and is usable by the optimisation consumer. Parser acceptance alone is insufficient. +7. **Interaction quality:** The reviewer can ask why and conduct 3–5 focused turns in the real panel without construction vocabulary taking over the conversation or observer work blocking ordinary turns. +8. **Failure visibility:** Provider-schema failure, observer lag if the observer is selected, unsupported projection, unresolved authority, and failed patch validation stop or visibly degrade the operation; none silently advances canonical state. + +## Mechanism restraint + +The target does not currently earn a comprehensive process-domain ontology, graph database, universal subject/predicate/value schema, deterministic capture-to-model fold, typed completion algebra, or full regeneration engine. Semantic interpretation is unavoidable; the design question is where it occurs and how it remains inspectable. + +The current candidate separates three epistemic levels: + +```text +what conversation evidence supports +what SDCPN-relevant structures guidance suggests might be present +what the projector actually represented in the net +``` + +The previous typed-map cluster (formerly numbered Mission 5) proposed required fields and unresolved gaps that would mechanically derive ask / construct / deliver. That remains a recorded hypothesis, not the new default. It re-enters only if the workpiece-to-projection tracer or construction-gap return demonstrates that model-assisted judgment cannot reliably name the smallest next question without typed demands. + +Optional mapping hints are advisory. A workpiece item may have none or several; the projector may accept, reject, or defer them. Hints do not establish completeness and do not duplicate Petrinaut payload fields. If the first real tracer works from evidence-backed prose without hints, do not add them for symmetry. + +# Retained teaching, workpiece, and seam backlog + +Not missions. These are the self-contained research and prior-design findings that must remain available when a later mission is cut. FE-1476 changes their priority and gives provenance/revision a present consumer; it does not turn any proposed mechanism into observed fact. + +## Universal teaching backlog and baseline-gated edits + +The supported universal core is objective-relative interviewing: establish intended questions, audience, boundary, horizon, accuracy need, non-claims, and tolerance for assumptions; begin with one concrete occasion and walk it before generalizing; preserve expert statement, inference, assumption, unknown, not-yet-asked, conflict, correction, omission, and loss; treat divergence as information; spend questions by information value; and stop on evidence-bearing criteria rather than fluency, headings, fatigue, or turn count. + +Candidate moves present in the research but without a reliable current home include: + +- A closing clearinghouse probe: ask what important thing was not asked before any completion claim. +- An anchored-hypothetical precondition: vary a real narrated incident rather than inviting idealized policy through free-floating hypotheticals. +- A clairvoyant definitional test before recording a quantity: define it so perfect knowledge rather than judgment could answer it. +- Contrastive and discriminating probes, including expert-versus-novice contrast, to surface cues and operational distinctions. +- A long-range consistency probe using the full conversation rather than only adjacent answers. +- A depth policy that does not accept the first answer on a load-bearing fact, while avoiding a universal turn-count rule. +- A guard against vague elicitor questions; the literature synthesis identified question vagueness as the most frequent human-interviewer mistake in its cited sample. +- Explicit exception and absence sweeps rather than treating unmentioned material as absent. +- Declined-to-answer, deferred, unknown-to-user, not-yet-decided, not-applicable, and explicitly absent as different outcomes. The current runbook IR has no clean exercised home for all of them. +- An anti-leading guard and premortem phrasing for failure-focused objectives. +- The correction discriminator: ask whether a new account corrects the old one or whether both apply under different conditions. + +Known duplication and vague guidance should constrain edits. Caveat/rabbit-hole prose overlaps failure catalogs; the restatement rule appears in several homes; typology children duplicate the last-time probe; and failure lists already repeat source catalogs. “High appetite,” “several turns produce nothing new,” and “deepen before recording” are too vague unless tied to observable behavior or an end state. Do not grow a second catalog to fix those problems. + +The main unresolved teaching calls are placement and dosage, not whether the concerns exist: the 2–4 batching guidance failed to prevent 4–10-question opening batteries because both runs asked before reading `elicitation.md`; one-question versus small shared-frame batch remains unsettled; posture collection can become an intake form; clarification versus deeper case probing remains situational; quantitative/tail scripts, closing cadence, and assent semantics need discriminating probes. The frozen prospective baseline must remain unchanged long enough to measure the current package. Teaching edits follow or run in a separately versioned campaign; they do not silently contaminate the baseline. + +## SDCPN investigation backlog + +The objective-relative investigation floor is a known slice with named holes, not every IR heading or a complete semantic model. Candidate guidance should preserve these obligations in operational vocabulary: + +1. Intended decision/comparison/worry, audience, boundary, horizon, accuracy need, and what the model must not claim. +2. One concrete occasion from trigger to end, including what flows, prerequisites, main activities, ordering, and outputs. +3. Per load-bearing input, distinguish consumed, reserved then released, or merely read/inspected; for scarce reusable inputs, ask capacity and simultaneous-demand behavior. +4. Who or what decides branches and contention; distinguish written policy from practiced behavior and retain overrides, retries, failures, recovery, and conflict. +5. Objective-relevant timing, typical and tail behavior, hidden waits, calendars, arrivals, directional losses, grouping/splitting, thresholds, and changing conditions. +6. Expert evidence, assumptions, unknowns, not-yet-asked items, conflicts, corrections, omissions, and projection limits without hardening assent, policy, hedges, or incidents into precise practiced values. +7. What observation would make the result trustworthy and which operational alternatives the expert wants compared. +8. A reconstructable process spine sufficient for construction to name the smallest consequential gap rather than fill it silently. + +Typology decisions remain provisional and strain-gated: + +- Keep the six current question shapes. Complete Grouped movement, whose questioning is thinner than its siblings. +- Strengthen Timed work to distinguish working from waiting before adding hidden waiting as a separate typology. +- Split Mode change's directional loss into time, material, availability, and what cannot run next. +- Threshold crossing does not cover condition-dependent duration, failure, or loss rate. Add the operational probe “does its state change how long or how often?” under properties/time or continuous-change guidance before promoting a new default type. +- Treat external arrival/demand as an explicit throughput trigger before adding a seventh default typology. Promote only if prospective throughput cases repeatedly omit arrivals. +- Do not add queue/waiting, priority/deadline, or escalation/approval typologies merely because those distinctions can matter; they currently overlap existing resource, branch, trigger, and lens guidance. +- A numeric threshold is mandatory only when the expert uses one or the objective depends on it; a judgeable objective does not always require a number. +- Unclear release of a reserved scarce resource is a construction blocker when alternate semantics change the objective; otherwise retain a conspicuous assumption. + +The short candidate return-to-elicitation list remains: missing objective; missing spine; unclear fate of a capped resource; policy without practiced contention; missing arrival account under a throughput objective; or missing tail behavior where the objective depends on tails. Mission 3 did not exercise this loop, so the list is a probe, not typed completion. + +The adversarial probe catalog remains available: policy versus practice; shared-resource contention; hidden waiting stored as processing time; directional changeover loss; a rare incident mistaken for a rate; unknown distribution forced into a named family; grouped work that may split; and a continuous quantity that crosses no threshold while still changing a relevant rate. Newcomer, borderline-case, same-situation/different-objective, evidence-order perturbation, and true correction after apparent readiness are useful additions. Probe metadata should distinguish acquisition miss, conservation miss, and simulator nondisclosure. + +The `Transform to PN` children still belong conceptually to construction, but two historical runs did not show vocabulary leakage. Move them only in an instrumented teaching variant; survival is weak evidence, not a license to keep duplicate construction knowledge indefinitely. + +## Workpiece structure hypotheses and observed strain + +Observed in both real Mission 3 artifacts: the Markdown IR was composed wholesale near the end rather than maintained incrementally. The six-mark epistemic vocabulary was useful, but labels did not prevent agent-authored values from reading like expert testimony or never-asked material from being filed as user unknowns. `Situation notes` aided retrieval and also duplicated other sections. `Projection losses` mixed elicitation gaps, construction choices, and genuine representational loss; the loss register should open at construction rather than speculate during elicitation. Assumptions often omitted the required reason and how-to-check. Inline and bulk unsettled marks had no authoritative home. “Not applicable” and “declined” were not exercised. Context-dependent quantities, especially directional values, were difficult to reconstruct from flat prose. + +Candidate structures remain hypotheses: + +- **A — case/process spine plus epistemic ledger:** one concrete flow carries operational context; a light ledger is the sole home for unresolved claims, assumptions, conflicts, and next questions. +- **B — entity/resource-centric register:** repeated entries make per-entity, product, direction, and quantity context easier to locate, at the cost of more semantic organization. +- **C — current shape split by authorship and phase:** retain broad headings but distinguish expert-given, agent-assumed with reason/check, construction-decided, elicitation gap, and construction-opened loss. This is the lowest-change control. +- **D — append-only journal:** retained only to bound the design space. It conflicts with the current maintained-workpiece intent and has no observed correction lifecycle strong enough to justify its cold-reading burden. +- **Objective slices, cases, then residue:** organize around the questions the model must answer, with supporting cases, evidence, assumptions, blockers, validation, and smallest next questions. This remains an alternate emphasis rather than a proven winner. +- **Versioned assertion clusters:** the current FE-1476 conversation adds this candidate: coherent evidence-backed prose units with revision lineage and optional advisory mapping hints. Its granularity and stability remain low-confidence. + +A transcript-blind reviewer should be able to state the objective; reconstruct the process and order; separate expert evidence, inference, assumption, unknown, and conflict; name unresolved contradictions; identify the smallest next questions; judge construction readiness without inventing the spine; and spot-check a claim's epistemic standing and typical-versus-tail shape. Historical cold reviews now calibrate some of these tasks, but successive revision and observer-fold behavior remain untested. + +Keep objective, boundary/horizon, concrete cases, practiced-versus-prescribed distinctions, contextual quantities, resources/contention, assumptions, conflicts, omissions, validation, and named losses. Move PN transformation knowledge into construction. Rewrite phase-mixed gap/loss material and formulaic closing claims. Cut duplicate summaries only after one authoritative home is proven. + +Rejected mechanisms retain their re-entry conditions: + +- Closed kind catalogs, slots, demand rows, and precision ladders re-enter only if the projector repeatedly cannot find consequential meaning in evidence-backed prose and advisory hints. +- Typed completion algebra re-enters only if evidence-based checks repeatedly allow unsupported readiness or cannot name the smallest next question. +- Per-statement epistemic enums re-enter only if prose/revision discipline repeatedly launders authorship or uncertainty despite targeted checks. +- Typed per-capture loss categories re-enter only if projection decisions cannot be audited through an explicit construction-opened loss account. +- Capture envelope → typed fold, `firesWhen`, motif/plugin/repertoire runtime, and one-artifact merger remain rejected until a real second consumer or observed failure requires their mechanism. + +## Prior capture/workpiece seam hypotheses and unrun probes + +The prior research compared four relationships; FE-1476 does not erase their evidence: + +- **A — complete production independence:** zero Condition 5 exposure and fully consistent with Missions 2–3, but cannot by itself answer FE-1476's required net-to-evidence provenance query. High synthesis fan-in and high context dependence would still support independence outside the delivery-specific derivation record. +- **B — support links only:** the smallest prior join, improving auditability without making capture the workpiece. Offline links require only evaluation-local statement identity; durable live links require workpiece identity stable across revisions. FE-1476 currently makes this family the nearest relevant hypothesis, but does not decide its storage form. +- **C — capture fold proposes workpiece updates:** presupposes semantic interpretation, ordered lifecycle, and update authority. It was unproven and carried more Condition 5 exposure. The inferential observer spike is a new, asynchronous version of this question, not proof that the fold is warranted. +- **D — one artifact:** capture payloads and editable workpiece entries merge. This remains refused because immutable evidence and editable semantic synthesis have different lifecycles and because it most closely recreates the prior latency/complexity failure shape. + +The prior offline shadow join remains an available non-critical-path probe: run Mission 2-style capture over a settled Mission 3 range, assign temporary ids to material workpiece statements, and grade evidence relation, epistemic treatment, lifecycle relation, and projection treatment separately. Do not collapse these into one mutually exclusive class or count explicit assumptions as evidence support. + +Useful measurements remain support coverage, synthesis fan-in, capture utility, context dependence, correction integrity, path sensitivity, and link churn across workpiece revisions. High coverage with fan-in near one and low context dependence weakens complete independence and supports links. High fan-in/context dependence supports keeping evidence ledger and semantic workpiece distinct. A fold becomes plausible only if order perturbation yields equivalent active meaning without in-loop latency and if the elicitor does not need to consult every fold. Deeper merger would require capture idempotency and editable-workpiece semantics to agree across correction, split/merge, and revision—currently unlikely. + +The former recommendation was production independence plus offline shadow mapping. FE-1476 supersedes independence only as a sufficient **delivery posture**, because visible provenance and revision are now owner-required. It does not prove a capture-store fold, one-artifact merger, live in-loop support linker, or comprehensive statement-identity system. The observer and derivation-record tracks are the smallest new probes against that changed obligation. + +# Mission 4 — owner-led runbook and workpiece redesign + +Mission 3 proved that the runbook approach technically works, not that its authored guidance or Markdown workpiece is optimal. The next mission is a design-and-evidence mission led step by step with the owner: use the completed research synthesis, historical artifacts, and frozen prospective control to manually reshape how the runbook elicits, organizes, qualifies, and presents operational knowledge. The owner expects the act of remodeling to expose edge cases and modelling assumptions that an autonomous rewrite would miss. + +This mission may start while Mission 3's baseline campaign runs because it begins from already-frozen inputs and does not mutate the control. Comparison claims wait for the completed baseline and adjudication. The mission should record each consequential redesign choice before implementing it, inspect the affected resource in context, make the smallest coherent edit, and walk concrete and adversarial examples through the changed shape before moving on. It is not a one-shot agent-generated replacement. + +The candidate throughline is: + +```text +research synthesis + historical workpieces + current authored resources +→ owner/agent inspect one observed strain or edge case +→ agree the local obligation and smallest structural or teaching change +→ manually revise the runbook/workpiece package +→ walk known and owner-supplied cases through the revision +→ freeze a versioned candidate instrument +→ run a prospective candidate campaign +→ compare against the frozen Mission 3 control with the same ruler +``` + +The likely edit surface is the existing one-skill package—`SKILL.md`, `elicitation.md`, `ir-template.md`, and only the construction/check material whose phase ownership is directly implicated. The current structural-typing specification may also need revision if an observed design decision contradicts it. The mission does not presume assertion cards, an observer, a graph, or a new runtime. + +Candidate evidence obligations: + +- The owner can review and explain each consequential change and the edge case or observed strain it answers. +- The revised package remains one real Flue skill with progressive disclosure and keeps ordinary elicitation in operational rather than PN/schema vocabulary. +- The workpiece gives one authoritative home to evidence, agent inference, assumptions with reason/how-to-check, unresolved or declined material, conflicts/corrections, and construction-opened losses without turning headings into an opening questionnaire. +- Concrete and adversarial walkthroughs cover at least opening overload, policy versus practice, contextual quantities, scarce-resource reservation/release, hidden waiting, directional loss, correction versus contextual coexistence, and genuine unknown versus not-yet-asked. +- A versioned prospective candidate run produces a recoverable inspectable workpiece through the production Flue door. The frozen ruler and independent cold/omniscient review identify gains, regressions, and remaining uncertainty relative to Mission 3's baseline. +- The result is suitable for preparing FE-1476's prebuilt artifact, but projection success is not fabricated as a redesign result. + +## Constraints already earned + +- Keep the prospective baseline files, prompts, case, ruler, and committed instrument immutable. Candidate runs write to a new versioned campaign and record their own manifest. +- Treat research as evidence and prompts for judgment, not a specification to implement wholesale. Shared-source repetition is not independent corroboration. +- The owner leads semantic and editorial decisions. The agent may present alternatives, trace consequences, edit accepted choices, and run probes; it must not silently decide the final runbook shape. +- Prefer subtraction, relocation, and clearer authority before adding more catalogs. Existing duplication and vague guidance are part of the observed strain. +- Keep universal elicitation, SDCPN investigation, workpiece structure, and PN construction distinct enough that one can change without turning every question into a schema slot. +- Do not add a comprehensive ontology, closed claim kinds, typed completion algebra, plugin runtime, observer fold, projection engine, or capture-store join to make the redesign feel rigorous. +- Preserve exact expert evidence and honest authorship. A normalized summary, agent assumption, unasked item, and explicit user unknown must not collapse into one label. +- Do not tune only to Vestera. Owner-supplied counterexamples may be used as walkthroughs, but reusable guidance must remain scenario-neutral. + +## Fog-line + +- Whether the best first change is primarily teaching order/dosage, workpiece structure, authorship/epistemic treatment, or a smaller combination. Decide one strain at a time rather than replacing the package wholesale. +- Whether the current broad headings survive, collapse into a case/process spine plus ledger, or become objective slices with supporting cases. Versioned assertion clusters remain a later possibility, not the redesign default. +- Whether batching guidance belongs in always-on routing, the skill body, or elicitation resources. The baseline must first show whether historical opening overload replicates. +- How much construction language should move out of elicitation and whether `Projection losses` should be opened only during construction. +- How to represent correction, contextual coexistence, declined/deferred material, and directional/context-dependent values without requiring per-statement semantic typing. +- The smallest candidate campaign that can reveal regression without pretending one scenario establishes universal superiority. Pin this after the redesigned instrument and baseline variance are visible. +- Which manually discovered edges belong in reusable guidance, a grader/probe catalog, the workpiece shape, or only mission evidence. + +## Stop or reorient + +- Stop if the redesign begins editing the frozen baseline instrument or grading prompts. +- Stop if the agent produces a wholesale replacement before the owner has worked through the consequential choices. +- Stop if headings or typologies become a scripted intake form or dictate the interview's opening order. +- Stop if uncertainty is handled by inventing more mandatory fields rather than preserving it honestly. +- Stop if construction, provenance, observer scheduling, or live net mutation expands into this mission merely because later missions need them. +- Stop if a candidate is called better from fluency, parser shape, or one favorable anecdote without the frozen comparison ruler and explicit regressions. + +# Mission 5 — traceable projection through the real panel + +The first unproven boundary is not a complete requirements graph. It is one evidence-backed workpiece item becoming one semantically meaningful live-net region whose provenance a reviewer can inspect through the production panel. + +A candidate tracer uses a fixed prior conversation and a deliberately prepared minimal workpiece item. The Brunch agent consumes that item, invokes the canonical Petrinaut client-tool path, creates or updates one non-empty net region with stable caller-supplied ids, records the derivation from workpiece item to net elements, and answers one “why was this modelled this way?” query by returning the workpiece account plus actual evidence excerpts. The tracer must not reread the whole transcript to invent the answer. + +Only after that path works should the mission broaden to the completed prebuilt workpiece and SDCPN needed for beats 1–3. A hand-prepared or one-off model-assisted fixture is legitimate for the first tracer if its provenance is explicit; pretending it is the final observer output is not. + +## Constraints already earned + +- Use the real Petrinaut panel, AI SDK transport, Flue `ChatAgent`, and existing `onToolCall` client execution. Do not create a second UI, server, or direct canvas bypass. +- Brunch prompting and recognition guidance remain Brunch-owned. Petrinaut schemas and payload shapes remain Petrinaut-owned and must be imported or generated mechanically. +- Start with the smallest canonical mutation subset that crosses the tracer. Do not absorb the stock modeller's full 46-tool catalog. +- The model may interpret evidence-backed prose. Do not disguise that inference as deterministic compilation. +- A derivation record must name workpiece item revisions, evidence references, net element ids, and projection rationale. Its exact storage shape is fog until the tracer exposes what the panel and revision consumer need. +- Construction success requires provider-visible schemas, non-empty output, semantic correspondence to the workpiece, and no unsupported consequential defaults. +- Stable ids must be exercised across at least one repeat projection or edit. Do not assume model-minted identity survives. +- The initial tracer does not require the asynchronous observer, a generic assertion fold, compaction, remote deployment, or a complete optimisation model. + +## Fog-line + +- Whether the current Markdown workpiece can host a stable evidence-backed item or whether a separate versioned assertion-card artifact is the smallest sufficient input. +- Whether the live client-tool route already exposes canonical Petrinaut schemas in a provider-visible form, avoiding the failed headless Valibot `looseObject` + `rawTransform` bridge. If not, determine whether Flue can accept Standard Schema or supplied JSON Schema, whether a shape-preserving Zod-to-Valibot conversion is the smallest path, or whether this is an upstream Flue requirement. +- Repair-loop behavior after canonical schema rejection: correction budget, stop condition, and whether provider-visible shape plus rejection messages are sufficient to recover from the recorded 0-for-9 failure. +- Petrinaut-core's file-format schemas and `action-schemas.ts` are separate families aligned by hand. The tracer depends on that alignment; a mismatch routes upstream rather than becoming a Brunch prose copy. +- Whether consuming the current last-`runbook-ir`-fence scrape is sufficient for the tracer or the evidence-backed workpiece becomes the first durable consumption contract. This interacts with compaction but does not justify designing persistence before the first projection. +- The smallest net region that is semantically meaningful and visually inspectable rather than a toy place/transition pair. +- How a reviewer identifies a net element for a provenance question: click context, explicit element name/id, or another existing panel affordance. +- Whether the derivation record travels with the workpiece, the net, or as a generated companion manifest. +- Whether a full desired net may be recomputed internally if only the stable diff is applied, or whether the delivery contract requires genuinely local projection computation. +- How much host-choice UI is necessary for this tracer versus a fixed Brunch mode. + +## Stop or reorient + +- Stop if the tracer requires inventing the complete assertion ontology before one item can project. +- Stop if canonical Petrinaut schemas must be manually copied into Brunch. +- Stop if the agent can only construct by rereading the transcript rather than consuming the workpiece item. +- Stop if provenance is generated as plausible prose without mechanical links to retained evidence. +- Stop if parser success can pass on an empty or semantically unrelated net. +- Stop if the first repeat projection churns unrelated identifiers and the smallest identity pin cannot explain why. +- Stop if panel integration requires Brunch-specific logic inside the Petrinaut library rather than an application-level or generic host extension. + +# Mission 6 — bounded reviewer revision to scoped net patch + +After Mission 5 establishes one traceable projection, the next mission proves beats 4–5: a reviewer who is not the original expert conducts one bounded 3–5-turn re-elicitation against that region; the system records a source-linked revision of the relevant workpiece meaning; and the Brunch agent applies a validated patch whose impact is limited to the linked net region or explicitly widened with a reason. + +The mission should begin from the Mission 5 artifact and one deliberately chosen revision with observable consequences. It is not a general correction platform. The chosen revision must be rich enough to distinguish simple overwrite from qualification, contextual coexistence, conflict, or genuine supersession; otherwise it will not test the workpiece's claimed value. + +The mechanism that creates the workpiece revision remains fog. The observer spike informs but does not block the choice and need not succeed. Candidates are an explicit phase-boundary synthesis by the foreground agent, an asynchronously consolidated assertion revision if the spike earns promotion, or the smallest combination that preserves evidence and meets latency. The mission does not assume a generic deterministic fold. + +## Constraints already earned + +- Ordinary review questions stay on the foreground runbook path and do not wait for semantic sweep/fold work. +- Before canonical workpiece or net state changes, all evidence required for that revision must be durably present and the responsible interpretation must be explicit. +- Preserve the previous workpiece revision and its evidence. A new account must not erase history merely because it is more recent. +- Reviewer authority must be represented honestly. A tentative proposal or unresolved contradiction must not silently become canonical expert truth. +- The patch consumes the current workpiece revision and current net, not the transcript as an unbounded fallback. +- Existing net ids outside the declared impact remain stable. New ids and impact expansion are reported. +- Validate through Petrinaut's canonical contracts and inspect semantic behavior against the selected revision. Tool-call success is not patch success. +- Do not make the observer, capture store, and workpiece one artifact merely to shorten the path. + +## Fog-line + +- Whether the reviewer can commit corrections directly, creates proposals pending confirmation, or has authority that varies by statement. +- Whether assertion-card revisions are stable enough across observer or foreground synthesis to anchor projection links. +- What counts as a sufficiently local patch when one operational change legitimately affects several connected net elements. +- Whether a selected workpiece item plus its linked net neighborhood gives the projector enough context without the full workpiece. +- Whether mapping hints should be persisted on workpiece revisions, generated after consolidation, or omitted because projection rationales suffice. +- The explicit synchronization point for an asynchronous observer: forced tail sweep and queue flush before revision commit is the current candidate, not a settled mechanism. + +## Stop or reorient + +- Stop if the revision path requires every conversation turn to block on extraction, typing, fold, completion, and projection. +- Stop if a changed prose item causes unrelated net regeneration with no detectable impact boundary. +- Stop if the reviewer can accidentally launder uncertainty into authoritative correction. +- Stop if previous evidence or projection rationale disappears after revision. +- Stop if the implementation grows a comprehensive semantic ontology to support one selected correction. +- If an observer is selected, stop if its failure or lag can produce a patch from stale evidence without a visible barrier. + +# Mission 7 — complete FE-1476 rehearsal and optimisation handoff + +After traceable projection and bounded revision are separately proven, broaden only enough to rehearse the complete six-beat story with the prebuilt artifact, the reviewer-facing panel, the selected revision, and the optimisation handoff. + +This mission owns integration breadth and staging, not a new semantic architecture. It should make a human witness able to decide whether the story works from the visible panel and exported artifacts. It may absorb only the host continuity and presentation work the rehearsal exposes as necessary. + +The completed artifact need not represent every Vestera fact. It must carry a coherent objective-relative slice sufficient for the selected SDCPN and optimisation experiment, with assumptions, omissions, and projection losses visible. The handoff must name what Chris and Yannis can run, which scenario/parameters accompany the net, and which claims remain provisional. + +## Constraints already earned + +- Keep the stock Petrinaut assistant working when Brunch is unavailable or not selected. Brunch remains a second assistant. +- The panel stays on `useChat` / `onToolCall`; do not rewrite it onto `@flue/react`. +- Do not splice stock and Brunch conversations. A mode choice may route them, but their histories remain distinct. +- Use the prebuilt artifact for the first beat; do not turn the rehearsal into a live first interview. +- No remote exposure while FE-1423's authentication, telemetry, state-versioning/backup, and restart-durability gates remain open. A local or preview rehearsal must be labelled accordingly. +- The optimisation handoff uses Petrinaut's published artifact/scenario boundary rather than coupling the two libraries. +- Presenter-grade polish follows proof of the visible state changes; it cannot substitute for them. + +## Fog-line + +- The exact picker location and whether mode can switch mid-net or only at start. +- Whether net id is a sufficient conversation discriminator for the rehearsal. Current localStorage maps conversation ids by `netId`, but save/load identity has not been proven. +- The smallest provenance presentation: answer in chat, linked detail panel, net-element selection, or exported manifest view. +- The exact artifact, scenario, parameter, and execution assumptions required by Chris and Yannis. +- Whether the delivery is local, preview-deployed, recorded, or live. Remote production gates do not disappear under deadline pressure. +- Which semantic checks are required before optimisation may treat the net as credible. + +## Stop or reorient + +- Stop if rehearsal defects expose a missing contract in Missions 5–6; fix the shared boundary rather than scripting around it in the demo. +- Stop if host choice rewrites or destabilizes the stock assistant. +- Stop if the handoff consumer must reconstruct model intent from the original transcript. +- Stop if optimisation runs against a net that passed only parser shape and not the workpiece-specific semantic checks. +- Stop if deployment bypasses the ratified remote-release gates. + +# Parallel and asynchronous proof tracks + +These tracks can begin before the numbered mission that may consume them. Product-changing tracks still need their own issue, branch, and mission when implementation begins. Results are evidence inputs; they do not silently rewrite another branch's authority. + +| Track | Can start from | Produces | Join gate | Does not block | +| --- | --- | --- | --- | --- | +| Frozen prospective baseline | Current committed runner and instrument | Three runs, independent grader reports, human adjudication | Mission 4 may begin manual decisions in parallel; reconcile before freezing or claiming improvement of the candidate instrument | Mission 5's projection tracer | +| Inferential observer fold | Fixed historical transcript and expected source excerpts | Evidence-backed consolidated revisions plus latency/failure observations | Decision report before Mission 6: admit observer-derived state only if evidence preservation, ordering, latency, and flush behavior pass; otherwise select foreground phase-boundary synthesis or reorient | Missions 4–5; Mission 6 is informed by this spike but does not require it to succeed | +| Provider-visible schema path | Existing failed paid run, canonical Petrinaut Zod schemas, live client-tool route | One real-model canonical nested tool call or a crisp upstream blocker | A successful canonical call admits Mission 5 construction; a crisp upstream blocker triggers Mission 5 stop/reorientation and does not count as satisfying the join | Baseline and Mission 4 redesign | +| Provenance interaction fixture | Fixed workpiece item and a tiny versioned derivation-record fixture | Reviewer-visible “why?” interaction | Freeze the smallest fixture contract before Mission 5 projection and UI branches diverge; both must consume that exact version | Provider/model construction while fixtures are used honestly | +| Host choice/session lifecycle | Existing panel routing and localStorage net/conversation mapping | Smallest proof of Brunch selection and resume for the selected artifact | Selected artifact resumes the same Brunch history, a new net starts distinct history, and stock mode remains unaffected | One fixed-mode Mission 5 tracer | +| Optimisation handoff | Existing Petrinaut artifact/scenario formats and stakeholder conversation | Written consumer input/output contract and one accepted fixture | Required before Mission 7 declares handoff | Missions 4–6 implementation | +| Simulation-backed semantic check | Hermetic non-empty net and selected workpiece expectations | Qualitative behavior comparison | Promotes into delivery only if cheap enough and discriminating | First provenance tracer | + +The first shared interface candidates are deliberately small and provisional: + +```text +EvidenceBackedWorkpieceItem +DerivationRecord +NetPatch +``` + +Do not freeze richer names or field catalogs before two tracks need them. If fixture UI and projection code cannot agree on the minimal derivation record, that disagreement is the next design evidence and parallel work pauses at that seam. + +# Inferential observer-fold hypothesis + +Not yet a mission and not part of the current production path. It is a candidate parallel spike whose result may supply Mission 6's revision mechanism. + +The observer is the candidate semantic sweep mechanism, not a harness counter parked under that label. It is a separate model-assisted editor over settled conversation ranges, neither a deterministic reducer nor the foreground elicitor calling a sweep tool. It receives new evidence plus the previously committed consolidated understanding and may rewrite coherent assertion-sized units using inference. This intentionally front-loads some interpretation while avoiding a comprehensive semantic typology. The binding may still execute the mechanical capture-store `apply-sweep`; the foreground model must not decide when to invoke it. + +A candidate consolidated item remains semantically open prose with generic mechanics only: stable id, revision, title/body, supporting evidence spans, unresolved material, lineage/change account, and optional target-formalism mapping hints. Fields such as `resource.capacity`, `shift: day`, or universal `subject/predicate/value` are not assumed. The right assertion granularity — neither atomic fragments nor one giant process digest — is itself part of the spike. + +## Mechanical shell around inferential semantics + +- Count eligible unscheduled tokens without a model call. Arm after an observed threshold; precedent is on the order of 10,000 tokens, not a locked value. +- Trigger on the next valid settled agent boundary. In Flue, `useAgentFinish` is the known “would stop” seam and also fires on `terminate: true` suspension, so the pending-affordance guard is load-bearing. Do not bind the architecture to an unverified Pi event name. +- Keep separate `scheduledThrough` and `foldedThrough` high-water marks so a lagging queue does not schedule overlapping ranges. +- Calls are asynchronous relative to the foreground, queued in semantic order, and retried on failure. A later range cannot commit against a state that excludes an earlier required range. +- A failed provider call may be retried from the same range and base revision. If a model result exists but persistence fails, retry the stored candidate commit rather than calling the model again and obtaining a different interpretation. +- Commit the observer result, revision lineage, and folded high-water mark atomically. Once committed, reinterpretation creates a new reviewed revision; it does not silently replace the old one. +- Ordinary elicitation reads neither the pending queue nor every fold. A compact re-group may occur after a larger token interval, on resume, before phase transition, or before revision commit; the prior `every XX tokens` notation named the idea, not an earned threshold. +- Mission 6's likely barrier is a forced tail sweep and queue flush after the 3–5 reviewer turns. This is a hypothesis to prove; the threshold alone may never fire during such a short review. +- If retries exhaust, mark the queue blocked, retain later ranges, and expose staleness. Questions may continue, but canonical revision or projection must not proceed from silently stale evidence. + +## Semantic obligation + +The observer may consolidate, qualify, split, merge, or rewrite assertions through inference. The safety claim is not deterministic replay. It is that every committed revision remains auditable against the prior revision and real evidence. + +A useful oracle compares: + +```text +previous consolidated meaning + newly disclosed evidence +against +new consolidated meaning +``` + +It asks whether prior supported meaning survived unless explicitly changed; new evidence was incorporated; hedges and context remained; corrections affected only what they corrected; conflicts were not harmonized away; unsupported specificity was marked; and every material statement is supportable or explicitly inferential. + +The existing elicitation-to-IR ruler and cold reviewer can seed this oracle, but they have not tested successive observer revisions. Path/order perturbation and a true correction are required before confidence rises. + +## SDCPN recognition hints + +The layer of SDCPN guidance that may later become plugin policy can name entity and relationship lenses plus tips, heuristics, motifs, distinguishing questions, common traps, and candidate projection relevance. Examples include things that flow, work that changes them, reusable constrained inputs, accumulation/waiting, operating conditions, arrivals/departures, reservation/release, consumption, blocking/enabling, routing, grouping/splitting, contention, and condition-dependent rates. + +These are attention and mapping hints, not a comprehensive domain ontology or mandatory assertion schema. An assertion revision may carry no hints or several competing hints. A hint must identify the prose it refers to and explain why; the projector records whether it accepted, rejected, or deferred it. Hints do not copy Petrinaut payload fields, establish completeness, or dictate topology. + +Evidence consolidation and SDCPN hint annotation remain logically separate even if the first spike emits both in one model response. The observer must first preserve the operational account, then annotate possible relevance. If hints bias consolidation or can be regenerated independently, split them into a later asynchronous pass. + +## Spike evidence that would justify promotion + +- Two or more settled ranges fold in order into coherent versioned items with valid evidence excerpts. +- An injected transient failure retries without duplicate commits or skipped evidence. +- A real ordinary foreground turn does not wait for observer completion and remains in the healthy teaching-turn latency class. +- A forced flush makes a short revision tail available before canonical workpiece update. +- A correction, a contextual qualification, and a conflict each preserve prior history rather than overwriting by recency. +- A cold reviewer can trace the consolidated revision to evidence and identify any unsupported inference. +- Optional SDCPN hints improve or focus projection on at least one case without degrading assertion fidelity; otherwise omit them. + +## Stop or reorient + +- Stop if the observer needs the full target ontology before it can preserve one coherent account. +- Stop if foreground questions block on observer calls or consult every intermediate fold. +- Stop if queue ordering requires a second conversation/event log beside Flue history. +- Stop if retry can duplicate or silently replace a committed interpretation. +- Stop if consolidated revisions lose prior supported meaning or launder observer prose into user evidence. +- Stop if mapping hints become required semantic slots, completion accounting, or copied Petrinaut schemas. +- Stop if the observer directly mutates the net; its first candidate role ends at evidence-backed workpiece revision. + +## Extraction thickness retained from the prior draft + +Mission 2 proved the pipe with no extraction model: one envelope per user utterance, quote equal to that text, payload `{}`. That remains the floor. + +Progressive re-entry remains an evidence ladder rather than a destination: + +1. Stub envelopes — proven. +2. A separate cheap extraction call producing quotes or opaque blobs without slot types or kind mapping — the prior unadmitted rung. The current observer spike extends this rung into inferential evidence-backed consolidation and must prove that the extra judgment pays for itself. +3. Closed typed claims or plugin proposal catalogs — the prior Condition 5 failure shape; re-enter only if a present consumer proves prose-plus-hints insufficient. + +Subagents remain undecided as a product mechanism. The specific prior idea was micro-cognitive specialists for decisioning and decomposition, not a floating multi-agent architecture and not the observer scheduler itself. + +# Delivery-adjacent host continuity + +These concerns were previously grouped as Mission 4. The FE-1476 spine now pulls only the minimum host work into the numbered mission where the real story requires it; the remainder stays here. + +## Two brains, same panel + +A person using the Petrinaut demo should be able to choose the stock modeller or the Brunch Flue agent without relaunching. Today the switch is `yarn dev` versus `yarn dev:brunch`. + +**Locked.** Brunch is a second assistant, not the new Petrinaut modeller. Panel stays `useChat` / `onToolCall`. Do not splice conversations. Stock must work with brunch-agent down. HASH embed stays stock unless opted in. Do not rewrite the panel onto `@flue/react`. + +**Fog, still unasked at the real boundary.** How both backends share an origin; where the picker lives; whether switching mid-net is legitimate or selection occurs only at start. Mission 5 may use a fixed Brunch mode; Mission 7 owns only the choice the rehearsal needs. + +## Net create/save/load as session lifecycle + +**Working assumption.** Petrinaut net id discriminates one Flue conversation per principal. Prove only when the review/resume path needs it: save/load keeps the same conversation; a new net id mints a new one. If net ids regenerate or collide, drop the assumption and rekey. + +**Facts.** Conversation ids today are a localStorage map keyed by `netId`. New session means mint another conversation id; resume means reload the same net. Archived Mission 2 keys capture by Flue conversation identity (principal + conversation id) until this proof lands. + +**Locked.** Net id is only the conversation discriminator. A distinct Brunch target-document stays later. Do not collapse “one net is the target-document”; that is an unearned product ontology and would leak into HASH entity versus demo localStorage. Two alternatives remain rejected: collapsing the net into the target-document, and sweeping into a throwaway store to splice later. + +## Compaction + +Prove the panel and transcript reconstruct across a real Flue compaction boundary (`compaction-vs-durable-history` / FE-1386) before claiming durable long-running provenance. Compaction is Flue-default and unpinned. Product control to compact or show a summarized range waits for that pin. + +The current runbook recovers its workpiece by scraping the last `runbook-ir` fence from Flue history. A compaction boundary that summarizes the fence away breaks that pattern. FE-1476 may avoid crossing a compaction boundary during the short rehearsal, but the exported provenance package must say whether it depends on live uncompacted history. + +Compaction is history reconstruction, not prompting. Do not use it to sequence the first traceable projection unless the real rehearsal crosses it. + +## Voice + +Voice is a git parent and integration constraint, not a delivery mission here. Stack on `kostandin/h-6763-openai-canonical-speech` when that branch is the parent. Use the same `POST /api/chat` dock Mission 1 named. Resolve UUID-per-net versus `petrinaut-preview:${netId}`, stolen versus configured `/api/chat`, and `submitText` with no `brunch_ask`. Brunch owns no provider audio. + +# Later / opportunistic tracks + +These are not sequenced on the FE-1476 critical path. They may ride a live mission only when its real throughline already exposes the hook. + +## Observability / eval / tracing + +Node OTel SDK → HASH collector / Tempo. Flue `instrument(...)` already exists in `app.ts`, but nothing exports. Prove `gen_ai.conversation.id` equals the Flue instance id. `dispatch()` on `/api/chat` does not propagate `traceparent`. Content capture stays off until a privacy policy. FE-1505 / FE-1423 remain production gates. + +Do not make broad OTel a proof bullet of Mission 5. Capture only the latency and tool evidence needed by the tracer; remote release remains separately gated. + +## Watch simulated conversations + +Parallel spike, not a mission. The product itch is that simulated conversations are not visually observable. Driver remains `@flue/sdk` JSON (`createFlueClient` → `send` → `wait` → `history()`), not PTY polling. Human observer is the same conversation URL. `:4321` already follows a conversation through `useFlueAgent` but only paints text; rendering `dynamic-tool`, `data-*`, and skill activation would make it useful. + +The missing capability is a second observer on the same conversation URL, not a new protocol. Herdr panes are PTYs, not browsers. Do not wait for a Herdr webview or couple every simulation to Petrinaut panel client tools. + +## Simulation-backed construct check + +Nothing yet proves that a non-empty parser-valid net behaves like the workpiece. Candidate oracle: Petrinaut's simulation engine runs the net against qualitative expectations retained with the selected objective and compares behavior. The hermetic side-quest net is a fixture; the empty paid run is not. + +This can run as a parallel feasibility track. Promote it into FE-1476 acceptance only if it gives a cheap discriminating check for the selected revision. Otherwise retain non-empty semantic inspection and make the limitation explicit at handoff. + +## AI SDK 7 `HarnessAgent` + +Undecided. It is the converse of the current door—resume a harness session by chat id. Flue already owns that session; `transport-aisdk` is the UI adapter. A Pi / Claude Code harness would be another substrate, which `binding-flue` isolates, or a Flue replacement. It is not the watch-sims surface and not part of FE-1476 without new evidence. + +# Live-mission leftovers and close inputs + +Live Mission 3 locked one off-canvas PN JSON result, Petrinaut validation, manual load as sufficient inspection, and no canvas tools. Its completed evidence and residual failures must be recorded before the next mission is cut. + +- The runbook skill packages and discloses universal elicitation, the Markdown workpiece, PN construction, and checks through the production Flue agent. +- Two historical real runs produced useful workpieces but are calibration only. The frozen prospective three-run campaign is ready and remains a parallel evidence track. +- Opening overload of 4–10 numbered questions appeared in both historical runs. The system/resource placement question remains unresolved; do not edit teaching before the frozen baseline if doing so would invalidate that baseline. +- One-shot construction ran 162–271 seconds, a distinct budget from healthy 5–23 second teaching turns. +- The validated construction side quest proved packaging, canonical callback validation, and a hermetic non-empty net. Its construct-only agent mounted exactly `getLatestNetDefinition`, `addType`, `addParameter`, `addPlace`, `addTransition`, and `addArc` through immutable Flue `initialData`; those tools remained absent from ordinary panel conversations. The one paid real-model run failed because provider-visible schemas erased nested shape: nine `addType.elements` calls encoded the array as a string. Parser acceptance of the resulting empty document was vacuous. +- Construction-discovered-gap return was unexercised. The agent delivered `partial-with-named-gaps` rather than asking the smallest next question. +- Periodic PN generation, programmatic loading, and a validated live patch remain successor concerns; they are not retroactive Mission 3 success. +- The six `Transform to PN` children still reside under elicitation despite construction owning that knowledge. Move them only in a teaching variant or mission that can observe the effect. + +The close decision should distinguish “runbook/workpiece path accepted” from “real-model semantic construction false.” Do not rewrite Mission 3 as if all original proof items passed; equally, do not keep it live merely because its falsified construction route has now become Mission 5's first boundary. + +# Standing decisions + +Not missions. + +## Ownership and teaching mechanism + +One real Flue skill remains the teaching mechanism. Do not grow a skill catalog. A concise always-on instruction routes to the skill; its body carries lifecycle procedure and its supporting resources disclose elicitation teaching, the workpiece, PN construction, and checks as needed. Flue `useSkill` is not always-on `useInstruction`, and runbook teaching may incorporate repertoire content without restoring the core YAML runtime or plugin keys. + +Ownership boundaries: + +- **Prompting and recognition guidance are Brunch-owned.** Interview policy, tips, heuristics, motifs, candidate entity/relationship lenses, and “how to build” prose live here. The latest `petrinautAiPrompt` is coverage evidence, not text to copy; its interview and “make it up” policy do not govern Brunch elicitation. +- **Contracts are Petrinaut-owned.** TypeScript API contracts and payload shapes for nets and Petrinaut are consumed by import or mechanical generation, never hand-copied. FE-1516's one-day prose drift remains the counterexample. +- **Skill packaging is Flue-owned.** An authored skill directory is statically imported through its bare `SKILL.md` specifier and mounted with `useSkill`; no custom frontmatter parser, hand-enumerated resource list, or source-relative runtime reads. +- **Assertion mechanics, if proven, are harness-owned; SDCPN hints are target-formalism policy.** Scheduling, evidence spans, revision lineage, storage, and failure semantics must not know `resource`, `shift`, `place`, or other SDCPN concepts. Optional hint vocabulary is injected guidance and remains advisory. + +The universal ↔ SDCPN provenance migration remains an editorial practice recorded per edit, not automation. Mission 3 exercised it zero times on new real evidence. + +## Capture and workpiece relationship + +Current production paths remain independent because no join has been proven. Mission 2's `apply-sweep` still writes model-free envelopes with empty payloads; the runbook path still writes no capture state. FE-1476 now supplies a concrete reason to test a narrow relationship: evidence-backed workpiece meaning must support provenance and revision. That requirement does not ratify the previous designed two-artifact join, a one-artifact capture/workpiece merger, or an idle capture store waiting for types. + +Three premature convergence shapes remain refused: + +- A comprehensive capture ledger → typed workpiece fold designed before one real assertion-to-net tracer. +- One artifact where a sweep payload is automatically the canonical workpiece update. +- Restoring kinds, slots, precision grades, completion algebra, plugin runtime, or repertoire YAML as the teaching vehicle. + +Condition 5 remains the strain threshold: typed mapping plus in-loop LLM judgment produced ordinary question turns on the order of minutes. Re-admit interpretation only off the foreground path or at an explicit phase barrier, and measure where the mechanism becomes untenable. ## Locked, not a mission - Keep the AI SDK adapter. Do not rewrite the Petrinaut panel onto `@flue/react`. -- `@flue/react` stays appropriate for brunch-agent's local debug UI. +- `@flue/react` remains appropriate for brunch-agent's local debug UI. - `binding-flue` stays a package even if it is the only binding. - Exploded-view net prototypes belong on petrinaut-website host routes, not on `:4321`. -- When `ChatAgent` leaves the app: `packages//` in libs; app remains the shell. - -## Out of scope - -- A HASH embed path that talks to Brunch. +- When `ChatAgent` leaves the app, place it under `packages//`; the app remains the shell. +- Historical Conditions 1 / 2 / 4 / 5 remain batch evidence. The useful drive loop is `createFlueClient` → `send` → `wait` → `history()`. No TUI and no retired SDCPN elicitor. +- No HASH embed path that talks to Brunch is in the current scope. diff --git a/libs/@hashintel/brunch-agent/README.md b/libs/@hashintel/brunch-agent/README.md index 36d5366912e..e3c0352a68e 100644 --- a/libs/@hashintel/brunch-agent/README.md +++ b/libs/@hashintel/brunch-agent/README.md @@ -4,8 +4,9 @@ Brunch is the stateful elicitation harness and package family at `libs/@hashinte - [`AGENTS.md`](./AGENTS.md) is the agent charter. - [`MISSION.md`](./MISSION.md) is the current objective and stop conditions. - [`MISSION.next.md`](./MISSION.next.md) is the scratchpad for later concerns (longer horizon than - one mission; not execution authority). Closed missions live under + [`MISSION.next.md`](./MISSION.next.md) is the self-contained canonical capture repository for + upcoming missions (longer horizon than one mission; not execution authority). Closed missions + live under [`docs/mission-archive/`](./docs/mission-archive/). - [`CONTEXT.md`](./CONTEXT.md) defines the domain language. - [`docs/specs/`](./docs/specs/) and [`docs/adr/`](./docs/adr/) record the harness contract and diff --git a/libs/@hashintel/brunch-agent/docs/adr/0007-harness-teaching-meets-plugin-content-at-fixed-keys.md b/libs/@hashintel/brunch-agent/docs/adr/0007-harness-teaching-meets-plugin-content-at-fixed-keys.md index d6aac0c31f3..71075048838 100644 --- a/libs/@hashintel/brunch-agent/docs/adr/0007-harness-teaching-meets-plugin-content-at-fixed-keys.md +++ b/libs/@hashintel/brunch-agent/docs/adr/0007-harness-teaching-meets-plugin-content-at-fixed-keys.md @@ -13,7 +13,7 @@ a harness package, never in app `skills/` directories), [ADR-0003](0003-three-re as code), IR Layer B's rule that interview ordering is derived from completion rather than taught, and completion rule 15 (whether a session may stop is session control, never guidance) Decided on: the `ln/fe-1406-harness-teaching-adr` branch, from the -[lineage audit](../evidence/proofs/audits/harness-teaching-lineage-audit.md) and the +[lineage audit](../evidence/audits/harness-teaching-lineage-audit.md) and the [penciled directions of 2026-08-14](../archive/planning-inputs/penciled-directions-2026-08-14.md); owning issue FE-1406 (gist: what the harness teaches) @@ -23,7 +23,7 @@ Kernel spec §11.5 has said since 2026-08-11 that **guidance ownership follows v ownership**: a plugin teaches what to notice in its formalism; the harness teaches how to work an interview situation the shared envelope can name. The rule was reaffirmed by FE-1397, ADR-0002, FE-1406, and ADR-0006's supersession map, and it has never been designed. The -[audit](../evidence/proofs/audits/harness-teaching-lineage-audit.md) finds fifteen restatements, +[audit](../evidence/audits/harness-teaching-lineage-audit.md) finds fifteen restatements, eight vocabularies, five layers, and no build; each rescoping shrank the deliverable — a designed quiver, then graduated cards, then five relocated rows — while the rule stood. What ships today is eight protocol sentences in `packages/core` followed by the plugin file's prose. diff --git a/libs/@hashintel/brunch-agent/docs/agents/git-workflow.md b/libs/@hashintel/brunch-agent/docs/agents/git-workflow.md index 8526898d18e..ff4d9c5c742 100644 --- a/libs/@hashintel/brunch-agent/docs/agents/git-workflow.md +++ b/libs/@hashintel/brunch-agent/docs/agents/git-workflow.md @@ -36,9 +36,11 @@ changes to make a stack operation proceed. ## Lifecycle -1. State the mission in `MISSION.md`. Collect successor concerns in `MISSION.next.md` without - declaring the next mission's focus or changing execution authority. On acceptance, archive the - closed mission under `docs/mission-archive/` and cut a focused `MISSION.md` from the scratchpad, +1. State the mission in `MISSION.md`. While planning context is active, capture successor concerns + in `MISSION.next.md` at conversational fidelity without changing execution authority. That draft + is the self-contained canonical source for future cuts; do not substitute an external transcript. + On acceptance, archive the closed mission, cut a focused `MISSION.md` from the next numbered + cluster, and compare the draft before and after so uncopied material stays at the same fidelity, per `AGENTS.md`. 2. After explicit approval, create its Linear issue in the `brunch-agent` project and assign the accountable human. diff --git a/libs/@hashintel/brunch-agent/docs/archive/decisions/superseded/recommendation-demo-vehicle.md b/libs/@hashintel/brunch-agent/docs/archive/decisions/superseded/recommendation-demo-vehicle.md deleted file mode 100644 index 83bf0778374..00000000000 --- a/libs/@hashintel/brunch-agent/docs/archive/decisions/superseded/recommendation-demo-vehicle.md +++ /dev/null @@ -1,95 +0,0 @@ -# Recommendation: the September demo vehicle - -**Ticket**: FE-1362 · **Formed**: 2026-08-12 · **Status**: Lu's settled position on the -wayfinder map (FE-1357) — the recommendation he brings to the integration discussion on -Tuesday 2026-08-18 (Dei, Chris, Lu; Kostandin back that week). Demo: 2026-09-17/18, ARIA -audience, format TBD. - -This document is the integration doc FE-1333's done-when asks for, offered as a recommendation -for that discussion: it covers the boundary, where sessions persist, and where the elicitor -runs. Build-approach statements within it (how the elicitation library is built) are within the -dev remit and stated as such; the integration posture and anything touching product shape are -recommendations until discussed. - -## The recommendation - -Build the September demo as a **one-off demo shell**: a purpose-built, explicitly disposable -application that consumes **two libraries** — - -1. the **elicitation library** — greenfield, built to the elicitation-kernel spec - (`docs/planning/elicitation-kernel/spec.md`): harness + CPS plugin + Flue binding, scoped to - a demo-critical slice, scavenging existing brunch surfaces opportunistically. The existing - brunch app is **not** the base. -2. the **Petrinaut libraries** — the published React UI + headless `petrinaut-core`, consumed - as-is, client-side. - -Neither library consumes the other. They meet at the **artifact boundary**: the elicitor emits -a versioned net file **plus a `scenario`** (nets carry no marking or timing — without a -scenario the net is dead), and Petrinaut consumes it through its production parser / -import-with-autolayout path. - -## Handoff mechanics on stage - -- Default beat: **in-process handoff** through the real serialize → parse path — seamless, but - the boundary crossed is the real one. -- One deliberate **"and it's just a file"** moment: save the artifact, open it in stock - Petrinaut. That is the decoupling claim made visible. -- The payoff shot is the net **running**: the elicited scenario animating the token game. A - rendered-but-dead net undercuts the "the interview produced a working model" narrative. - -## Runtime and persistence (FE-1333 coverage) - -- The **elicitor runs server-side** in the demo shell, on the Flue substrate (as the - `prototype/10-flue-roundtrip` walking skeleton already does). -- **Sessions persist in the demo shell's storage**: capture store + session-log archive behind - the kernel spec's storage port. -- Petrinaut's libraries are **purely client-side** — rendering and simulating the artifact, - with zero persistence responsibilities. -- Deployment and storage specifics remain open (map fog), judged low-difficulty. - -## Demo-critical set (demo-legibility test) - -Demo-critical, in narrative order: **(a) durable capture with provenance** (quoted evidence -spans), **(b) completion accounting** (the elicitor knows what it still lacks), **(c) the live -interpretation-render panel** — the display surface through which the audience _sees_ (a) and -(b) happening — and **(d) the artifact handoff into Petrinaut, running**. These are precisely -what the incumbent in-Petrinaut assistant cannot do: the differentiation narrative is "what a -prompt-in-a-panel cannot do." - -**Voice is conditional**: a nice-to-have considered only if (a)–(d) are very solid with -significant time to spare (then T0 push-to-talk floor, T2 live-extraction target, per -FE-1359's tiers). Note: even the _prospect_ of voice argues for the demo-shell topology — -voice bolts on at the ui/turn shell, which is only ours to modify if we own the shell. - -## Evidence topology (carry into Tuesday 2026-08-18) - -1. **Petrinaut survey** (FE-1358, `research/petrinaut-survey.md`): the artifact boundary - already exists in production — versioned file format, pure parser, import-with-autolayout - (Import currently hidden in HASH's embed: a small unhide ask to the Petrinaut team). The - incumbent assistant is browser-resident with no headless path, no queue, and no provenance - capture anywhere in the stack. -2. **Chris (Petrinaut lead), on FE-1333**: supports circumspection about coupling; confirms - both elements are libraries meant to be consumed by applications; expects HASH to consume - both in the end — "Petrinaut should not do the instantiation of Brunch directly." -3. **Standing preference**: decoupling unless evidence forces coupling — no evidence does; the - demo-legibility test _favors_ the boundary (the handoff is a beat, not a seam to hide). -4. **Voice tractability** (FE-1359): the bolt-on seam only exists in a shell we own. -5. **The "entirely new" stance** (grilling round 1) plus the differentiation narrative: the - demo's claims are exactly the kernel spec's machinery and exactly what the incumbent lacks. - -## Positions on in-flight issues - -| Issue | Position | -| ---------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| FE-1328 (extract the elicitation core) | **Build approach (dev remit).** The deliverable stands — an importable elicitation core with no server/DB/UI deps — but "extraction" is figurative: the core is rebuilt greenfield by abstraction and extension per the kernel spec. Literal extraction would import brunch's design debt and gaps. | -| FE-1329 (make the brunch elicitor generic) | **Build approach (dev remit).** Genericity arrives via the harness/plugin split — the CPS plugin is a thin shell over a generic harness — not by retrofitting brunch. | -| FE-1331 (start elicitation from create-new-net) | **Recommend deferring** — the demo-shell recommendation contradicts it for September; in-Petrinaut initiation is the natural post-September consumer topology (once HASH consumes both libraries, per Chris's framing). Deferred, not rejected; product call stays with the PM side. | -| FE-1333 (define the integration) | **Answered by this document as the recommended position**; stays open for Tuesday's discussion. | -| Dora's PRO-98 claim #5 (in-Petrinaut one-shot-then-iterate initiation) | **Recommend against for September** — under this recommendation, session initiation happens in the demo shell. Recommendation with reasons delivered as a comment on PRO-98 (2026-08-12). | - -## Left open (map fog) - -- **Package naming**: `brunch-lite` / `brunch-core` are interim candidates; a post-September - step could fold back into `brunch` proper as a monorepo with the harness as a sub-path - package. Not important now. -- **Deployment & storage specifics** for the demo shell (remote/sandbox story). diff --git a/libs/@hashintel/brunch-agent/docs/archive/external-snapshots/open-questions-elicitation-design-2026-08-11.md b/libs/@hashintel/brunch-agent/docs/archive/design/open-questions-elicitation-design-2026-08-11.md similarity index 100% rename from libs/@hashintel/brunch-agent/docs/archive/external-snapshots/open-questions-elicitation-design-2026-08-11.md rename to libs/@hashintel/brunch-agent/docs/archive/design/open-questions-elicitation-design-2026-08-11.md diff --git a/libs/@hashintel/brunch-agent/docs/archive/evaluations/README.md b/libs/@hashintel/brunch-agent/docs/archive/evaluations/README.md new file mode 100644 index 00000000000..6ca6360b986 --- /dev/null +++ b/libs/@hashintel/brunch-agent/docs/archive/evaluations/README.md @@ -0,0 +1,9 @@ +# Retired evaluation instruments + +This directory holds concise human-readable retirement records for evaluation instruments that +are no longer supported. A record names the replacement, the final evidence bundle, and the git +revision containing removed executable source. + +Do not copy runnable code here for compatibility. Delete obsolete runners and tests after their +provenance has been recorded; immutable outputs remain under +[`docs/evidence/evaluations/`](../../evidence/evaluations/). diff --git a/libs/@hashintel/brunch-agent/docs/archive/meetings/expert-meeting-prep-2026-08-11.md b/libs/@hashintel/brunch-agent/docs/archive/meetings/expert-meeting-prep-2026-08-11.md deleted file mode 100644 index c10fdbe8121..00000000000 --- a/libs/@hashintel/brunch-agent/docs/archive/meetings/expert-meeting-prep-2026-08-11.md +++ /dev/null @@ -1,150 +0,0 @@ -# Expert meeting prep — cyber-physical process models & elicitation - -2026-08-11. Prep for meeting with modelling-expert colleague. Purpose: stabilize and bound -understanding of the "cyber-physical" domain, and capture how an expert modeller would -himself elicit, from a domain expert, what he needs to build a faithful model. - -Sources synthesized: Brunch — September Plan (Notion), Production Process Scheduling -Optimization (Notion, the one Spec'd exemplar), Petri net business use cases (Notion page + -database), SDCPN Library — Ideas (inbox), SAILS public report (inbox). - ---- - -## A. What already seems stable (don't spend meeting time re-deriving) - -1. **In-house "SDCPN" decomposes into feature axes**, not one monolith. PN class: Timed · - Stochastic · Deterministic · Statically vs Dynamically Typed/Coloured · Cyclic/Acyclic. - PN features: subnets · concurrency · execution conflict resolution · resource contention · - live data feed. A given use case draws the axes it needs (the scheduling exemplar is - statically-coloured timed, in deterministic and stochastic variants). -2. **A consistent anatomy template** for describing a candidate system: physical layer / - cyber layer / events / continuous state / emergent behaviour (SDCPN Library doc), echoed - in the DB schema (Physical system, Cyber component, Phenomena). -3. **What a "done" model definition looks like** (the scheduling exemplar's table of - contents): one-liner → problem & context → system sketch (physical + cyber) → why this - formalism → formal problem statement (Given / Decide / Maximise) → places, transitions, - colours → questions the model answers → data requirements → limitations → validation bar. -4. **The elicitation-heavy inputs are the unwritten ones.** Scheduling doc, verbatim: "the - family taxonomy, ramp scrap structure and penalty weights are the three things nobody has - written down, and all three are load-bearing." Pattern: taxonomies, cost/penalty weights, - unwritten constraints ("products that 'always' run on line 2"). -5. **Validation notion**: reproduce a historical period's actual behaviour from recorded - inputs before counterfactuals mean anything; closed-form sanity checks (net with failures - off must reproduce the spreadsheet formula). -6. **The net evaluates; it does not decide.** Optimisation/analysis layers sit on top and are - Petrinaut-team scope. Also: output may be a _model_ (description of a system) or a _plan_ - (actions to undertake) — FE-1330 names both. -7. **"Cyber" in practice so far = enterprise data systems** (ERP, MES, historian, CMMS, - scheduling layer), i.e. the sources/consumers of model data — not exotic control loops. -8. **SAILS framing**: the net is a candidate _world model_ in the gatekeeper triad (world - model + safety specification + verifier); tacit knowledge is a named deployment barrier; - practitioners distrust what they can't edit or trace. - -## B. Meeting shape (suggested, ~55 min) - -1. **Bound the domain** (5–10 min) — his operational definition of "cyber-physical control - system"; where the modelled-system boundary sits. -2. **Role-play elicitation** (15 min) — he interviews you (or narrates) for one scenario he - knows cold; you capture questions _and their grounding_. -3. **Completeness & cross-examination** (15 min) — his definition-of-done and his traps. -4. **The SDCPN feature ladder** (15 min) — which expressive capabilities a system must _earn_ - and what evidence earns each. -5. **Meta** (5 min) — what he wishes he'd asked earlier in past engagements; where interviews - fail. - -## C. Sharpened questions - -### C1. Bounding the domain - -- "Define 'cyber-physical control system' the way you'd defend it to a referee. What's an - example of a system that is _not_ one, that a layperson might think is?" -- "In the scheduling spec, the 'cyber' side is ERP/MES/historian. Is the cyber layer always - just the data estate, or does it sometimes include an existing automated controller whose - behaviour must itself be modelled?" -- "Where does the model boundary sit relative to the controller being designed? (SAILS: the - net is the _world model_, the AI is the _controller_.) When you model, are you modelling - the plant, the controller, or both — and how do you decide?" - -### C2. His elicitation questions, with grounding (the PRO-98 seed) - -- "You're in front of the plant's master scheduler, blank page. What are your first five - questions — and for each, which model element does the answer feed?" (Structure? colour? - rate? constraint? objective?) -- "What's the _dependency order_ of your questions — what does an early answer change about - what you ask next?" (An elicitor needs interviewing policy, not just a checklist.) -- "Which answers do you ask for as _numbers_, which as _distributions_, which as _stories_? - When do you ask for 'a specific bad day' rather than an average?" -- "How do you elicit things nobody wrote down — taxonomies, penalty weights, unwritten - constraints? What phrasings actually work on a domain expert?" -- "How much of a net do you assemble from recurring motifs (queue/buffer, resource pool, - failure/repair, changeover, inspection) versus invent fresh? Could you enumerate your motif - catalogue?" — _high leverage: if nets are mostly motif instantiation, elicitation can - interview against a motif catalogue instead of synthesising free-form structure._ - -### C3. Completeness criteria (definition-for-purpose-of-modelling-and-simulation) - -- "What do you check before declaring 'I can now build this'? Is there a minimal category - set — structure/topology, types & colours, rates & distributions, initial marking, - objective & penalties, constraints (hard/soft/unwritten), data bindings, validation data?" -- "Is completeness _question-relative_ — complete only relative to the questions the model - must answer (every spec'd use case carries a 'questions the model answers' table)? Should - an elicitor therefore elicit the questions first?" -- "PRO-99 wants 'a written list containing all the facts necessary to make the net.' Is a - fact-list the right form, or is the real criterion behavioural (can reproduce a historical - trace)?" -- "What would you accept as evidence that a model definition elicited by an AI interviewer is - complete? What's your acceptance test?" (Feeds PRO-104 benchmarks.) - -### C4. Cross-examination (gap & weak-assumption tests) - -- "What are your standard traps? e.g.: token conservation ('where do these entities come - from and where do they go?'); boundary probing ('what did you deliberately leave out?'); - conflict policy ('when two X compete for one Y, who wins, by what rule?')." -- "How do you catch _data-vs-decision conflation_? (Scheduling doc: supplier-delay belongs in - the Materials guard, deliberate idling belongs in `wait` — same field, opposite meanings.)" -- "How do you detect an _assumed_ quantity that should be _emergent_? (Line throughput is the - canonical case: the spreadsheet assumes it; the net derives it.)" -- "What contrastive cases do you use when two interpretations of the expert's words diverge? - Give a real example." (Maps to our disambiguate-style questioning.) -- "When domain experts disagree with each other, what do you do — and what should an AI - interviewer do?" -- "What arrives late and forces rework? What question, asked in week one, would have - prevented it?" - -### C5. The SDCPN feature ladder (which systems _earn_ which capability) - -For each capability: **what evidence in a domain expert's answers tells you the system needs -it — and what tells you it doesn't?** - -| Capability | Earning question (draft — have him correct) | -| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Timed | Do durations matter to any question the model must answer? | -| Stochastic | Does variability change a decision, or would nominal values give the same answer? Which distributions, from what data? | -| Coloured (static) | Do token attributes change behaviour (durations read from token colour), or is one token type enough? | -| **Dynamically coloured** | Does continuous state evolve _between_ events and feed back into event timing/guards (degradation, temperature, battery)? Or can it be discretised into thresholds? | -| Guards / conflict resolution | Is there a decision rule at contention points, and is it policy (elicitable) or optimisation (external)? | -| Subnets / templating | Are there repeated structural units that must stay in sync? | -| Live data feed / marking injection | Will the model be re-run from observed current state (reactive use)? | -| Cyclic | Steady-state operation vs one-shot horizon? | - -- "My working hypothesis: _most systems don't earn the full SDCPN stack, and a simpler PN - variant — or a different structure entirely — is often the better model._ Where am I wrong? - Which capability is more often needed than people expect, and which is prestige?" -- "What can a Petri net of any variant _not_ capture that you routinely need? (Goals, - rationale, spatial layout, continuous control laws…) Where do you record those?" - -### C6. Meta / practice - -- "In your last three modelling engagements, how much calendar time was elicitation vs - model-building vs validation?" -- "If you had an AI interviewer that produced the fact-list + provenance for you, what would - you _not_ trust it with?" -- "What makes a natural-language summary of a net trustworthy to a domain expert?" (FE-1335's - deterministic-summary requirement.) - -## D. Capture instructions (for after) - -Meeting notes → `docs/inbox/` with the usual timestamped name. The outputs feed, at minimum: -the elicitation checklist (PRO-98), reference-use-case criteria (PRO-99), the -SDCPN-earning/abstraction-ladder question, and the motif-catalogue hypothesis. These become -resolved fog or ticket seeds when the map is charted. diff --git a/libs/@hashintel/brunch-agent/docs/archive/migrations/hash-monorepo-import-plan.md b/libs/@hashintel/brunch-agent/docs/archive/migrations/hash-monorepo-import-plan.md deleted file mode 100644 index 63400b0fe84..00000000000 --- a/libs/@hashintel/brunch-agent/docs/archive/migrations/hash-monorepo-import-plan.md +++ /dev/null @@ -1,322 +0,0 @@ -# HASH monorepo import plan - -**Status:** recommended execution plan for FE-1437 (the monorepo import). This is a bounded -cross-effort plan: it settles as a record when `hashintel/hash` is the sole writable authority for -Brunch. [ADR-0004](../../adr/0004-in-petrinaut-staging-and-the-monorepo-import.md) owns the accepted -decision to move; [COORDINATION](./COORDINATION.md) owns the live handoff threshold and sequencing. - -## Recommendation - -Assimilate Brunch into HASH's Yarn/Turbo workspace as native child workspaces of one Brunch -context root. Preserve the current package boundaries; do not preserve the current Bun workspace -as a nested monorepo and do not collapse it into one implementation package. - -The current workspaces are architectural modules, not a repository boundary that must survive. -Their tested direction remains useful after HASH becomes their workspace root: - -```text -libs/@hashintel/brunch-agent/ # context and agent-session root; not a workspace -├── CONTEXT.md -├── docs/ -├── .agents/ -├── .claude/ -└── packages/ - ├── core/ # @hashintel/brunch-agent - ├── binding-flue/ # @hashintel/brunch-agent-binding-flue - ├── transport-aisdk/ # @hashintel/brunch-agent-transport-aisdk - └── plugin-gherkin/ # @hashintel/brunch-agent-plugin-gherkin - -apps/brunch-agent - package: @apps/brunch-agent - role: remote server, application wiring, local diagnostics - | - HTTP / AI SDK stream - | - v -apps/petrinaut-website --------> libs/@hashintel/petrinaut -``` - -`apps/petrinaut-website` is the compile-time meeting place. The Brunch packages remain -renderer-agnostic; `@hashintel/petrinaut` remains elicitor-agnostic; and `@apps/brunch-agent` -remains Petrinaut-independent. The server and website meet only through the AI SDK/HTTP transport. -The website owns the client mode, host-supplied interactive tool definitions, and remote transport -wiring. - -## Package ownership and public surface - -Use `@hashintel` for every reusable Brunch package whose contract is intended to work outside the -HASH applications. Use `@local` only for code whose contract is meaningful inside `hashintel/hash` -and nowhere else. Package scope and publication state are separate decisions: HASH already -contains private packages under `libs/@hashintel/`, so an `@hashintel` name does not require -publishing during the import. - -Here **supported package** means a named, installable boundary that the Brunch maintainers own, -document, and include in build and boundary gates. It does not promise publication, semantic -version stability, or external support while `private: true` remains set. - -| HASH path | Package | Owned contract | Import posture | -| ------------------------------------------------------- | ----------------------------------------- | --------------------------------------------------------------------------------- | --------------- | -| `libs/@hashintel/brunch-agent/packages/core` | `@hashintel/brunch-agent` | Harness mechanism, substrate-neutral protocols, storage port, plugin SDK | `private: true` | -| `libs/@hashintel/brunch-agent/packages/binding-flue` | `@hashintel/brunch-agent-binding-flue` | Flue binding and current local-file storage implementation | `private: true` | -| `libs/@hashintel/brunch-agent/packages/transport-aisdk` | `@hashintel/brunch-agent-transport-aisdk` | AI SDK UI-message-stream transport | `private: true` | -| `libs/@hashintel/brunch-agent/packages/plugin-gherkin` | `@hashintel/brunch-agent-plugin-gherkin` | Gherkin target plugin and reference implementation | `private: true` | -| `apps/brunch-agent` | `@apps/brunch-agent` | Deployment, authentication, environment configuration, HTTP mounting, diagnostics | always private | - -This placement follows the intended portability: - -- A consumer should be able to install the harness with only the binding, transport, and plugins - its deployment needs. -- Binding and transport packages are part of how Brunch supports different substrates and UI - shells; treating them as `@local` would falsely describe them as HASH-only wiring. -- FE-1437 moves the existing local-file store with the Flue binding. FE-1441 separately decides and - adds the Postgres implementation behind the unchanged storage port. An environment-neutral - implementation belongs to the binding; HASH credentials, deployment manifests, and - environment-specific composition stay in `@apps/brunch-agent`. -- No `@local` Brunch package is created during the import merely to defer a publication decision. - -### Export and dependency rules - -1. `@hashintel/brunch-agent` names the current `core` contract. It does not re-export bindings, - transports, plugins, or application code through an umbrella facade. -2. Each extension package has its own explicit exports and depends inward on - `@hashintel/brunch-agent`; core never imports an extension package. -3. Plugins import core only. Bindings import core and their substrate. Transports import core and - their wire library, never a binding. -4. `apps/brunch-agent` composes the Brunch packages and remains Petrinaut-independent. - `apps/petrinaut-website` is the only compile-time Brunch–Petrinaut composition point. -5. The existing role nouns (`binding-*`, `transport-*`, `plugin-*`) remain in the package names so - dependency direction stays legible. - -ADR-0004's singular `@hashintel/brunch-agent` name remains the harness package. Its amendment makes -the companion package family explicit; it does not turn the harness into an umbrella package. - -All four libraries remain `private: true` through FE-1437. Publishing is a later, independent -release decision: remove `private` only when there is a named consumer and a reviewed versioned -contract. At that point HASH's normal Changesets process and `workspace:^` rule apply. Deferring -publication must not force another namespace or directory migration. - -## Application disposition - -The accepted end state re-charters the standalone `apps/dev` as HASH's `apps/brunch-agent`: the -remote server application carrying forward the target-gallery and diagnostic-surface charter as -internal operational roles. -The imported [route map](../../../../../../apps/brunch-agent/src/app.ts) mounts the Flue agent -router, the Petrinaut chat transport, and the production diagnostic assets. Re-chartering that -application avoids a second app around those same routes. - -ADR-0004, ADR-0002 N3, the kernel spec, and FE-1437 record that re-charter explicitly as of the -cutover preparation. If the deployed server later proves to require a materially different -runtime, that evidence can justify a second app then. - -## Authority threshold - -The import is an authority handoff, not a freeze on all harness development: - -```text -brunch-lite authoritative - FE-1434 + FE-1435 verdicts landed - FE-1388/1389/1390/1399 review stack merged - | - v - == FE-1437 import == - | - v -hashintel/hash authoritative - FE-1440 website wiring + FE-1441 deployment -``` - -FE-1438 (client-tool round-trip) and FE-1439 (private durable sessions) may land before FE-1437 and -travel with the imported history. The write freeze occurs when the final standalone head SHA is -recorded and its history-import commit is created on the FE-1437 HASH branch. From that point, -unfinished work continues only in HASH; after FE-1437 lands, HASH is the canonical repository. -There is no dual-write period or compatibility bridge between repository layouts. - -## Execution plan - -### 1. Prepare the cutover - -- Satisfy the gates in `COORDINATION.md` and take a final green baseline in `brunch-lite`. -- Amend ADR-0004 and FE-1437 to clarify the package family and the `apps/dev` re-charter. -- Record the final standalone `main` SHA, then stop accepting new standalone changes before - creating the history-import commit. If that SHA must change, regenerate the import before any - HASH-side semantic work proceeds. - -### 2. Import history without creating a permanent subtree - -- Create a disposable clone of `brunch-lite`; never rewrite the shared checkout. The import input - is the complete ancestry of the recorded `main` SHA, including its merge commits. Unmerged - branches, pull-request refs, and unrelated tags are not inputs unless the preparation step names - one explicitly. -- Prefix that ancestry under a temporary path such as `imports/brunch-agent` using - `git filter-repo --refs refs/heads/main --to-subdirectory-filter imports/brunch-agent`. -- Merge that history into the FE-1437 branch of `hashintel/hash` without squashing. -- Format the imported files with HASH's mandatory formatter before moving them; the pre-commit - hook otherwise combines formatting with the path change. -- Move each implementation and test tree to its final - `libs/@hashintel/brunch-agent/packages/*` or `apps/brunch-agent` path. Move and convert manifests - in the native-workspace registration commit because HASH's pre-commit Yarn validation requires a - matching lockfile as soon as a manifest enters workspace discovery. -- Run `git blame` and `git log --follow` on one pre-existing source file and one pre-existing test - file from each of the five moved workspaces. Every sample must reach a pre-import commit. - -This is a one-time history merge, not an ongoing `git subtree`, submodule, or synchronization -relationship. - -### 3. Dispose of repository-level material deliberately - -- Use `docs/INDEX.md` plus the tracked files outside `apps/`, `packages/`, and `test/` as the - disposition inventory. For every entry, record one outcome in the FE-1437 work: move to a named - HASH path, retain as an explicitly historical record, or remove after import with a reason. -- The named context root is `libs/@hashintel/brunch-agent/`; it is not a workspace and has no - package manifest or lockfile. Its `docs/` directory is the home for living documentation. Move - there - everything that still governs the imported code: ADR-0002 (the placement rules behind the - boundary gates), ADR-0004, the amended kernel spec, and this plan's own settled record. Keep - `CONTEXT.md` at the context root and link this home from every package README. -- Re-home the cross-effort control surfaces that remain live after the handoff to the same home. - At import time FE-1440, FE-1441, and FE-1442 are still open, so `COORDINATION.md` and - `SPEC-LEDGER.md` qualify. Historical planning need not remain live. -- The agent working methods (`AGENTS.md`, `CLAUDE.md`, the `docs/agents/*` protocols, the - `arc-close` skill, and the INDEX gate in `test/docs-index.test.ts`) receive an explicit - recorded decision rather than disposal by omission: either re-author them under HASH's own - agent-guidance conventions, or retire them and restate the practices that outlive this - repository — the issue-writing contract, triage roles, and documentation protocol govern the - Linear-side work regardless of where the code lives. Re-author them as the context's operating - surface without creating a nested package-manager root. -- Do not carry standalone CI, Bun lockfiles, or repository setup forward. -- The disposition is complete only when every inventory row resolves to its recorded destination - or removal commit. Removing a file after the mechanical import does not remove its Git history. - -#### Repository-level disposition inventory - -Paths below are relative to the repository root. A directory pattern covers every indexed child; -the import review checks the expanded tracked-file list against these rows before removal. - -| Standalone material | Disposition in `hashintel/hash` | -| ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `CONTEXT.md` | Move to `libs/@hashintel/brunch-agent/CONTEXT.md`; remains the Brunch context glossary. | -| `docs/adr/*.md` | Move to `libs/@hashintel/brunch-agent/docs/adr/`; all four decisions still govern imported code. | -| `docs/planning/elicitation-kernel/spec.md` | Move to `libs/@hashintel/brunch-agent/docs/spec.md`; remains the package contract. | -| Other `docs/planning/elicitation-kernel/**` | Retain under `libs/@hashintel/brunch-agent/docs/history/planning/elicitation-kernel/` as the settled design record. | -| `docs/planning/process-model-elicitation/**` | Move to the same relative path under `libs/@hashintel/brunch-agent/docs/`; the FE-1357 effort and its open issues remain live after import. | -| `docs/planning/_shared/**` | Move to the same relative path under `libs/@hashintel/brunch-agent/docs/`; settle this import plan after FE-1437 lands, while the other control documents keep their declared lifetimes. | -| `docs/planning/legibility-sweep/**` | Move to the same relative path under `libs/@hashintel/brunch-agent/docs/`; preserve each indexed document's active or settled status until its owning issue closes. | -| `docs/reference/**` | Move to `libs/@hashintel/brunch-agent/docs/reference/`; preserve provenance and existing consumers. | -| `docs/INDEX.md` | Re-author as `libs/@hashintel/brunch-agent/docs/INDEX.md` over the moved living and historical set. | -| External canonical entries in `docs/INDEX.md` | Preserve their URLs and `external` status in the re-authored INDEX; no external document is copied into HASH. | -| `docs/agents/**` | Re-author under `libs/@hashintel/brunch-agent/docs/agents/` for HASH paths and Graphite (`gt`); preserve the issue-writing, triage, documentation, domain, posture, legibility, arc-close, and Flue-routing disciplines. | -| `AGENTS.md`, `CLAUDE.md` | Re-author as context-local guidance at `libs/@hashintel/brunch-agent/`; defer to HASH root guidance and link the re-homed agent protocols. Add pointer files under `apps/brunch-agent/`. | -| `.agents/skills/arc-close/SKILL.md` | Re-author under the context root and mirror it under `.claude/skills/arc-close/`; the wrapper delegates to the canonical `docs/agents/arc-close.md`. | -| `test/docs-index.test.ts` | Port to the imported package test suite and point it at the re-authored package index. | -| `scripts/linear-project-graph.ts` | Move to `libs/@hashintel/brunch-agent/scripts/linear-project-graph.ts`; retain project-registry auditing while `brunch-agent` remains the Linear ownership boundary. | -| `.github/workflows/ci.yml` | Remove after HASH tasks and CI gates cover the imported workspaces; do not nest standalone CI. | -| `bun.lock`, `bunfig.toml`, root `package.json` | Remove after HASH manifests and lockfile install successfully; do not retain a nested workspace root. | -| `.oxfmtrc.json`, `tsconfig.base.json`, `tsconfig.json` | Remove after each workspace adopts HASH's formatter and TypeScript conventions. | -| `.gitignore` | Remove after any still-relevant package-local patterns are represented by HASH's ignore rules. | -| `docs/inbox/.gitkeep` | Remove; HASH already supplies the repository root and an empty standalone inbox has no content to preserve. | - -### 4. Adopt the HASH toolchain - -- Convert manifests to native HASH workspaces and the package names above. -- Use `workspace:*` while packages are private. Do not add exceptions to HASH's dependency - constraints merely to preserve standalone-repo versions. -- Replace `bun:test` with Vitest and replace the small number of Bun runtime APIs with Node or web - platform equivalents. -- Adopt HASH's package-level TypeScript, oxlint, Vite/Vitest, and Turbo task conventions rather - than wrapping the old root scripts. Every imported library exposes `build`, `lint:eslint`, - `lint:tsc`, and `test:unit`; the app exposes the same four tasks plus its normal `dev` task. -- Reconcile dependency versions through HASH's constraints. The AI SDK wire package already - matches the website's AI SDK major. Flue and Valibot do not yet exist in HASH: add their required - versions to the imported manifests, run `yarn lint:constraints`, and resolve any conflict rather - than adding a Brunch-specific constraint exemption. -- Remove `bun.lock`, `bunfig.toml`, Bun-specific root configuration, and the old workspace root - only after their HASH replacements run. - -### 5. Preserve architectural enforcement - -Port the current -[boundary suite](../../../packages/core/test/architecture/boundaries.test.ts) and its -[workspace scanner](../../../packages/core/test/architecture/workspace.ts) instead of replacing them -with prose: - -- Keep the manifest and source-import checks that enforce plugin/core, binding/core/substrate, and - transport/core/wire directions. -- Adapt package discovery and expected names to HASH paths and scopes. -- Use `yarn.config.cjs` constraints for declared workspace relationships and the ported source - scanner for actual imports. -- Do not claim that forbidden third-party imports physically cannot resolve under HASH's hoisted - `node_modules` linker. The current Bun resolver proof depends on isolated installation; replace - that portion with an honest gate over manifests and source imports rather than a green false - equivalent. -- Retain the fail-loud Flue entrypoint checks: directive placement, pinned agent identity, mount, - and storage entry. Port the - [bundle assertions](../../../../../../apps/brunch-agent/test/build-artifact.test.ts) that prove the - emitted server registers every agent, mounts the router and conversation store, carries no model - key, and that emitted HTML points to a built client asset. -- Prove the negative oracle once during the move by temporarily adding an `@flue/runtime` import to - the Gherkin plugin in the disposable import worktree: the targeted boundary test must fail, then - pass again after the mutation is removed. - -### 6. Complete FE-1437 before integrating the applications - -FE-1437 is complete when the imported package family builds and tests natively in HASH, history is -traceable, and the architectural gates hold. The application runtime proof ports -`apps/dev/test/petrinaut-chat.test.ts`: POST the existing conversation fixture through the mounted -application route with the faux provider and assert the AI SDK stream, without a Petrinaut website -checkout. Keep the following in later issues: - -- FE-1440 commits the Petrinaut website's elicitor mode and transport switch. -- FE-1441 adds HASH deployment, Postgres-backed storage, rate limiting, and origin policy. -- Publication of any Brunch package remains separate from both the import and demo deployment. - -### 7. End standalone authority - -- Mark `hashintel/hash` as the canonical source in the old repository's landing page. -- Close or redirect automation that could accept new standalone changes. -- Archive the old repository only after the HASH branch is landed and the history checks pass. - -These are shared-state actions and require explicit approval when FE-1437 executes them. - -## Verification gates - -| Gate | Required evidence | -| ----------- | ------------------------------------------------------------------------------------------------------------ | -| Baseline | Standalone lint, format, typecheck, all tests, and build pass at the recorded import SHA | -| Workspace | HASH install and constraints pass with no Brunch-specific exemption | -| Packages | Each Brunch workspace exposes and passes `build`, `lint:eslint`, `lint:tsc`, and `test:unit` | -| Boundaries | A temporary Gherkin-plugin → Flue import fails the targeted gate; the restored tree passes | -| Runtime | The application builds both bundles and the faux-provider POST passes through its mounted conversation route | -| History | One source and one test file from every moved workspace reach pre-import commits by blame and follow-log | -| Removal | No Bun lockfile, nested workspace root, or Bun runtime/test import remains | -| Integration | FE-1440 separately proves the real Petrinaut website against the imported server | - -## Trade-offs - -**What native assimilation buys** - -- Atomic changes across Brunch, Petrinaut, and the website. -- One dependency policy, lockfile, CI graph, security-update path, and deployment environment. -- No publishing or local-link ceremony for product integration work. -- Package boundaries remain explicit while cross-package refactors become easier. - -**What it costs** - -- A one-time Bun-to-HASH tooling port. -- A larger install, CI, and review context for Brunch-only work. -- Root dependency constraints may expose Flue, Vite, React, or schema-library conflicts. -- The standalone repository's focused iteration loop is replaced by HASH's broader governance. -- The history import and document disposition require a deliberately staged review. - -## Alternatives not recommended - -| Alternative | When it would be better | Why it is not the current choice | -| ------------------------------------------------------ | -------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | -| Keep Brunch separate behind HTTP or published packages | Independent ownership, release cadence, deployment lifecycle, or external consumers dominate | Reverses ADR-0004 and loses atomic work across the current product seam | -| Collapse everything into `@hashintel/brunch-agent` | The package boundaries prove ceremonial and there is one enduring consumer | Erases currently tested substrate/plugin/transport directions | -| Nest the Bun monorepo inside HASH | Very short-lived import staging only | Creates two package managers, lockfiles, task graphs, and ambiguous workspace discovery | -| Git submodule or permanent subtree | Source must remain independently authoritative | Preserves the cross-repository coordination cost the move is intended to remove | - -The reconsideration trigger is concrete evidence of independent product life: a separate owner, -release cadence, external consumer base, or runtime that must deploy independently of HASH. Without -that evidence, native workspaces with the reusable package family under `@hashintel` are the -smallest coherent end state. diff --git a/libs/@hashintel/brunch-agent/docs/archive/migrations/issue-pr-legibility-2026-08-20/README.md b/libs/@hashintel/brunch-agent/docs/archive/migrations/issue-pr-legibility-2026-08-20/README.md deleted file mode 100644 index 843f491fa76..00000000000 --- a/libs/@hashintel/brunch-agent/docs/archive/migrations/issue-pr-legibility-2026-08-20/README.md +++ /dev/null @@ -1,144 +0,0 @@ -# Issue and PR legibility migration package - -This package freezes the 20 August 2026 source records and proposed legibility edits for the -`brunch-agent` Linear project and its `brunch-lite` pull requests. Preparing and committing the -package did not write to Linear or GitHub. Application began only after separate approval; -[`apply-log.jsonl`](apply-log.jsonl) is its append-only audit record. - -The migration completed on 20 August 2026. Final reconciliation verified all 67 Linear targets, -all 25 GitHub targets, and the title, body, and URL of all six excluded issues against the -frozen source. FE-1333's `updatedAt` moved to `2026-08-20T10:50:17.548Z` while the first FE-1357 -write was in flight; FE-1333 is the duplicate issue tied to FE-1357, and no excluded content -changed. The other five excluded timestamps remain exact. - -## Scope - -The Linear snapshot contains 73 issues. The proposals cover all 67 Lu Nelson-authored issues, -including sub-issues. FE-1328, FE-1329, FE-1330, FE-1331, FE-1333, and FE-1334 are Dora -Ma-authored and have no proposal. The migration has 67 issue targets. It changes 65 titles and -writes 66 bodies; FE-1451 already has the exact canonical stored body and is verified without a -write. - -The GitHub snapshot contains 25 pull requests. The proposals change 24 titles and eight bodies; -the remaining bodies already have the required structure. Historical issue and pull-request -comments are outside the migration and remain untouched. - -For each proposed body, the short outer prose may change while the existing detailed record is -preserved inside one canonical `🏗️ Agent notes` wrapper. PR #23 is the sole normalization case: -its accidentally nested standalone wrapper lines are removed, while every other byte of its -inner record remains in source order. - -The reviewed Linear proposal files remain frozen as approved. A trial write showed that Linear -requires a space after the `+++` opener and blank separator lines around the folded content; -both its issue CLI and raw GraphQL API canonicalize absent whitespace. The exact stored bodies -are therefore derived from each approved `proposedOuter` and byte-preserved `innerRecord`, with -their hashes frozen in `data/linear-canonical-target-hashes.json`. Linear also expands the bare -`demo.petrinaut.org` domain in the FE-1433 and FE-1440 outers into its stored Markdown-link -syntax; those two exact replacements live beside their target hashes. No inner-record byte -changes. The wrapper format matches the body Linear already stores for FE-1451. - -## Review surfaces - -- [`review/linear-FE-1357.md`](review/linear-FE-1357.md), - [`review/linear-FE-1366.md`](review/linear-FE-1366.md), - [`review/linear-FE-1383.md`](review/linear-FE-1383.md), - [`review/linear-FE-1401.md`](review/linear-FE-1401.md), and - [`review/linear-FE-1406.md`](review/linear-FE-1406.md) show each issue title and outer-prose - change without repeating the detailed record. -- [`review/github.md`](review/github.md) shows the equivalent pull-request changes. -- [`review/linear-editorial-review.md`](review/linear-editorial-review.md) and - [`review/github-editorial-review.md`](review/github-editorial-review.md) record the independent - editorial and fidelity passes. -- [`data/`](data/) holds the byte-exact source snapshots, reviewed proposals, exact Linear stored - target hashes, and per-record source hashes. The source snapshots include records that are - intentionally excluded from the migration. - -## Frozen data - -| File | SHA-256 | -| ------------------------------------------ | ------------------------------------------------------------------ | -| `data/linear-source.json` | `863ba1deb87718cf3ba3c2accf70ac11cbe7b51a479aff9d9f1bf2719f26f152` | -| `data/linear-canonical-target-hashes.json` | `2368d1d7a6d63193b981757e713148f8c155fb29e3ae68c1c805a03b1e338b25` | -| `data/linear-proposals-FE-1366.json` | `2473f32ebe5a4ef59f11d910e1ccf57f9eb22df60a9de40440652509e61bc327` | -| `data/linear-proposals-FE-1383.json` | `80146357d942c89602a3bbe74a31efa2f2c47556988c10c37c291d2778ae9ea7` | -| `data/linear-proposals-FE-1401.json` | `acdc70da63e7d8d74bff8aa736aa903978c695134e9f828e23419d7f4fa3a3d8` | -| `data/linear-proposals-FE-1406.json` | `87324f17f294996c8dcf0fc9ba92c76c51554ee0f7c48082f404e966e7275fbf` | -| `data/github-source.json` | `ff209f957d17fb3c3049a3c19845b2c3f2310c88cda88d01712a2a3ab67c5113` | -| `data/github-proposals.json` | `5c77c007537c15357a2d482c51bc933bc61b7727a6982e8324851d007e55adcc` | - -Two later, deliberate departures from the original freeze, both made after the migration was -applied and both preserved in git history: - -- `data/linear-proposals-FE-1357.json` (frozen as - `3832104ff8079ff56102235d86c1982970bfb5752e508baa4fc4646a69162985`) was removed from the - repository: its issue-URL-adjacent prose tripped hashintel/hash's preflight scan that blocks - merging any pull request whose title names a ticket still referenced beside a task marker, - for every ticket from FE-1437 through FE-1441. The - validator's `REMOVED_LINEAR_PROPOSAL_TITLES` map carries the 29 removed proposals' - identifiers and applied titles, so coverage and the PR title checks still account for the - whole subtree. The canonical target hashes for those issues remain in - `data/linear-canonical-target-hashes.json`. -- `data/linear-source.json` and `data/github-source.json` were captured minified - (`1a7200ac…`, `2d00d591…`) and were pretty-printed by the monorepo formatter when Brunch was - assimilated into hashintel/hash. The parsed content is unchanged; the table above pins the - reformatted bytes, and `source-record-hashes.json` keeps the capture-time hashes in its - `generatedFrom` block. - -[`data/source-record-hashes.json`](data/source-record-hashes.json) records the source -`updatedAt`, title hash, and body hash for every one of the 73 issues and 25 pull requests. Its -SHA-256 is `15ff2f72cd7e49a6d3d20f3a71ebbf6f583573f384700a68775f1a4390c31717`. - -Run the deterministic validator from the repository root: - -```sh -node --experimental-strip-types docs/planning/legibility-sweep/issue-pr-migration-2026-08-20/validate.ts -``` - -It checks the frozen file hashes, source coverage, authorship exclusions, per-record source and -proposal hashes, byte-exact inner-record preservation, Linear canonical target hashes, wrapper -shape, and issue-to-PR title links. - -## Apply protocol - -Application requires separate, explicit approval. Apply Linear issues in identifier order, then -GitHub pull requests in number order, one record at a time. For each record: - -1. Fetch its raw title, body, and `updatedAt` immediately before writing. Require all three to - equal the frozen source values and recompute the title and body hashes. Any difference is - drift: do not write that record. Rebuild and review its proposal from the fresh content. - FE-1357 and FE-1433 are the documented exceptions: failed trial writes and byte-exact - rollbacks changed only their immutable `updatedAt` history. Require their frozen title/body - hashes and the last recovered timestamps recorded in the apply log instead. -2. For Linear, write the exact `proposedTitle` and derive the stored body only from the approved - `proposedOuter` and `innerRecord` using the canonical wrapper and two declared bare-domain - replacements above; require its hash to match `linear-canonical-target-hashes.json` before - writing. For GitHub, write the exact `proposedTitle` and `proposedBody`. Do not reconstruct - editorial content from a compact review manifest. -3. Fetch the raw record again. Require exact title and body equality with the system-specific - target and recompute its hashes. Any mismatch is a failed verification: stop before touching - the next record. -4. Append one result to `apply-log.jsonl` in this directory: record id and URL, pre-fetch time, - source `updatedAt` and hashes, proposed hashes, post-fetch time and hashes, and pass/fail. A - failed or drifting record stops the run. - -After the last record, fetch the complete scope again and reconcile it with the proposals and -the apply log. The six excluded Dora-authored issues must still match the source snapshot in -title, body, and URL. Require the source `updatedAt` too, except for FE-1333's exact documented -metadata-only side effect above. No step in this protocol calls a comment-writing endpoint, so -historical comments remain outside the operation. - -## Rollback - -The raw snapshots are the rollback authority. To emit their original titles and bodies without -adding or removing a newline, choose an empty destination and run: - -```sh -bun docs/planning/legibility-sweep/issue-pr-migration-2026-08-20/validate.ts \ - --emit-originals /tmp/brunch-legibility-originals -``` - -This creates `title.txt` and `body.md` for all 73 issues and 25 pull requests and reads every -file back to prove byte equality. Before restoring a record, fresh-fetch it and require it to -match the applied system-specific target; otherwise stop for review. After restoring, -fresh-fetch again and require its title and body hashes to match -`data/source-record-hashes.json`. diff --git a/libs/@hashintel/brunch-agent/docs/archive/migrations/issue-pr-legibility-2026-08-20/apply-log.jsonl b/libs/@hashintel/brunch-agent/docs/archive/migrations/issue-pr-legibility-2026-08-20/apply-log.jsonl deleted file mode 100644 index 455f1ae3fca..00000000000 --- a/libs/@hashintel/brunch-agent/docs/archive/migrations/issue-pr-legibility-2026-08-20/apply-log.jsonl +++ /dev/null @@ -1,249 +0,0 @@ -{"event":"attempt","system":"linear","id":"FE-1357","url":"https://linear.app/hash/issue/FE-1357/plan-the-september-elicitation-demo-and-the-plugin-spec-behind-it","preFetchAt":"2026-08-20T10:50:17.191Z","sourceUpdatedAt":"2026-08-19T16:26:28.291Z","sourceTitleSha256":"83601abba5441bd6090bce3ea1dcf9568ec80844a9bb19dcdbb1284df7608a84","sourceBodySha256":"70c31f029f6aa53ce7fa10701436754ed06140024841d6f451db9e7e5771a525","proposedTitleSha256":"edb56b437c811cec397eeb6940f63cac99f3ebd29bbf371c3378e0184e50bffe","proposedBodySha256":"813d332b818faef209bb08cf6d6c84f8a6c3a0531aad23ff3978d4c087d16ab2"} -{"event":"result","status":"verification-failed","system":"linear","id":"FE-1357","url":"https://linear.app/hash/issue/FE-1357/plan-the-september-elicitation-demo-and-the-plugin-spec-behind-it","postFetchAt":"2026-08-20T10:50:18.365Z","postUpdatedAt":"2026-08-20T10:50:17.829Z","proposedTitleSha256":"edb56b437c811cec397eeb6940f63cac99f3ebd29bbf371c3378e0184e50bffe","actualTitleSha256":"edb56b437c811cec397eeb6940f63cac99f3ebd29bbf371c3378e0184e50bffe","proposedBodySha256":"813d332b818faef209bb08cf6d6c84f8a6c3a0531aad23ff3978d4c087d16ab2","actualBodySha256":"af21a19b276419b7f351b3edb6c48bf3f598dd25934f835e971cc7a908d60f98","writeExitCode":0,"writeStderr":""} -{"event":"recovery","status":"restored-to-source","system":"linear","id":"FE-1357","url":"https://linear.app/hash/issue/FE-1357/plan-the-september-elicitation-demo-and-the-plugin-spec-behind-it","recordedAt":"2026-08-20T10:51:52Z","frozenSourceUpdatedAt":"2026-08-19T16:26:28.291Z","restoredUpdatedAt":"2026-08-20T10:51:25.480Z","sourceTitleSha256":"83601abba5441bd6090bce3ea1dcf9568ec80844a9bb19dcdbb1284df7608a84","restoredTitleSha256":"83601abba5441bd6090bce3ea1dcf9568ec80844a9bb19dcdbb1284df7608a84","sourceBodySha256":"70c31f029f6aa53ce7fa10701436754ed06140024841d6f451db9e7e5771a525","restoredBodySha256":"70c31f029f6aa53ce7fa10701436754ed06140024841d6f451db9e7e5771a525","cause":"linear issue update normalized the proposed Markdown; raw GraphQL restored the frozen title and body byte-exactly"} -{"event":"attempt","system":"linear","id":"FE-1357","url":"https://linear.app/hash/issue/FE-1357/plan-the-september-elicitation-demo-and-the-plugin-spec-behind-it","preFetchAt":"2026-08-20T10:53:00.554Z","frozenSourceUpdatedAt":"2026-08-19T16:26:28.291Z","sourceUpdatedAt":"2026-08-20T10:51:25.480Z","sourceTitleSha256":"83601abba5441bd6090bce3ea1dcf9568ec80844a9bb19dcdbb1284df7608a84","sourceBodySha256":"70c31f029f6aa53ce7fa10701436754ed06140024841d6f451db9e7e5771a525","proposedTitleSha256":"edb56b437c811cec397eeb6940f63cac99f3ebd29bbf371c3378e0184e50bffe","proposedBodySha256":"813d332b818faef209bb08cf6d6c84f8a6c3a0531aad23ff3978d4c087d16ab2"} -{"event":"result","status":"verification-failed","system":"linear","id":"FE-1357","url":"https://linear.app/hash/issue/FE-1357/plan-the-september-elicitation-demo-and-plugin-specification","postFetchAt":"2026-08-20T10:53:01.565Z","postUpdatedAt":"2026-08-20T10:53:01.084Z","proposedTitleSha256":"edb56b437c811cec397eeb6940f63cac99f3ebd29bbf371c3378e0184e50bffe","actualTitleSha256":"edb56b437c811cec397eeb6940f63cac99f3ebd29bbf371c3378e0184e50bffe","proposedBodySha256":"813d332b818faef209bb08cf6d6c84f8a6c3a0531aad23ff3978d4c087d16ab2","actualBodySha256":"af21a19b276419b7f351b3edb6c48bf3f598dd25934f835e971cc7a908d60f98","writeExitCode":0,"writeStderr":""} -{"event":"recovery","status":"restored-to-source","system":"linear","id":"FE-1357","url":"https://linear.app/hash/issue/FE-1357/plan-the-september-elicitation-demo-and-the-plugin-spec-behind-it","recordedAt":"2026-08-20T10:53:58Z","frozenSourceUpdatedAt":"2026-08-19T16:26:28.291Z","restoredUpdatedAt":"2026-08-20T10:53:47.086Z","sourceTitleSha256":"83601abba5441bd6090bce3ea1dcf9568ec80844a9bb19dcdbb1284df7608a84","restoredTitleSha256":"83601abba5441bd6090bce3ea1dcf9568ec80844a9bb19dcdbb1284df7608a84","sourceBodySha256":"70c31f029f6aa53ce7fa10701436754ed06140024841d6f451db9e7e5771a525","restoredBodySha256":"70c31f029f6aa53ce7fa10701436754ed06140024841d6f451db9e7e5771a525","cause":"Linear canonicalized the fold delimiters through raw GraphQL as well; the frozen title and body were restored byte-exactly and the migration remains halted"} -{"event":"attempt","system":"linear","id":"FE-1357","url":"https://linear.app/hash/issue/FE-1357/plan-the-september-elicitation-demo-and-the-plugin-spec-behind-it","preFetchAt":"2026-08-20T11:00:48.082Z","frozenSourceUpdatedAt":"2026-08-19T16:26:28.291Z","sourceUpdatedAt":"2026-08-20T10:53:47.086Z","sourceTitleSha256":"83601abba5441bd6090bce3ea1dcf9568ec80844a9bb19dcdbb1284df7608a84","sourceBodySha256":"70c31f029f6aa53ce7fa10701436754ed06140024841d6f451db9e7e5771a525","proposedTitleSha256":"edb56b437c811cec397eeb6940f63cac99f3ebd29bbf371c3378e0184e50bffe","proposedBodySha256":"af21a19b276419b7f351b3edb6c48bf3f598dd25934f835e971cc7a908d60f98"} -{"event":"result","status":"pass","system":"linear","id":"FE-1357","url":"https://linear.app/hash/issue/FE-1357/plan-the-september-elicitation-demo-and-plugin-specification","postFetchAt":"2026-08-20T11:00:49.389Z","postUpdatedAt":"2026-08-20T11:00:48.864Z","proposedTitleSha256":"edb56b437c811cec397eeb6940f63cac99f3ebd29bbf371c3378e0184e50bffe","actualTitleSha256":"edb56b437c811cec397eeb6940f63cac99f3ebd29bbf371c3378e0184e50bffe","proposedBodySha256":"af21a19b276419b7f351b3edb6c48bf3f598dd25934f835e971cc7a908d60f98","actualBodySha256":"af21a19b276419b7f351b3edb6c48bf3f598dd25934f835e971cc7a908d60f98","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1358","url":"https://linear.app/hash/issue/FE-1358/survey-petrinaut-architecture-dependencies-assistant-implementation","preFetchAt":"2026-08-20T11:00:49.634Z","frozenSourceUpdatedAt":"2026-08-19T16:20:27.268Z","sourceUpdatedAt":"2026-08-19T16:20:27.268Z","sourceTitleSha256":"f0ba585b8182ae752cbf2efffaabe8e3e805dedd6e0c9fe4712c01dda18c2366","sourceBodySha256":"db2735d101439db145bdceb1a38b8e290487853806a91464677bfb650d4d4b5d","proposedTitleSha256":"92a7187c58c6662981c602d001ed28e1088a40f24a05befdaae9b399816cb9c4","proposedBodySha256":"08c91e46efd7697eb722cc04373be6680de6591dc6ad4ed9c35f971f6ca55899"} -{"event":"result","status":"pass","system":"linear","id":"FE-1358","url":"https://linear.app/hash/issue/FE-1358/survey-petrinaut-for-the-september-integration","postFetchAt":"2026-08-20T11:00:50.778Z","postUpdatedAt":"2026-08-20T11:00:50.413Z","proposedTitleSha256":"92a7187c58c6662981c602d001ed28e1088a40f24a05befdaae9b399816cb9c4","actualTitleSha256":"92a7187c58c6662981c602d001ed28e1088a40f24a05befdaae9b399816cb9c4","proposedBodySha256":"08c91e46efd7697eb722cc04373be6680de6591dc6ad4ed9c35f971f6ca55899","actualBodySha256":"08c91e46efd7697eb722cc04373be6680de6591dc6ad4ed9c35f971f6ca55899","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1359","url":"https://linear.app/hash/issue/FE-1359/voice-interviewing-bolt-on-adapter-or-architectural-rewrite","preFetchAt":"2026-08-20T11:00:51.012Z","frozenSourceUpdatedAt":"2026-08-19T16:20:27.307Z","sourceUpdatedAt":"2026-08-19T16:20:27.307Z","sourceTitleSha256":"90f6a9c3c8a4623f3635441de84273aab1c8639d9630ecef4b98d16d0623ffcd","sourceBodySha256":"2bfe24356304af451f85f6518453b9b202a67d301b76f9c42fc50fead0b7776b","proposedTitleSha256":"24fe52139a5192afa3a13f0264dd052c25b64dd721942d2e3bfd24b1f0d4137c","proposedBodySha256":"5229603b3d41374e36d31d761c83528f7ba5291963ac84c843d60a138d56652b"} -{"event":"result","status":"pass","system":"linear","id":"FE-1359","url":"https://linear.app/hash/issue/FE-1359/decide-whether-voice-changes-the-elicitor-architecture","postFetchAt":"2026-08-20T11:00:51.885Z","postUpdatedAt":"2026-08-20T11:00:51.481Z","proposedTitleSha256":"24fe52139a5192afa3a13f0264dd052c25b64dd721942d2e3bfd24b1f0d4137c","actualTitleSha256":"24fe52139a5192afa3a13f0264dd052c25b64dd721942d2e3bfd24b1f0d4137c","proposedBodySha256":"5229603b3d41374e36d31d761c83528f7ba5291963ac84c843d60a138d56652b","actualBodySha256":"5229603b3d41374e36d31d761c83528f7ba5291963ac84c843d60a138d56652b","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1360","url":"https://linear.app/hash/issue/FE-1360/ground-elicitation-strategy-in-the-literature","preFetchAt":"2026-08-20T11:00:52.695Z","frozenSourceUpdatedAt":"2026-08-19T16:20:27.656Z","sourceUpdatedAt":"2026-08-19T16:20:27.656Z","sourceTitleSha256":"f9b6d463b4fbd91c31446559b1cb91cb27a2f6080e416c5758e20c81040ea6f9","sourceBodySha256":"25c76c4fe5da56fbfd6fbd123e84e8c544fcb09e3e455ae0924f3924aadebca9","proposedTitleSha256":"7e705b33a3fc5c880137fa49b8805afc4d1d547dd75063206fbb9c64d867c97d","proposedBodySha256":"a6d7709d6fad560785f2c4d8d5d4d93731ff5d6c8e2b16dcf2bc384ac5daae19"} -{"event":"result","status":"pass","system":"linear","id":"FE-1360","url":"https://linear.app/hash/issue/FE-1360/derive-elicitation-guidance-from-published-research","postFetchAt":"2026-08-20T11:00:53.350Z","postUpdatedAt":"2026-08-20T11:00:53.056Z","proposedTitleSha256":"7e705b33a3fc5c880137fa49b8805afc4d1d547dd75063206fbb9c64d867c97d","actualTitleSha256":"7e705b33a3fc5c880137fa49b8805afc4d1d547dd75063206fbb9c64d867c97d","proposedBodySha256":"a6d7709d6fad560785f2c4d8d5d4d93731ff5d6c8e2b16dcf2bc384ac5daae19","actualBodySha256":"a6d7709d6fad560785f2c4d8d5d4d93731ff5d6c8e2b16dcf2bc384ac5daae19","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1361","url":"https://linear.app/hash/issue/FE-1361/baseline-control-what-does-one-shot-ai-elicitation-already-achieve","preFetchAt":"2026-08-20T11:00:53.647Z","frozenSourceUpdatedAt":"2026-08-19T16:20:28.021Z","sourceUpdatedAt":"2026-08-19T16:20:28.021Z","sourceTitleSha256":"89252bf7368aae36924c6a2fb34ace7477a76199a34255b373bda2440a4e9010","sourceBodySha256":"95f7163a475614b062c16230b0ef576e624ead0280d702b97dcac9deb8d575f0","proposedTitleSha256":"af5b3693ff1fe6a3345539ae08530bafa9b2df8a60e5ba708d7833ba963d2a71","proposedBodySha256":"5bd4de3670bdb15872c5826c6d053fcf0e4bbc17542313a21640b6732ae941e8"} -{"event":"result","status":"pass","system":"linear","id":"FE-1361","url":"https://linear.app/hash/issue/FE-1361/measure-the-one-shot-ai-elicitation-baseline","postFetchAt":"2026-08-20T11:00:54.284Z","postUpdatedAt":"2026-08-20T11:00:53.931Z","proposedTitleSha256":"af5b3693ff1fe6a3345539ae08530bafa9b2df8a60e5ba708d7833ba963d2a71","actualTitleSha256":"af5b3693ff1fe6a3345539ae08530bafa9b2df8a60e5ba708d7833ba963d2a71","proposedBodySha256":"5bd4de3670bdb15872c5826c6d053fcf0e4bbc17542313a21640b6732ae941e8","actualBodySha256":"5bd4de3670bdb15872c5826c6d053fcf0e4bbc17542313a21640b6732ae941e8","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1362","url":"https://linear.app/hash/issue/FE-1362/decide-the-september-demo-vehicle","preFetchAt":"2026-08-20T11:00:54.552Z","frozenSourceUpdatedAt":"2026-08-19T16:26:28.338Z","sourceUpdatedAt":"2026-08-19T16:26:28.338Z","sourceTitleSha256":"cdb93df46f8071efeb92a153b4eadcae8d65a4c301e9ab8c5985a2da0576886a","sourceBodySha256":"b7a1b6eea883a9151ce69a93149934c588deeb33fcaf97f19bdc97957d45c230","proposedTitleSha256":"703de24bb88a4bc6802a75b8dca0e0c7d4a09fb93f72aa8c532d8c5853326959","proposedBodySha256":"9576d5d00ff1e5cc3e8d8ee8d61c762e297bb63889aaea2469a7a778418d9993"} -{"event":"result","status":"pass","system":"linear","id":"FE-1362","url":"https://linear.app/hash/issue/FE-1362/decide-the-september-demo-architecture","postFetchAt":"2026-08-20T11:00:55.168Z","postUpdatedAt":"2026-08-20T11:00:54.835Z","proposedTitleSha256":"703de24bb88a4bc6802a75b8dca0e0c7d4a09fb93f72aa8c532d8c5853326959","actualTitleSha256":"703de24bb88a4bc6802a75b8dca0e0c7d4a09fb93f72aa8c532d8c5853326959","proposedBodySha256":"9576d5d00ff1e5cc3e8d8ee8d61c762e297bb63889aaea2469a7a778418d9993","actualBodySha256":"9576d5d00ff1e5cc3e8d8ee8d61c762e297bb63889aaea2469a7a778418d9993","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1363","url":"https://linear.app/hash/issue/FE-1363/choose-the-reference-use-case-settle-the-sdcpn-showcase-criterion","preFetchAt":"2026-08-20T11:00:55.410Z","frozenSourceUpdatedAt":"2026-08-19T16:20:28.274Z","sourceUpdatedAt":"2026-08-19T16:20:28.274Z","sourceTitleSha256":"5a20ee169ead5fc437c962db1ff0db13adc0d1c46776cb8862b5f3a25830c0a7","sourceBodySha256":"ffa0d06dc4022dd33a5921e8c3e2e741f8f9599b632bc6734e051256a5140509","proposedTitleSha256":"d14ab7a091020ba12c4c7eeacc619c18f3099e57703e8ba5cf68efbe17328b8a","proposedBodySha256":"4cc749b9cfdeeec3960bba78110fe36874827ca888085991a97f232009e89d97"} -{"event":"result","status":"pass","system":"linear","id":"FE-1363","url":"https://linear.app/hash/issue/FE-1363/choose-the-demo-use-case-and-modelling-criteria","postFetchAt":"2026-08-20T11:00:56.486Z","postUpdatedAt":"2026-08-20T11:00:56.060Z","proposedTitleSha256":"d14ab7a091020ba12c4c7eeacc619c18f3099e57703e8ba5cf68efbe17328b8a","actualTitleSha256":"d14ab7a091020ba12c4c7eeacc619c18f3099e57703e8ba5cf68efbe17328b8a","proposedBodySha256":"4cc749b9cfdeeec3960bba78110fe36874827ca888085991a97f232009e89d97","actualBodySha256":"4cc749b9cfdeeec3960bba78110fe36874827ca888085991a97f232009e89d97","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1364","url":"https://linear.app/hash/issue/FE-1364/define-the-intermediate-representation-for-process-model-elicitation","preFetchAt":"2026-08-20T11:00:56.722Z","frozenSourceUpdatedAt":"2026-08-19T16:26:27.321Z","sourceUpdatedAt":"2026-08-19T16:26:27.321Z","sourceTitleSha256":"aa12482b696cc600940045a86c3fdfc36df84ff3e463c29bc2184fbefde75d1e","sourceBodySha256":"a468c2c80f6e27f387ae7cb76493f6cba7f974099060b2617a105535244de04b","proposedTitleSha256":"7ca4533b4bb0e42d0a310e72c5a9a8aae181115af2ad1ffcc85587f49437a3a1","proposedBodySha256":"454e2287159c78a31d09cbdea9402be4594916e53106026ec42944445d768a24"} -{"event":"result","status":"pass","system":"linear","id":"FE-1364","url":"https://linear.app/hash/issue/FE-1364/define-the-process-model-elicitation-representation","postFetchAt":"2026-08-20T11:00:57.380Z","postUpdatedAt":"2026-08-20T11:00:56.995Z","proposedTitleSha256":"7ca4533b4bb0e42d0a310e72c5a9a8aae181115af2ad1ffcc85587f49437a3a1","actualTitleSha256":"7ca4533b4bb0e42d0a310e72c5a9a8aae181115af2ad1ffcc85587f49437a3a1","proposedBodySha256":"454e2287159c78a31d09cbdea9402be4594916e53106026ec42944445d768a24","actualBodySha256":"454e2287159c78a31d09cbdea9402be4594916e53106026ec42944445d768a24","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1366","url":"https://linear.app/hash/issue/FE-1366/spec-the-elicitation-harness-architecture-archived-wayfinder-map","preFetchAt":"2026-08-20T11:00:57.618Z","frozenSourceUpdatedAt":"2026-08-19T16:26:24.440Z","sourceUpdatedAt":"2026-08-19T16:26:24.440Z","sourceTitleSha256":"3cd9b3e42040c5bd4f19c7ccb505e838266683b4098bb7d02934b03c8df8f6b5","sourceBodySha256":"991af4e8570535aa208f829fd81107ebcf5163186d25837f78746cee7ed77015","proposedTitleSha256":"ecd6270308cb53c1cddecbd489543f583a9d2850c1bcb29aa919e7fcf0c588dd","proposedBodySha256":"d07ec0b6e6bfdb01e9dbcc57de35dbfeeae65c80d5c688a0fb4b0f855c05739d"} -{"event":"result","status":"pass","system":"linear","id":"FE-1366","url":"https://linear.app/hash/issue/FE-1366/document-the-elicitation-harness-architecture","postFetchAt":"2026-08-20T11:00:58.405Z","postUpdatedAt":"2026-08-20T11:00:58.023Z","proposedTitleSha256":"ecd6270308cb53c1cddecbd489543f583a9d2850c1bcb29aa919e7fcf0c588dd","actualTitleSha256":"ecd6270308cb53c1cddecbd489543f583a9d2850c1bcb29aa919e7fcf0c588dd","proposedBodySha256":"d07ec0b6e6bfdb01e9dbcc57de35dbfeeae65c80d5c688a0fb4b0f855c05739d","actualBodySha256":"d07ec0b6e6bfdb01e9dbcc57de35dbfeeae65c80d5c688a0fb4b0f855c05739d","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1367","url":"https://linear.app/hash/issue/FE-1367/flue-architecture-deep-read-archive","preFetchAt":"2026-08-20T11:00:58.661Z","frozenSourceUpdatedAt":"2026-08-19T16:20:27.414Z","sourceUpdatedAt":"2026-08-19T16:20:27.414Z","sourceTitleSha256":"d56adb14bff1dbfe03cac1c1c2a312d5fc3ac3693b36a8324c59c080ef5aea2a","sourceBodySha256":"5f6506ff45d1964c8545e824d2c5b8f989410ac176bfa5fef6b9cfe150109239","proposedTitleSha256":"56a728873a351ffa5abcb9e165694d1c6a882e43fc29d126ab46598a482465aa","proposedBodySha256":"0d564e0427b73c79440b31b09c85a11d83bdada98759c0c0571531e1b7eacdc2"} -{"event":"result","status":"pass","system":"linear","id":"FE-1367","url":"https://linear.app/hash/issue/FE-1367/define-how-the-elicitation-harness-uses-flue","postFetchAt":"2026-08-20T11:00:59.398Z","postUpdatedAt":"2026-08-20T11:00:59.003Z","proposedTitleSha256":"56a728873a351ffa5abcb9e165694d1c6a882e43fc29d126ab46598a482465aa","actualTitleSha256":"56a728873a351ffa5abcb9e165694d1c6a882e43fc29d126ab46598a482465aa","proposedBodySha256":"0d564e0427b73c79440b31b09c85a11d83bdada98759c0c0571531e1b7eacdc2","actualBodySha256":"0d564e0427b73c79440b31b09c85a11d83bdada98759c0c0571531e1b7eacdc2","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1368","url":"https://linear.app/hash/issue/FE-1368/zil-lean-survey-archive","preFetchAt":"2026-08-20T11:00:59.662Z","frozenSourceUpdatedAt":"2026-08-19T16:20:27.450Z","sourceUpdatedAt":"2026-08-19T16:20:27.450Z","sourceTitleSha256":"aa2bdea3725452e09651b2a77e90deef72ab518995b82968d256935035bf1dad","sourceBodySha256":"a34174377a2da0548781191d7cbb33e606fb329065a5f14afe4d22454917596f","proposedTitleSha256":"671c8d7d2ec1d02dd04e6791f04e91d907c514f38ea3e8bbf371757bcd44d183","proposedBodySha256":"58eac8c7f4da2110cff73432764c20d71576fbedbed167d88a7c0ccaacfe8b70"} -{"event":"result","status":"pass","system":"linear","id":"FE-1368","url":"https://linear.app/hash/issue/FE-1368/assess-zil-lean-as-an-elicitation-subject","postFetchAt":"2026-08-20T11:01:00.564Z","postUpdatedAt":"2026-08-20T11:01:00.028Z","proposedTitleSha256":"671c8d7d2ec1d02dd04e6791f04e91d907c514f38ea3e8bbf371757bcd44d183","actualTitleSha256":"671c8d7d2ec1d02dd04e6791f04e91d907c514f38ea3e8bbf371757bcd44d183","proposedBodySha256":"58eac8c7f4da2110cff73432764c20d71576fbedbed167d88a7c0ccaacfe8b70","actualBodySha256":"58eac8c7f4da2110cff73432764c20d71576fbedbed167d88a7c0ccaacfe8b70","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1369","url":"https://linear.app/hash/issue/FE-1369/brunch-exchange-schema-audit-archive","preFetchAt":"2026-08-20T11:01:00.827Z","frozenSourceUpdatedAt":"2026-08-19T16:20:27.340Z","sourceUpdatedAt":"2026-08-19T16:20:27.340Z","sourceTitleSha256":"dfdef0d5ba4e9548fd25e728e40fb8685c0bd6e0daa4432d3793d8d5d6cb85e6","sourceBodySha256":"1fd94e7b433040097a806d169e9cd702bb67db2dcbf906a5e197e678ed4249c2","proposedTitleSha256":"ad51b1463946bcbcf00e57b7bffac75180cde408d6db865a9c2bf8349597a4d2","proposedBodySha256":"f9acc9c49a910e07ea12eee26f5156578029354e5b695d3b6f679744a4d30d3d"} -{"event":"result","status":"pass","system":"linear","id":"FE-1369","url":"https://linear.app/hash/issue/FE-1369/classify-brunch-exchange-structures-for-reuse","postFetchAt":"2026-08-20T11:01:02.019Z","postUpdatedAt":"2026-08-20T11:01:01.421Z","proposedTitleSha256":"ad51b1463946bcbcf00e57b7bffac75180cde408d6db865a9c2bf8349597a4d2","actualTitleSha256":"ad51b1463946bcbcf00e57b7bffac75180cde408d6db865a9c2bf8349597a4d2","proposedBodySha256":"f9acc9c49a910e07ea12eee26f5156578029354e5b695d3b6f679744a4d30d3d","actualBodySha256":"f9acc9c49a910e07ea12eee26f5156578029354e5b695d3b6f679744a4d30d3d","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1370","url":"https://linear.app/hash/issue/FE-1370/contract-decomposition-kernel-host-plugin-pack-boundary-archive","preFetchAt":"2026-08-20T11:01:02.268Z","frozenSourceUpdatedAt":"2026-08-19T16:20:27.501Z","sourceUpdatedAt":"2026-08-19T16:20:27.501Z","sourceTitleSha256":"e26e5d7d5c992fae3d3148ce48e892d0f0246c65aace5cb2f76f686181b778a8","sourceBodySha256":"2234383db65ad51aafd0ef50877851b0449988ebd0e1c57173120549a6f95c11","proposedTitleSha256":"fff1e6cd68dee8290e99e3b5a27ef3e63a5a20a6f634b058724ca33d08cb32aa","proposedBodySha256":"586900fec359fdea19b40355ce70ada6f4dc6cc1a4b318a5266fd04925e9330b"} -{"event":"result","status":"pass","system":"linear","id":"FE-1370","url":"https://linear.app/hash/issue/FE-1370/define-the-harness-and-plugin-responsibilities","postFetchAt":"2026-08-20T11:01:03.027Z","postUpdatedAt":"2026-08-20T11:01:02.574Z","proposedTitleSha256":"fff1e6cd68dee8290e99e3b5a27ef3e63a5a20a6f634b058724ca33d08cb32aa","actualTitleSha256":"fff1e6cd68dee8290e99e3b5a27ef3e63a5a20a6f634b058724ca33d08cb32aa","proposedBodySha256":"586900fec359fdea19b40355ce70ada6f4dc6cc1a4b318a5266fd04925e9330b","actualBodySha256":"586900fec359fdea19b40355ce70ada6f4dc6cc1a4b318a5266fd04925e9330b","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1371","url":"https://linear.app/hash/issue/FE-1371/questioning-ux-contract-archive","preFetchAt":"2026-08-20T11:01:03.774Z","frozenSourceUpdatedAt":"2026-08-19T16:20:27.539Z","sourceUpdatedAt":"2026-08-19T16:20:27.539Z","sourceTitleSha256":"9c5bb4c0020f39dc129390162dbb882bf463fa6e089c5000047f64924d49f497","sourceBodySha256":"7ec025083f39f180878790e096ec19c369a7d4120c839730dacc7fe7980cc9fb","proposedTitleSha256":"6df3270da2d36b2ce6b9f28706d1d60830de04f2a7284ce91e04bab6c5440116","proposedBodySha256":"8e34e41697c46c46a81ea4c5ab48edcb49de55973b2b11732bcf451b8191a17f"} -{"event":"result","status":"pass","system":"linear","id":"FE-1371","url":"https://linear.app/hash/issue/FE-1371/define-structured-questions-within-conversation","postFetchAt":"2026-08-20T11:01:04.601Z","postUpdatedAt":"2026-08-20T11:01:04.082Z","proposedTitleSha256":"6df3270da2d36b2ce6b9f28706d1d60830de04f2a7284ce91e04bab6c5440116","actualTitleSha256":"6df3270da2d36b2ce6b9f28706d1d60830de04f2a7284ce91e04bab6c5440116","proposedBodySha256":"8e34e41697c46c46a81ea4c5ab48edcb49de55973b2b11732bcf451b8191a17f","actualBodySha256":"8e34e41697c46c46a81ea4c5ab48edcb49de55973b2b11732bcf451b8191a17f","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1372","url":"https://linear.app/hash/issue/FE-1372/shipping-shape-kernel-library-vs-flue-agent-archive","preFetchAt":"2026-08-20T11:01:04.940Z","frozenSourceUpdatedAt":"2026-08-19T16:20:27.583Z","sourceUpdatedAt":"2026-08-19T16:20:27.583Z","sourceTitleSha256":"1dab9baee26c9752955fa86d601f08aed2a362b5cf4c721084b6f2259ab68b68","sourceBodySha256":"b0f7c959583b663416d505a36c08f6db34f037d1bf8842b950cddce5797462c5","proposedTitleSha256":"f62fc5f62252c5cd20b94fe9527ab32111d710f20b95513c430b0b89e389a89d","proposedBodySha256":"4ceed9ff828af36baeb0d9fe3be38f70f48b89310c8f6eb86afb98ba9ea34d1e"} -{"event":"result","status":"pass","system":"linear","id":"FE-1372","url":"https://linear.app/hash/issue/FE-1372/choose-how-the-elicitation-harness-ships","postFetchAt":"2026-08-20T11:01:05.553Z","postUpdatedAt":"2026-08-20T11:01:05.224Z","proposedTitleSha256":"f62fc5f62252c5cd20b94fe9527ab32111d710f20b95513c430b0b89e389a89d","actualTitleSha256":"f62fc5f62252c5cd20b94fe9527ab32111d710f20b95513c430b0b89e389a89d","proposedBodySha256":"4ceed9ff828af36baeb0d9fe3be38f70f48b89310c8f6eb86afb98ba9ea34d1e","actualBodySha256":"4ceed9ff828af36baeb0d9fe3be38f70f48b89310c8f6eb86afb98ba9ea34d1e","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1373","url":"https://linear.app/hash/issue/FE-1373/dev-target-portfolio-confirmation-archive","preFetchAt":"2026-08-20T11:01:05.978Z","frozenSourceUpdatedAt":"2026-08-19T16:20:27.618Z","sourceUpdatedAt":"2026-08-19T16:20:27.618Z","sourceTitleSha256":"f716acc84bd7e8b18c26d9f5020804c103bd5a28cd5f957f643d47885903a973","sourceBodySha256":"a1a499c06b23735a1b16cabcc6c787f35c1e3fda3af30f09c87927173b54f426","proposedTitleSha256":"49526b09da7cea4f956b0fc97da3a537287b6e1dc0a5599a0dc744273bbd4057","proposedBodySha256":"3427cd321d38c72b7abb00985cdc8eb1d4de70b2b1cf4be1bad6537edf468d03"} -{"event":"result","status":"pass","system":"linear","id":"FE-1373","url":"https://linear.app/hash/issue/FE-1373/choose-the-initial-elicitation-subjects","postFetchAt":"2026-08-20T11:01:06.579Z","postUpdatedAt":"2026-08-20T11:01:06.262Z","proposedTitleSha256":"49526b09da7cea4f956b0fc97da3a537287b6e1dc0a5599a0dc744273bbd4057","actualTitleSha256":"49526b09da7cea4f956b0fc97da3a537287b6e1dc0a5599a0dc744273bbd4057","proposedBodySha256":"3427cd321d38c72b7abb00985cdc8eb1d4de70b2b1cf4be1bad6537edf468d03","actualBodySha256":"3427cd321d38c72b7abb00985cdc8eb1d4de70b2b1cf4be1bad6537edf468d03","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1374","url":"https://linear.app/hash/issue/FE-1374/assemble-the-spec-archive","preFetchAt":"2026-08-20T11:01:06.811Z","frozenSourceUpdatedAt":"2026-08-19T16:20:26.406Z","sourceUpdatedAt":"2026-08-19T16:20:26.406Z","sourceTitleSha256":"e0ef68909d31cd8e17da3910dd6beb59c3fd02068b82f7a8179a35a498f40f44","sourceBodySha256":"a61865e7695eb3e3170612f0101ffd8bf4dd97c8322fc08c4bd129d2ea0828c1","proposedTitleSha256":"a8d6e8af11c31f8e0b937ba43b6d758e79988d751f29a1222bb5097e33ce4f78","proposedBodySha256":"cb0436794a3e2c782938939475a1fa2a0b68e3969b1e1ff03eb8fa4306d4abc0"} -{"event":"result","status":"pass","system":"linear","id":"FE-1374","url":"https://linear.app/hash/issue/FE-1374/assemble-the-elicitation-harness-specification","postFetchAt":"2026-08-20T11:01:07.440Z","postUpdatedAt":"2026-08-20T11:01:07.086Z","proposedTitleSha256":"a8d6e8af11c31f8e0b937ba43b6d758e79988d751f29a1222bb5097e33ce4f78","actualTitleSha256":"a8d6e8af11c31f8e0b937ba43b6d758e79988d751f29a1222bb5097e33ce4f78","proposedBodySha256":"cb0436794a3e2c782938939475a1fa2a0b68e3969b1e1ff03eb8fa4306d4abc0","actualBodySha256":"cb0436794a3e2c782938939475a1fa2a0b68e3969b1e1ff03eb8fa4306d4abc0","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1375","url":"https://linear.app/hash/issue/FE-1375/formal-verification-canon-survey-archive","preFetchAt":"2026-08-20T11:01:07.745Z","frozenSourceUpdatedAt":"2026-08-19T16:20:27.379Z","sourceUpdatedAt":"2026-08-19T16:20:27.379Z","sourceTitleSha256":"686845995b96dbb4605c51d32a3db23e65fe13afab1b3d29fb71ac0f3aa751f5","sourceBodySha256":"da21a9ce028674fd3a68be0b7b6401585fa9af23cd013781a53c1d1a8a1a787c","proposedTitleSha256":"1014535547f1f5612dda184fea1218227d4b4674f4bf9b18fa3c9d9841c9740a","proposedBodySha256":"f1e803bca9e75caf52ea4346c7d8fe2c859523a1de616367c56b1dc58da2d82c"} -{"event":"result","status":"pass","system":"linear","id":"FE-1375","url":"https://linear.app/hash/issue/FE-1375/align-the-assurance-subject-with-verification-practice","postFetchAt":"2026-08-20T11:01:08.495Z","postUpdatedAt":"2026-08-20T11:01:08.093Z","proposedTitleSha256":"1014535547f1f5612dda184fea1218227d4b4674f4bf9b18fa3c9d9841c9740a","actualTitleSha256":"1014535547f1f5612dda184fea1218227d4b4674f4bf9b18fa3c9d9841c9740a","proposedBodySha256":"f1e803bca9e75caf52ea4346c7d8fe2c859523a1de616367c56b1dc58da2d82c","actualBodySha256":"f1e803bca9e75caf52ea4346c7d8fe2c859523a1de616367c56b1dc58da2d82c","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1376","url":"https://linear.app/hash/issue/FE-1376/walking-skeleton-flue-question-round-trip-archive","preFetchAt":"2026-08-20T11:01:09.596Z","frozenSourceUpdatedAt":"2026-08-19T16:20:26.447Z","sourceUpdatedAt":"2026-08-19T16:20:26.447Z","sourceTitleSha256":"b44606046ef4f88db1c99ea98b64ca1c22da4d2dbe757268232851b775131f77","sourceBodySha256":"ed9a8f3a2277d2bbb6122af8c449793c65672faa235173b5d51d657f8e5dc3c6","proposedTitleSha256":"780a9ff8453c76d6b8fd253a634ef6049f1ef97808e74451361e164bac65ab98","proposedBodySha256":"3a752cf0d3362718f9364083a2469b501e3d5037dbfd8fde747216b4e53e36ec"} -{"event":"result","status":"pass","system":"linear","id":"FE-1376","url":"https://linear.app/hash/issue/FE-1376/prove-a-flue-question-round-trip","postFetchAt":"2026-08-20T11:01:10.379Z","postUpdatedAt":"2026-08-20T11:01:09.911Z","proposedTitleSha256":"780a9ff8453c76d6b8fd253a634ef6049f1ef97808e74451361e164bac65ab98","actualTitleSha256":"780a9ff8453c76d6b8fd253a634ef6049f1ef97808e74451361e164bac65ab98","proposedBodySha256":"3a752cf0d3362718f9364083a2469b501e3d5037dbfd8fde747216b4e53e36ec","actualBodySha256":"3a752cf0d3362718f9364083a2469b501e3d5037dbfd8fde747216b4e53e36ec","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1377","url":"https://linear.app/hash/issue/FE-1377/logic-prototype-capture-sweep-and-settlement-archive","preFetchAt":"2026-08-20T11:01:10.649Z","frozenSourceUpdatedAt":"2026-08-19T16:20:26.491Z","sourceUpdatedAt":"2026-08-19T16:20:26.491Z","sourceTitleSha256":"37acb34d281af002054f9c0cecaececb353463f7bd3f37b592c050efcb5076f7","sourceBodySha256":"1f7efed2a48a98b6b9ddb02cce3aef6afedb49aeb7ba33415ca5a32dfc38b908","proposedTitleSha256":"5679461ba36f1b7ddc1d98c64e7f54b59038d3e217d0722c2953b1f2044b99bd","proposedBodySha256":"fac78c76da54522de3bfa0777b4f048279581c51300c3fc74bcf80548c0d8cba"} -{"event":"result","status":"pass","system":"linear","id":"FE-1377","url":"https://linear.app/hash/issue/FE-1377/prove-repeatable-conversation-capture","postFetchAt":"2026-08-20T11:01:11.338Z","postUpdatedAt":"2026-08-20T11:01:10.954Z","proposedTitleSha256":"5679461ba36f1b7ddc1d98c64e7f54b59038d3e217d0722c2953b1f2044b99bd","actualTitleSha256":"5679461ba36f1b7ddc1d98c64e7f54b59038d3e217d0722c2953b1f2044b99bd","proposedBodySha256":"fac78c76da54522de3bfa0777b4f048279581c51300c3fc74bcf80548c0d8cba","actualBodySha256":"fac78c76da54522de3bfa0777b4f048279581c51300c3fc74bcf80548c0d8cba","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1378","url":"https://linear.app/hash/issue/FE-1378/multi-session-elicitation-and-durable-target-state-archive","preFetchAt":"2026-08-20T11:01:11.645Z","frozenSourceUpdatedAt":"2026-08-19T16:20:26.948Z","sourceUpdatedAt":"2026-08-19T16:20:26.948Z","sourceTitleSha256":"a0a6d108d755c05072d5afcc84ed4ebddb1d01b3b55c339dbfb22662c6bad90a","sourceBodySha256":"4a2e6fa5bc9e664bf7fed660d561ed2a843aa2f99509734de0773e29b169c8ca","proposedTitleSha256":"442998135a28eb368d03b58a5ec32ff9fbd4ee0743b7afc16030e3dac70494cf","proposedBodySha256":"77648b3f761ba84c2837ad23b418104e13c2182772006d3e175f63b0066cf065"} -{"event":"result","status":"pass","system":"linear","id":"FE-1378","url":"https://linear.app/hash/issue/FE-1378/define-durable-state-across-elicitation-sessions","postFetchAt":"2026-08-20T11:01:12.266Z","postUpdatedAt":"2026-08-20T11:01:11.912Z","proposedTitleSha256":"442998135a28eb368d03b58a5ec32ff9fbd4ee0743b7afc16030e3dac70494cf","actualTitleSha256":"442998135a28eb368d03b58a5ec32ff9fbd4ee0743b7afc16030e3dac70494cf","proposedBodySha256":"77648b3f761ba84c2837ad23b418104e13c2182772006d3e175f63b0066cf065","actualBodySha256":"77648b3f761ba84c2837ad23b418104e13c2182772006d3e175f63b0066cf065","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1379","url":"https://linear.app/hash/issue/FE-1379/walking-skeleton-sweep-seam-on-flue-archive","preFetchAt":"2026-08-20T11:01:12.503Z","frozenSourceUpdatedAt":"2026-08-19T16:20:26.560Z","sourceUpdatedAt":"2026-08-19T16:20:26.560Z","sourceTitleSha256":"4508ec17020f4110e54e4fd9bb6409653fa6051f503485280387d982b90eae6f","sourceBodySha256":"77ba77f249d1d1e88dda09de03554a2672f52d91e2e75261cfdbe0520f3c4cb7","proposedTitleSha256":"d11a2dcabcaa56f89789e629bc0d41d5d4756ee3702e7cff79bea4e6f2074ad5","proposedBodySha256":"3b61f9e6a64f171a18eb0482da780e41dab67f44bfd7c7568acca05f5b39c85c"} -{"event":"result","status":"pass","system":"linear","id":"FE-1379","url":"https://linear.app/hash/issue/FE-1379/prove-the-remaining-flue-capabilities","postFetchAt":"2026-08-20T11:01:13.338Z","postUpdatedAt":"2026-08-20T11:01:12.831Z","proposedTitleSha256":"d11a2dcabcaa56f89789e629bc0d41d5d4756ee3702e7cff79bea4e6f2074ad5","actualTitleSha256":"d11a2dcabcaa56f89789e629bc0d41d5d4756ee3702e7cff79bea4e6f2074ad5","proposedBodySha256":"3b61f9e6a64f171a18eb0482da780e41dab67f44bfd7c7568acca05f5b39c85c","actualBodySha256":"3b61f9e6a64f171a18eb0482da780e41dab67f44bfd7c7568acca05f5b39c85c","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1382","url":"https://linear.app/hash/issue/FE-1382/source-dossier-published-fleet-maintenance-models-operational-data-for","preFetchAt":"2026-08-20T11:01:13.582Z","frozenSourceUpdatedAt":"2026-08-19T16:20:26.109Z","sourceUpdatedAt":"2026-08-19T16:20:26.109Z","sourceTitleSha256":"8789e5d3309cda42c6acf9fc2fb9ed8f3fd7f223fd4c9a5b31475a5f39510bca","sourceBodySha256":"98a2f626e1fc039a1f81b0d0fa6ecdf6a39c1fa525a4494097e78c3fb3688c34","proposedTitleSha256":"cca762a296ff0a645b3398b349a1aba34b009e2a72520cd6ca50a1995ecbf782","proposedBodySha256":"dcbee28f52b04684d16de429518666c033f3d5dc2ace992d55b5fe49f5180e7f"} -{"event":"result","status":"pass","system":"linear","id":"FE-1382","url":"https://linear.app/hash/issue/FE-1382/compile-the-truck-fleet-source-dossier","postFetchAt":"2026-08-20T11:01:14.272Z","postUpdatedAt":"2026-08-20T11:01:13.930Z","proposedTitleSha256":"cca762a296ff0a645b3398b349a1aba34b009e2a72520cd6ca50a1995ecbf782","actualTitleSha256":"cca762a296ff0a645b3398b349a1aba34b009e2a72520cd6ca50a1995ecbf782","proposedBodySha256":"dcbee28f52b04684d16de429518666c033f3d5dc2ace992d55b5fe49f5180e7f","actualBodySha256":"dcbee28f52b04684d16de429518666c033f3d5dc2ace992d55b5fe49f5180e7f","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1383","url":"https://linear.app/hash/issue/FE-1383/build-the-elicitation-harness-to-milestone-one-an-interview-that","preFetchAt":"2026-08-20T11:01:14.575Z","frozenSourceUpdatedAt":"2026-08-19T16:26:28.251Z","sourceUpdatedAt":"2026-08-19T16:26:28.251Z","sourceTitleSha256":"d5fbe781b6392a20e99ebbcd938e2556a3591b1eec6b089aa46e450fc54abd2b","sourceBodySha256":"6ffa6c3bd7e56e39402e2b9b3c4a6dca3a3d926202fcfa19b6d41feecdadf78d","proposedTitleSha256":"e16c710e0bf421f6afd4e73b0924ece8d021e65511fe57aef680f3f7854d0577","proposedBodySha256":"3e7b2d47ea437880f995a53d223fb9380873e02ae5adac83ceafd9d38a6f55cb"} -{"event":"result","status":"pass","system":"linear","id":"FE-1383","url":"https://linear.app/hash/issue/FE-1383/build-the-first-complete-elicitation-interview","postFetchAt":"2026-08-20T11:01:15.204Z","postUpdatedAt":"2026-08-20T11:01:14.845Z","proposedTitleSha256":"e16c710e0bf421f6afd4e73b0924ece8d021e65511fe57aef680f3f7854d0577","actualTitleSha256":"e16c710e0bf421f6afd4e73b0924ece8d021e65511fe57aef680f3f7854d0577","proposedBodySha256":"3e7b2d47ea437880f995a53d223fb9380873e02ae5adac83ceafd9d38a6f55cb","actualBodySha256":"3e7b2d47ea437880f995a53d223fb9380873e02ae5adac83ceafd9d38a6f55cb","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1384","url":"https://linear.app/hash/issue/FE-1384/generative-test-corpus-over-the-replay-driver","preFetchAt":"2026-08-20T11:01:15.595Z","frozenSourceUpdatedAt":"2026-08-19T16:20:26.528Z","sourceUpdatedAt":"2026-08-19T16:20:26.528Z","sourceTitleSha256":"34fd893cbe8f38105a28d034e986b36d093d2bb616f11fd3705d9a89ca11a606","sourceBodySha256":"dc23151710c724730fd5e28460cf1e18ad96ee9b6faf63acf4c4385c7b963ab8","proposedTitleSha256":"d578f097f2c89e3b8d52f319f960726bafe7bdf786a8ee7cd7613c52cc1708ed","proposedBodySha256":"bf5b66bf1a18287a64f583e0f7df1761c1d3e13278ff349998410d5aa425156c"} -{"event":"result","status":"pass","system":"linear","id":"FE-1384","url":"https://linear.app/hash/issue/FE-1384/generate-replay-tests-for-the-harness-rules","postFetchAt":"2026-08-20T11:01:16.386Z","postUpdatedAt":"2026-08-20T11:01:15.906Z","proposedTitleSha256":"d578f097f2c89e3b8d52f319f960726bafe7bdf786a8ee7cd7613c52cc1708ed","actualTitleSha256":"d578f097f2c89e3b8d52f319f960726bafe7bdf786a8ee7cd7613c52cc1708ed","proposedBodySha256":"bf5b66bf1a18287a64f583e0f7df1761c1d3e13278ff349998410d5aa425156c","actualBodySha256":"bf5b66bf1a18287a64f583e0f7df1761c1d3e13278ff349998410d5aa425156c","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1385","url":"https://linear.app/hash/issue/FE-1385/dev-app-target-gallery-and-diagnostic-probe-surface","preFetchAt":"2026-08-20T11:01:16.680Z","frozenSourceUpdatedAt":"2026-08-19T16:20:26.147Z","sourceUpdatedAt":"2026-08-19T16:20:26.147Z","sourceTitleSha256":"7e4a538631309c52c61b4d6d4e1d405aa54ba435c20f18e8684b3f2a020ceedb","sourceBodySha256":"1bd36079855c694a19c2c16781b8fe79cb03ea9ed3cd3bbce9b31f3ba0993cb4","proposedTitleSha256":"735ae093eeaea26baacee13aa32bf1a2903b9ea666421c903d460c1987a63543","proposedBodySha256":"9a9849fe884a4047d00b0d673f085c3ca401643df2647eb3a9da07e1530991c6"} -{"event":"result","status":"pass","system":"linear","id":"FE-1385","url":"https://linear.app/hash/issue/FE-1385/expand-the-dev-app-into-a-target-gallery-and-diagnostic-view","postFetchAt":"2026-08-20T11:01:17.304Z","postUpdatedAt":"2026-08-20T11:01:16.969Z","proposedTitleSha256":"735ae093eeaea26baacee13aa32bf1a2903b9ea666421c903d460c1987a63543","actualTitleSha256":"735ae093eeaea26baacee13aa32bf1a2903b9ea666421c903d460c1987a63543","proposedBodySha256":"9a9849fe884a4047d00b0d673f085c3ca401643df2647eb3a9da07e1530991c6","actualBodySha256":"9a9849fe884a4047d00b0d673f085c3ca401643df2647eb3a9da07e1530991c6","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1386","url":"https://linear.app/hash/issue/FE-1386/verify-compaction-leaves-the-durable-entry-projection-intact","preFetchAt":"2026-08-20T11:01:17.544Z","frozenSourceUpdatedAt":"2026-08-19T16:20:26.302Z","sourceUpdatedAt":"2026-08-19T16:20:26.302Z","sourceTitleSha256":"e290d7dbfc6823fb2391b9835c7851aac4dcfd7266cbcca1076a83916a843bc0","sourceBodySha256":"b032d11e90f77e094632a0437a00a3b19ba43f3b74d851fda5f9983d7059a675","proposedTitleSha256":"828d7efb7c4bde98357f8ea410087cc82d009524241bc6f8a4dbab46d701c2a0","proposedBodySha256":"6714044cb3c8c5e5f5a511ec8339ec15f8d639b78814cd2f430d068265c4443f"} -{"event":"result","status":"pass","system":"linear","id":"FE-1386","url":"https://linear.app/hash/issue/FE-1386/test-durable-history-across-transcript-compaction","postFetchAt":"2026-08-20T11:01:18.172Z","postUpdatedAt":"2026-08-20T11:01:17.820Z","proposedTitleSha256":"828d7efb7c4bde98357f8ea410087cc82d009524241bc6f8a4dbab46d701c2a0","actualTitleSha256":"828d7efb7c4bde98357f8ea410087cc82d009524241bc6f8a4dbab46d701c2a0","proposedBodySha256":"6714044cb3c8c5e5f5a511ec8339ec15f8d639b78814cd2f430d068265c4443f","actualBodySha256":"6714044cb3c8c5e5f5a511ec8339ec15f8d639b78814cd2f430d068265c4443f","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1387","url":"https://linear.app/hash/issue/FE-1387/author-the-second-pack-and-freeze-the-plugin-contract","preFetchAt":"2026-08-20T11:01:18.441Z","frozenSourceUpdatedAt":"2026-08-19T16:20:26.344Z","sourceUpdatedAt":"2026-08-19T16:20:26.344Z","sourceTitleSha256":"1e34347c94852da08269de757dd7c58086a76b740fcff0e69c8104de4853eff7","sourceBodySha256":"0e246edd61bf61e1ca4ca67103d4f396f0d7b1d11611474727197431788ca94b","proposedTitleSha256":"37bf181908327ddd86b0e09bc446b5eb02383a441693d6a9fa604d7455756205","proposedBodySha256":"e02aff72a2640dbe03ff1027ec7ec203c67be06796834148e5ff7b577cbfd66a"} -{"event":"result","status":"pass","system":"linear","id":"FE-1387","url":"https://linear.app/hash/issue/FE-1387/choose-a-second-target-and-stabilize-the-plugin-interface","postFetchAt":"2026-08-20T11:01:19.040Z","postUpdatedAt":"2026-08-20T11:01:18.698Z","proposedTitleSha256":"37bf181908327ddd86b0e09bc446b5eb02383a441693d6a9fa604d7455756205","actualTitleSha256":"37bf181908327ddd86b0e09bc446b5eb02383a441693d6a9fa604d7455756205","proposedBodySha256":"e02aff72a2640dbe03ff1027ec7ec203c67be06796834148e5ff7b577cbfd66a","actualBodySha256":"e02aff72a2640dbe03ff1027ec7ec203c67be06796834148e5ff7b577cbfd66a","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1388","url":"https://linear.app/hash/issue/FE-1388/scaffold-the-bun-workspace-and-prove-the-ci-smoke","preFetchAt":"2026-08-20T11:01:19.362Z","frozenSourceUpdatedAt":"2026-08-19T16:20:27.054Z","sourceUpdatedAt":"2026-08-19T16:20:27.054Z","sourceTitleSha256":"1f553d53fd1c4493bf1808505a85c8275a908f377cc646c3d822d7ee9cc4a4b7","sourceBodySha256":"5b79a1a15bf83d384bc11b114188db2252eeec734c0299f196f56f83c59a4d94","proposedTitleSha256":"cba702bfe08fbfe6c118f53f94058b89f4e279b1e65f7eaec8f8a764dcc99249","proposedBodySha256":"09f30e8f38b786bb7d9645dec9b45137931774e2dbe8d033acabf410fac84a7b"} -{"event":"result","status":"pass","system":"linear","id":"FE-1388","url":"https://linear.app/hash/issue/FE-1388/create-the-bun-workspace-and-enforce-dependency-boundaries","postFetchAt":"2026-08-20T11:01:19.949Z","postUpdatedAt":"2026-08-20T11:01:19.623Z","proposedTitleSha256":"cba702bfe08fbfe6c118f53f94058b89f4e279b1e65f7eaec8f8a764dcc99249","actualTitleSha256":"cba702bfe08fbfe6c118f53f94058b89f4e279b1e65f7eaec8f8a764dcc99249","proposedBodySha256":"09f30e8f38b786bb7d9645dec9b45137931774e2dbe8d033acabf410fac84a7b","actualBodySha256":"09f30e8f38b786bb7d9645dec9b45137931774e2dbe8d033acabf410fac84a7b","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1389","url":"https://linear.app/hash/issue/FE-1389/walking-skeleton-the-harness-asks-a-free-text-question-and-binds-the","preFetchAt":"2026-08-20T11:01:20.312Z","frozenSourceUpdatedAt":"2026-08-19T16:20:28.046Z","sourceUpdatedAt":"2026-08-19T16:20:28.046Z","sourceTitleSha256":"02a0530dca8c589424e2bc9a4813afe1a1cf33887212424ba50fdeba8cb20a49","sourceBodySha256":"3623852992eec05f9567c745d0d253773713feb486dbef929fc28db16f7a317c","proposedTitleSha256":"671d58f11fdffe9a2aef0c9355b44e9b1e5c66d0c25504369b6a25de54e0a6b3","proposedBodySha256":"61d11a095b9ca88dfcd274a2d50e1d1e337713abfce81dc75cf29cbd1dad3c80"} -{"event":"result","status":"pass","system":"linear","id":"FE-1389","url":"https://linear.app/hash/issue/FE-1389/implement-the-first-suspended-free-text-question","postFetchAt":"2026-08-20T11:01:20.990Z","postUpdatedAt":"2026-08-20T11:01:20.583Z","proposedTitleSha256":"671d58f11fdffe9a2aef0c9355b44e9b1e5c66d0c25504369b6a25de54e0a6b3","actualTitleSha256":"671d58f11fdffe9a2aef0c9355b44e9b1e5c66d0c25504369b6a25de54e0a6b3","proposedBodySha256":"61d11a095b9ca88dfcd274a2d50e1d1e337713abfce81dc75cf29cbd1dad3c80","actualBodySha256":"61d11a095b9ca88dfcd274a2d50e1d1e337713abfce81dc75cf29cbd1dad3c80","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1390","url":"https://linear.app/hash/issue/FE-1390/capture-envelope-storage-port-and-the-local-capture-store","preFetchAt":"2026-08-20T11:01:21.270Z","frozenSourceUpdatedAt":"2026-08-19T16:20:27.947Z","sourceUpdatedAt":"2026-08-19T16:20:27.947Z","sourceTitleSha256":"79014f58a3fcbcbe710117e024203f408d2c85b31178827f4e2a1f2f7c50d74b","sourceBodySha256":"dce103e6c13f999a39d866e51e61826185cc97f34dd221baa2a8b8f55bf77826","proposedTitleSha256":"4f9a2a6a52ee76fc88cf973ed854c90d5a3425c1b81d444fdee080bb2736c392","proposedBodySha256":"6cf8d9572068580ce883faeed54d1ef0ac20d8c60666d32d810c1ff45b56a0ad"} -{"event":"result","status":"pass","system":"linear","id":"FE-1390","url":"https://linear.app/hash/issue/FE-1390/implement-capture-history-and-local-persistence","postFetchAt":"2026-08-20T11:01:22.191Z","postUpdatedAt":"2026-08-20T11:01:21.541Z","proposedTitleSha256":"4f9a2a6a52ee76fc88cf973ed854c90d5a3425c1b81d444fdee080bb2736c392","actualTitleSha256":"4f9a2a6a52ee76fc88cf973ed854c90d5a3425c1b81d444fdee080bb2736c392","proposedBodySha256":"6cf8d9572068580ce883faeed54d1ef0ac20d8c60666d32d810c1ff45b56a0ad","actualBodySha256":"6cf8d9572068580ce883faeed54d1ef0ac20d8c60666d32d810c1ff45b56a0ad","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1391","url":"https://linear.app/hash/issue/FE-1391/durable-entry-projection-harness-resolved-anchoring-and-the-session","preFetchAt":"2026-08-20T11:01:22.490Z","frozenSourceUpdatedAt":"2026-08-20T08:02:56.636Z","sourceUpdatedAt":"2026-08-20T08:02:56.636Z","sourceTitleSha256":"64ec7826e8f32ec6ab84c8a8b36879fb5b9b6c7153d9f5ff3fca2616a42ff230","sourceBodySha256":"464a7faf1c52aa7bbc16b542aee5d1085fff78cd671575e61dd832e2ec6295d0","proposedTitleSha256":"cc41527c73a8817b0d2f23773f40b6d72de92f3d91a5a2c6b5259a68861de1c8","proposedBodySha256":"a10cd59d9b58e0903965106cfc42cace3aa01a8366c414a76196c867c46c5683"} -{"event":"result","status":"pass","system":"linear","id":"FE-1391","url":"https://linear.app/hash/issue/FE-1391/resolve-evidence-quotes-to-durable-conversation-entries","postFetchAt":"2026-08-20T11:01:23.150Z","postUpdatedAt":"2026-08-20T11:01:22.808Z","proposedTitleSha256":"cc41527c73a8817b0d2f23773f40b6d72de92f3d91a5a2c6b5259a68861de1c8","actualTitleSha256":"cc41527c73a8817b0d2f23773f40b6d72de92f3d91a5a2c6b5259a68861de1c8","proposedBodySha256":"a10cd59d9b58e0903965106cfc42cace3aa01a8366c414a76196c867c46c5683","actualBodySha256":"a10cd59d9b58e0903965106cfc42cace3aa01a8366c414a76196c867c46c5683","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1392","url":"https://linear.app/hash/issue/FE-1392/settlement-trigger-and-sweep-the-first-captured-statement","preFetchAt":"2026-08-20T11:01:23.399Z","frozenSourceUpdatedAt":"2026-08-20T08:02:56.825Z","sourceUpdatedAt":"2026-08-20T08:02:56.825Z","sourceTitleSha256":"dc560fe9d1bf4ae882310eb8563baa90c49375e0ab8009a2069bddb5877fe98f","sourceBodySha256":"7a58e8a4d7e8960c3b41b0b0a91396e7c14f002f3fbf68ec63aa3dfdc8e46b3b","proposedTitleSha256":"e5bc000d22834d39bfc4daefdec98c8bca2cb0883dc5d4e2a4e44b0f47e7d8a6","proposedBodySha256":"71902c740537f89d9251bcdab39d0569d59e2445e9198305e578100b9eafe77d"} -{"event":"result","status":"pass","system":"linear","id":"FE-1392","url":"https://linear.app/hash/issue/FE-1392/capture-settled-conversation-statements-safely","postFetchAt":"2026-08-20T11:01:24.048Z","postUpdatedAt":"2026-08-20T11:01:23.655Z","proposedTitleSha256":"e5bc000d22834d39bfc4daefdec98c8bca2cb0883dc5d4e2a4e44b0f47e7d8a6","actualTitleSha256":"e5bc000d22834d39bfc4daefdec98c8bca2cb0883dc5d4e2a4e44b0f47e7d8a6","proposedBodySha256":"71902c740537f89d9251bcdab39d0569d59e2445e9198305e578100b9eafe77d","actualBodySha256":"71902c740537f89d9251bcdab39d0569d59e2445e9198305e578100b9eafe77d","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1393","url":"https://linear.app/hash/issue/FE-1393/plugin-sdk-and-the-gherkin-plugin-the-first-projected-artifact","preFetchAt":"2026-08-20T11:01:24.375Z","frozenSourceUpdatedAt":"2026-08-19T16:20:25.951Z","sourceUpdatedAt":"2026-08-19T16:20:25.951Z","sourceTitleSha256":"c19a8e89dc13b6eb7cf8b1e023e20a8f82ef1d254ab865caf2fdea5e2a463a3a","sourceBodySha256":"eb4be2fba6a05bdbcdace930230c21ca24be097e2d707bcbf8441cebeb1367c8","proposedTitleSha256":"a7ae805905d5b1a216a522c1492669e21aca11ab15eb0892cbcd4a44e84d6dec","proposedBodySha256":"9cc7f0cedf11c039e9e6c55f9904fd35340c2cb329d275cdefa77e022695fffe"} -{"event":"result","status":"pass","system":"linear","id":"FE-1393","url":"https://linear.app/hash/issue/FE-1393/produce-the-first-gherkin-artifact-through-the-plugin-sdk","postFetchAt":"2026-08-20T11:01:25.046Z","postUpdatedAt":"2026-08-20T11:01:24.693Z","proposedTitleSha256":"a7ae805905d5b1a216a522c1492669e21aca11ab15eb0892cbcd4a44e84d6dec","actualTitleSha256":"a7ae805905d5b1a216a522c1492669e21aca11ab15eb0892cbcd4a44e84d6dec","proposedBodySha256":"9cc7f0cedf11c039e9e6c55f9904fd35340c2cb329d275cdefa77e022695fffe","actualBodySha256":"9cc7f0cedf11c039e9e6c55f9904fd35340c2cb329d275cdefa77e022695fffe","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1394","url":"https://linear.app/hash/issue/FE-1394/conflict-supersession-and-the-interpretation-render","preFetchAt":"2026-08-20T11:01:25.293Z","frozenSourceUpdatedAt":"2026-08-19T16:20:26.378Z","sourceUpdatedAt":"2026-08-19T16:20:26.378Z","sourceTitleSha256":"300684bbdff69dcc7f9ee11eb17102a826a7e24bfd5cc9b47d7c2cc6fa5c6693","sourceBodySha256":"59342c6a55a213c528d8566c757ac6a85af9a08819e19af57664798e93b8d2ef","proposedTitleSha256":"8e8cc690f434c0e6b827fec9acd5887087cd5e638347b55a486eab0b5139a497","proposedBodySha256":"3a738a9370e3a1698e0c0d0fd6c966d15e7635f9be498470ac8c8ef37e8297b1"} -{"event":"result","status":"pass","system":"linear","id":"FE-1394","url":"https://linear.app/hash/issue/FE-1394/preserve-conflicts-until-the-user-resolves-them","postFetchAt":"2026-08-20T11:01:26.341Z","postUpdatedAt":"2026-08-20T11:01:25.600Z","proposedTitleSha256":"8e8cc690f434c0e6b827fec9acd5887087cd5e638347b55a486eab0b5139a497","actualTitleSha256":"8e8cc690f434c0e6b827fec9acd5887087cd5e638347b55a486eab0b5139a497","proposedBodySha256":"3a738a9370e3a1698e0c0d0fd6c966d15e7635f9be498470ac8c8ef37e8297b1","actualBodySha256":"3a738a9370e3a1698e0c0d0fd6c966d15e7635f9be498470ac8c8ef37e8297b1","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1395","url":"https://linear.app/hash/issue/FE-1395/the-full-affordance-set-choices-questionnaire-chaining-and-the-absence","preFetchAt":"2026-08-20T11:01:26.699Z","frozenSourceUpdatedAt":"2026-08-19T16:20:26.675Z","sourceUpdatedAt":"2026-08-19T16:20:26.675Z","sourceTitleSha256":"3c8ed19b489983343fae2ea6dcbced4c87330e476f984fb75fa9937fb1974f67","sourceBodySha256":"2a5426c3cda4bfd5d3b5e7451880acfb2000efd8f906ae9512651fa23aa94d01","proposedTitleSha256":"9142527acadbf1d1d8d0a777a129404cc15df6c4471aacc2fe91601b2c470397","proposedBodySha256":"325fb0866d84fc3386d7dadd18e1fc5140e0f02bc64508826dada5a514c79318"} -{"event":"result","status":"pass","system":"linear","id":"FE-1395","url":"https://linear.app/hash/issue/FE-1395/add-choices-questionnaires-and-explicit-absence-replies","postFetchAt":"2026-08-20T11:01:27.305Z","postUpdatedAt":"2026-08-20T11:01:26.966Z","proposedTitleSha256":"9142527acadbf1d1d8d0a777a129404cc15df6c4471aacc2fe91601b2c470397","actualTitleSha256":"9142527acadbf1d1d8d0a777a129404cc15df6c4471aacc2fe91601b2c470397","proposedBodySha256":"325fb0866d84fc3386d7dadd18e1fc5140e0f02bc64508826dada5a514c79318","actualBodySha256":"325fb0866d84fc3386d7dadd18e1fc5140e0f02bc64508826dada5a514c79318","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1396","url":"https://linear.app/hash/issue/FE-1396/re-entry-briefing-resume-reconciliation-and-restart-durability","preFetchAt":"2026-08-20T11:01:27.814Z","frozenSourceUpdatedAt":"2026-08-19T16:20:25.918Z","sourceUpdatedAt":"2026-08-19T16:20:25.918Z","sourceTitleSha256":"81da9d71280d7fa1fd21ba19d101e1cd02d113fd2e626f425d50ea302efe93f8","sourceBodySha256":"a107fcbcc646a6054c66bd208141252bda7298308546c689e1fcf9d0ff79d797","proposedTitleSha256":"66b2c12e3036f132c63ee071cd1e98f126bccccf9223062effe4e2f9c3658084","proposedBodySha256":"60a1aa6eb6aacbb63940637435b2ecd8dcbc8d38ab4e8057976301ba89665b59"} -{"event":"result","status":"pass","system":"linear","id":"FE-1396","url":"https://linear.app/hash/issue/FE-1396/restore-interview-context-after-resume-and-restart","postFetchAt":"2026-08-20T11:01:28.444Z","postUpdatedAt":"2026-08-20T11:01:28.090Z","proposedTitleSha256":"66b2c12e3036f132c63ee071cd1e98f126bccccf9223062effe4e2f9c3658084","actualTitleSha256":"66b2c12e3036f132c63ee071cd1e98f126bccccf9223062effe4e2f9c3658084","proposedBodySha256":"60a1aa6eb6aacbb63940637435b2ecd8dcbc8d38ab4e8057976301ba89665b59","actualBodySha256":"60a1aa6eb6aacbb63940637435b2ecd8dcbc8d38ab4e8057976301ba89665b59","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1397","url":"https://linear.app/hash/issue/FE-1397/validate-the-generic-ir-definition-against-worked-payload-designs","preFetchAt":"2026-08-20T11:01:28.694Z","frozenSourceUpdatedAt":"2026-08-19T16:19:46.091Z","sourceUpdatedAt":"2026-08-19T16:19:46.091Z","sourceTitleSha256":"57101af27a029ed36c454595d43f1a63d722dc7fecde14313f61c122f3ff195b","sourceBodySha256":"676e3faa8b8d4801db351f8c82bdf8899036df30c8a5b2789d9a3f3e2b2ca534","proposedTitleSha256":"f07438c59634073e97ad1225c8b971ececa39d27ebd19aa27958da8e4eb11bf6","proposedBodySha256":"728d19cef7d300fa63be05be9ba870290863be42072f08e865cee254f2ec9cbf"} -{"event":"result","status":"pass","system":"linear","id":"FE-1397","url":"https://linear.app/hash/issue/FE-1397/validate-the-generic-ir-against-worked-plugin-payloads","postFetchAt":"2026-08-20T11:01:29.298Z","postUpdatedAt":"2026-08-20T11:01:28.974Z","proposedTitleSha256":"f07438c59634073e97ad1225c8b971ececa39d27ebd19aa27958da8e4eb11bf6","actualTitleSha256":"f07438c59634073e97ad1225c8b971ececa39d27ebd19aa27958da8e4eb11bf6","proposedBodySha256":"728d19cef7d300fa63be05be9ba870290863be42072f08e865cee254f2ec9cbf","actualBodySha256":"728d19cef7d300fa63be05be9ba870290863be42072f08e865cee254f2ec9cbf","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1399","url":"https://linear.app/hash/issue/FE-1399/make-the-ci-gates-and-dev-app-fail-loudly-where-review-found-they-fail","preFetchAt":"2026-08-20T11:01:29.565Z","frozenSourceUpdatedAt":"2026-08-19T16:20:26.590Z","sourceUpdatedAt":"2026-08-19T16:20:26.590Z","sourceTitleSha256":"2dccdd3bedc0bb7b28b086c4a1c0c8da6c68a894767abe0713ba8b3550a581d7","sourceBodySha256":"6b0a691af9b858b532b0ac30b62f352fcf19afc87071534e5f6ea25b13cc6379","proposedTitleSha256":"a376b90e615f99d8f593ca82d7aebcf143ae49697119f3126cf511dea4242c64","proposedBodySha256":"93f520f9c2e08c999d5105fdd07a770dbe8a20b6f8bea6c1995067bcd44cf221"} -{"event":"result","status":"pass","system":"linear","id":"FE-1399","url":"https://linear.app/hash/issue/FE-1399/fix-verified-silent-failures-in-ci-and-the-dev-app","postFetchAt":"2026-08-20T11:01:30.247Z","postUpdatedAt":"2026-08-20T11:01:29.815Z","proposedTitleSha256":"a376b90e615f99d8f593ca82d7aebcf143ae49697119f3126cf511dea4242c64","actualTitleSha256":"a376b90e615f99d8f593ca82d7aebcf143ae49697119f3126cf511dea4242c64","proposedBodySha256":"93f520f9c2e08c999d5105fdd07a770dbe8a20b6f8bea6c1995067bcd44cf221","actualBodySha256":"93f520f9c2e08c999d5105fdd07a770dbe8a20b6f8bea6c1995067bcd44cf221","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1400","url":"https://linear.app/hash/issue/FE-1400/close-the-review-found-gaps-where-the-gates-dev-app-and-baseline","preFetchAt":"2026-08-20T11:01:30.508Z","frozenSourceUpdatedAt":"2026-08-19T16:20:26.709Z","sourceUpdatedAt":"2026-08-19T16:20:26.709Z","sourceTitleSha256":"4e4a7a42972cd87205b806f4c89d7ba44e0ab2ddb6efa1abbab18de34022ed39","sourceBodySha256":"ea04928499443681cda2c797a268fe7c62b970dacb49d787ada0a84d2e516257","proposedTitleSha256":"26fb6f297b802aab16e250ef0aa30c16815e5b2f894b7c6dbc8666e569344e65","proposedBodySha256":"096ccde21fd800d9c0cbb751b580ad160b998666313b43702030a78056471204"} -{"event":"result","status":"pass","system":"linear","id":"FE-1400","url":"https://linear.app/hash/issue/FE-1400/strengthen-verification-dev-storage-and-the-baseline-runner","postFetchAt":"2026-08-20T11:01:31.131Z","postUpdatedAt":"2026-08-20T11:01:30.774Z","proposedTitleSha256":"26fb6f297b802aab16e250ef0aa30c16815e5b2f894b7c6dbc8666e569344e65","actualTitleSha256":"26fb6f297b802aab16e250ef0aa30c16815e5b2f894b7c6dbc8666e569344e65","proposedBodySha256":"096ccde21fd800d9c0cbb751b580ad160b998666313b43702030a78056471204","actualBodySha256":"096ccde21fd800d9c0cbb751b580ad160b998666313b43702030a78056471204","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1401","url":"https://linear.app/hash/issue/FE-1401/resolve-the-follow-ups-from-the-stack-legibility-session","preFetchAt":"2026-08-20T11:01:32.778Z","frozenSourceUpdatedAt":"2026-08-19T16:20:28.561Z","sourceUpdatedAt":"2026-08-19T16:20:28.561Z","sourceTitleSha256":"3eb163af50263452be68f66950600c587100fa854645791d88be95fddb53e306","sourceBodySha256":"add5a3da06b366dbc9fe4cdc44b9ce161d2775c8b43fb18af380b2fe2c9c9d5e","proposedTitleSha256":"b8874647b7f094aff4db44c720b9053d7f2c155ea641aea541bcc35728b18b60","proposedBodySha256":"9e51a20089190e2e34e70e0a4b6faa35b73c8818a11eb8dce1fcd5402c652a20"} -{"event":"result","status":"pass","system":"linear","id":"FE-1401","url":"https://linear.app/hash/issue/FE-1401/resolve-the-stack-legibility-follow-ups","postFetchAt":"2026-08-20T11:01:34.369Z","postUpdatedAt":"2026-08-20T11:01:33.059Z","proposedTitleSha256":"b8874647b7f094aff4db44c720b9053d7f2c155ea641aea541bcc35728b18b60","actualTitleSha256":"b8874647b7f094aff4db44c720b9053d7f2c155ea641aea541bcc35728b18b60","proposedBodySha256":"9e51a20089190e2e34e70e0a4b6faa35b73c8818a11eb8dce1fcd5402c652a20","actualBodySha256":"9e51a20089190e2e34e70e0a4b6faa35b73c8818a11eb8dce1fcd5402c652a20","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1402","url":"https://linear.app/hash/issue/FE-1402/decide-when-an-elicitation-is-complete-or-should-stop","preFetchAt":"2026-08-20T11:01:34.672Z","frozenSourceUpdatedAt":"2026-08-19T16:19:47.912Z","sourceUpdatedAt":"2026-08-19T16:19:47.912Z","sourceTitleSha256":"518f7d5cf52ba2195138f03237f8cebbff16622631fd34f3d96e68e9066fc5db","sourceBodySha256":"0443796ebe3de81af985a3621799610618b8f020b26ee7e4558e237ea1752df1","proposedTitleSha256":"91d4dd660323d85121bb7cc935e581934239bac3ec00303ec944077276a33f34","proposedBodySha256":"b2abd827fdba53895fdccc6d227d8981e7615a103564a8b5955108faf44a49fa"} -{"event":"result","status":"pass","system":"linear","id":"FE-1402","url":"https://linear.app/hash/issue/FE-1402/define-and-rehearse-the-elicitation-completion-contract","postFetchAt":"2026-08-20T11:01:35.451Z","postUpdatedAt":"2026-08-20T11:01:35.015Z","proposedTitleSha256":"91d4dd660323d85121bb7cc935e581934239bac3ec00303ec944077276a33f34","actualTitleSha256":"91d4dd660323d85121bb7cc935e581934239bac3ec00303ec944077276a33f34","proposedBodySha256":"b2abd827fdba53895fdccc6d227d8981e7615a103564a8b5955108faf44a49fa","actualBodySha256":"b2abd827fdba53895fdccc6d227d8981e7615a103564a8b5955108faf44a49fa","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1403","url":"https://linear.app/hash/issue/FE-1403/assemble-the-cps-packs-interviewing-guidance-and-desk-test-it-where","preFetchAt":"2026-08-20T11:01:35.692Z","frozenSourceUpdatedAt":"2026-08-19T16:19:48.641Z","sourceUpdatedAt":"2026-08-19T16:19:48.641Z","sourceTitleSha256":"76a2a8c3c3f4070aa4979a9414bd2d22eca8e87303966a2377bf17fc8a417196","sourceBodySha256":"0f8b52a00cb0756e5a5175f053ee2911bca209347466334d61f9f7607f78ccdc","proposedTitleSha256":"c2dd05ba5210b2e540bc4f337998070f3341f1bdaf3d14bdfdeca4526924de72","proposedBodySha256":"d4f1804674241c2b523f122d3a510d4730eabaa564dc2a589e63886e48b94240"} -{"event":"result","status":"pass","system":"linear","id":"FE-1403","url":"https://linear.app/hash/issue/FE-1403/assemble-and-test-the-cps-interview-guidance","postFetchAt":"2026-08-20T11:01:36.290Z","postUpdatedAt":"2026-08-20T11:01:35.971Z","proposedTitleSha256":"c2dd05ba5210b2e540bc4f337998070f3341f1bdaf3d14bdfdeca4526924de72","actualTitleSha256":"c2dd05ba5210b2e540bc4f337998070f3341f1bdaf3d14bdfdeca4526924de72","proposedBodySha256":"d4f1804674241c2b523f122d3a510d4730eabaa564dc2a589e63886e48b94240","actualBodySha256":"d4f1804674241c2b523f122d3a510d4730eabaa564dc2a589e63886e48b94240","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1404","url":"https://linear.app/hash/issue/FE-1404/run-the-armed-baseline-condition-3-completion-contract-and-pack","preFetchAt":"2026-08-20T11:01:36.723Z","frozenSourceUpdatedAt":"2026-08-19T16:19:48.957Z","sourceUpdatedAt":"2026-08-19T16:19:48.957Z","sourceTitleSha256":"dc85e7886b6e6ba00a4848394abc5de81f7592f16cfdd8db8291d6586675e22f","sourceBodySha256":"f88abfbaa37d65613194b26c3c52b12356af84b1bd39e5b8b062b86d03fefd85","proposedTitleSha256":"cbed4c76e448b5ccb190c628bbd109a0c35dbfabe879716ff24ebb8c061e732b","proposedBodySha256":"0f61b1baed5eb374bd7366fc51344fd21a65f900ce7a4fc273241ef919819b9d"} -{"event":"result","status":"pass","system":"linear","id":"FE-1404","url":"https://linear.app/hash/issue/FE-1404/run-the-third-baseline-with-completion-and-interview-guidance","postFetchAt":"2026-08-20T11:01:37.377Z","postUpdatedAt":"2026-08-20T11:01:37.001Z","proposedTitleSha256":"cbed4c76e448b5ccb190c628bbd109a0c35dbfabe879716ff24ebb8c061e732b","actualTitleSha256":"cbed4c76e448b5ccb190c628bbd109a0c35dbfabe879716ff24ebb8c061e732b","proposedBodySha256":"0f61b1baed5eb374bd7366fc51344fd21a65f900ce7a4fc273241ef919819b9d","actualBodySha256":"0f61b1baed5eb374bd7366fc51344fd21a65f900ce7a4fc273241ef919819b9d","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1405","url":"https://linear.app/hash/issue/FE-1405/draft-the-cps-payload-interiors-annotated-shapes-for-the-ten-kinds","preFetchAt":"2026-08-20T11:01:37.619Z","frozenSourceUpdatedAt":"2026-08-20T08:02:56.864Z","sourceUpdatedAt":"2026-08-20T08:02:56.864Z","sourceTitleSha256":"6684fe28ff26349efddef701756334dbc89e05ad594c401f49a8b0f575f0845a","sourceBodySha256":"42b2be3331cc53b8d4f319769fc410edee6afe23b96111e1f79cc3adf9c59785","proposedTitleSha256":"67364fd5e5963238cb0aea5624374d9a6c0d81adeb00f213467ca08100955034","proposedBodySha256":"cf4bbd9f6c5f81cac7a38ae614a1a87011efdf7ca19020c95e7e14f177fc6517"} -{"event":"result","status":"pass","system":"linear","id":"FE-1405","url":"https://linear.app/hash/issue/FE-1405/draft-and-test-the-cps-payload-schemas","postFetchAt":"2026-08-20T11:01:38.202Z","postUpdatedAt":"2026-08-20T11:01:37.874Z","proposedTitleSha256":"67364fd5e5963238cb0aea5624374d9a6c0d81adeb00f213467ca08100955034","actualTitleSha256":"67364fd5e5963238cb0aea5624374d9a6c0d81adeb00f213467ca08100955034","proposedBodySha256":"cf4bbd9f6c5f81cac7a38ae614a1a87011efdf7ca19020c95e7e14f177fc6517","actualBodySha256":"cf4bbd9f6c5f81cac7a38ae614a1a87011efdf7ca19020c95e7e14f177fc6517","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1406","url":"https://linear.app/hash/issue/FE-1406/design-reusable-elicitation-strategies","preFetchAt":"2026-08-20T11:01:38.460Z","frozenSourceUpdatedAt":"2026-08-19T16:20:27.749Z","sourceUpdatedAt":"2026-08-19T16:20:27.749Z","sourceTitleSha256":"9e9eb8ab449ad16c522398efabb98d5eff616e2e1e7cd8c81b99ff2b54d86ba2","sourceBodySha256":"c5698449d7a48b8b4f4f1b3f9dead3407ae1a737b26c62c6034528c809b993f9","proposedTitleSha256":"9e9eb8ab449ad16c522398efabb98d5eff616e2e1e7cd8c81b99ff2b54d86ba2","proposedBodySha256":"25af8c35ff021b508612a58d96316c4ec10415e9abed4f2e32e0ccb1fe4100c4"} -{"event":"result","status":"pass","system":"linear","id":"FE-1406","url":"https://linear.app/hash/issue/FE-1406/design-reusable-elicitation-strategies","postFetchAt":"2026-08-20T11:01:39.149Z","postUpdatedAt":"2026-08-20T11:01:38.757Z","proposedTitleSha256":"9e9eb8ab449ad16c522398efabb98d5eff616e2e1e7cd8c81b99ff2b54d86ba2","actualTitleSha256":"9e9eb8ab449ad16c522398efabb98d5eff616e2e1e7cd8c81b99ff2b54d86ba2","proposedBodySha256":"25af8c35ff021b508612a58d96316c4ec10415e9abed4f2e32e0ccb1fe4100c4","actualBodySha256":"25af8c35ff021b508612a58d96316c4ec10415e9abed4f2e32e0ccb1fe4100c4","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1407","url":"https://linear.app/hash/issue/FE-1407/catalogue-the-frontier-elicitor-failure-modes-the-published","preFetchAt":"2026-08-20T11:01:39.410Z","frozenSourceUpdatedAt":"2026-08-19T16:19:49.974Z","sourceUpdatedAt":"2026-08-19T16:19:49.974Z","sourceTitleSha256":"20bc9b033919f9de69d2601587094279f30a7542246f0b91a234fc77247e5500","sourceBodySha256":"147f3f51d5bb84c4d365fcbeb62793a602f48218bcfa36358fb2179f38e737cd","proposedTitleSha256":"168b66b77c82a14116e0f84ad35b26a630d9d70155415c6117cac5b8cabcf3de","proposedBodySha256":"58bff2297ba46cb4a223217bb178784550174cb0d72b951d0a4b631e97bffe9f"} -{"event":"result","status":"pass","system":"linear","id":"FE-1407","url":"https://linear.app/hash/issue/FE-1407/catalogue-elicitor-failures-that-published-measures-miss","postFetchAt":"2026-08-20T11:01:40.049Z","postUpdatedAt":"2026-08-20T11:01:39.687Z","proposedTitleSha256":"168b66b77c82a14116e0f84ad35b26a630d9d70155415c6117cac5b8cabcf3de","actualTitleSha256":"168b66b77c82a14116e0f84ad35b26a630d9d70155415c6117cac5b8cabcf3de","proposedBodySha256":"58bff2297ba46cb4a223217bb178784550174cb0d72b951d0a4b631e97bffe9f","actualBodySha256":"58bff2297ba46cb4a223217bb178784550174cb0d72b951d0a4b631e97bffe9f","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1419","url":"https://linear.app/hash/issue/FE-1419/close-the-seams-where-the-capture-store-and-verification-gates-claim","preFetchAt":"2026-08-20T11:01:40.294Z","frozenSourceUpdatedAt":"2026-08-19T16:20:27.978Z","sourceUpdatedAt":"2026-08-19T16:20:27.978Z","sourceTitleSha256":"c7839e241c62f56c68fa6c425f55035423c76bda7a42bdbb9916214a3d0ffdca","sourceBodySha256":"8943da27bdc24725789f0721adc47978caad9bc3dc841c5086d8bd7860c07b59","proposedTitleSha256":"bee550820a6ba0ba0d693aa7f58f0ade1b3deeb5c0871290f3ebbd71a16fe342","proposedBodySha256":"cb43c18d19933fb4d41b43b6ba6ebf2a0bbb2ee0bf57a1837ea7cb6045bccc8e"} -{"event":"result","status":"pass","system":"linear","id":"FE-1419","url":"https://linear.app/hash/issue/FE-1419/align-capture-store-rules-and-verification-claims","postFetchAt":"2026-08-20T11:01:40.891Z","postUpdatedAt":"2026-08-20T11:01:40.563Z","proposedTitleSha256":"bee550820a6ba0ba0d693aa7f58f0ade1b3deeb5c0871290f3ebbd71a16fe342","actualTitleSha256":"bee550820a6ba0ba0d693aa7f58f0ade1b3deeb5c0871290f3ebbd71a16fe342","proposedBodySha256":"cb43c18d19933fb4d41b43b6ba6ebf2a0bbb2ee0bf57a1837ea7cb6045bccc8e","actualBodySha256":"cb43c18d19933fb4d41b43b6ba6ebf2a0bbb2ee0bf57a1837ea7cb6045bccc8e","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1420","url":"https://linear.app/hash/issue/FE-1420/the-affordance-protocol-survives-retries-unknown-forms-and-abandoned","preFetchAt":"2026-08-20T11:01:41.158Z","frozenSourceUpdatedAt":"2026-08-19T16:20:26.642Z","sourceUpdatedAt":"2026-08-19T16:20:26.642Z","sourceTitleSha256":"3bfff13eaf86dd08b581c3c256de11384d3a0322712bec45e72909d23488c045","sourceBodySha256":"81b2a078103ec981c418c6d8583f31e301dd47b5025e06e967b13f9143b7123c","proposedTitleSha256":"3520d374d5f0b363f505c96c37efeeed65db0724230dccf71150240faec331c5","proposedBodySha256":"316d82c70192905999f13c7cb3a972faeca36f024e8e87de11ddd7ed3822536f"} -{"event":"result","status":"pass","system":"linear","id":"FE-1420","url":"https://linear.app/hash/issue/FE-1420/make-affordance-handling-safe-under-retries-and-abandonment","postFetchAt":"2026-08-20T11:01:41.862Z","postUpdatedAt":"2026-08-20T11:01:41.516Z","proposedTitleSha256":"3520d374d5f0b363f505c96c37efeeed65db0724230dccf71150240faec331c5","actualTitleSha256":"3520d374d5f0b363f505c96c37efeeed65db0724230dccf71150240faec331c5","proposedBodySha256":"316d82c70192905999f13c7cb3a972faeca36f024e8e87de11ddd7ed3822536f","actualBodySha256":"316d82c70192905999f13c7cb3a972faeca36f024e8e87de11ddd7ed3822536f","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1422","url":"https://linear.app/hash/issue/FE-1422/the-ask-protocol-is-substrate-portable-mechanism-moves-from-the-flue","preFetchAt":"2026-08-20T11:01:42.740Z","frozenSourceUpdatedAt":"2026-08-20T08:02:56.900Z","sourceUpdatedAt":"2026-08-20T08:02:56.900Z","sourceTitleSha256":"2f1e7a4a1dc36d322f689117372148fc36b8b32010e7c5a83ad8b5164882edd3","sourceBodySha256":"896c07b62ab8f586a5b0a3e91d47dd2227e5e69ab4b76c60e2ee33afea3de43d","proposedTitleSha256":"21ba840643993170f966a14395f4216a18910b1215ddb901ddef073977bbeded","proposedBodySha256":"e12e25753221ac1b0052d013d53da4cdb98ed42d2b4d2bfbbc0cc3c7725c4029"} -{"event":"result","status":"pass","system":"linear","id":"FE-1422","url":"https://linear.app/hash/issue/FE-1422/move-the-portable-ask-protocol-into-core","postFetchAt":"2026-08-20T11:01:43.540Z","postUpdatedAt":"2026-08-20T11:01:43.248Z","proposedTitleSha256":"21ba840643993170f966a14395f4216a18910b1215ddb901ddef073977bbeded","actualTitleSha256":"21ba840643993170f966a14395f4216a18910b1215ddb901ddef073977bbeded","proposedBodySha256":"e12e25753221ac1b0052d013d53da4cdb98ed42d2b4d2bfbbc0cc3c7725c4029","actualBodySha256":"e12e25753221ac1b0052d013d53da4cdb98ed42d2b4d2bfbbc0cc3c7725c4029","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1423","url":"https://linear.app/hash/issue/FE-1423/the-demo-shell-is-safe-to-expose-beyond-localhost","preFetchAt":"2026-08-20T11:01:43.789Z","frozenSourceUpdatedAt":"2026-08-19T16:20:27.185Z","sourceUpdatedAt":"2026-08-19T16:20:27.185Z","sourceTitleSha256":"20bfcf5adb6266e8a21935a7059acb2f2b3a72b6a45328d5a666febdbd322a07","sourceBodySha256":"5a59b5eb9a3ef99c1232b3833764309a3a4523371cc3d50f17b733daf28e9cde","proposedTitleSha256":"3197d4c558ec40f05879656ee185fd8ece13a4af7ecdde2bd829f66f613e0455","proposedBodySha256":"7007aa7a3cf88c13c6d8712425b5ef60a16e648f0339551fcafc3cc79ce6cd68"} -{"event":"result","status":"pass","system":"linear","id":"FE-1423","url":"https://linear.app/hash/issue/FE-1423/require-safe-remote-access-to-the-elicitor-server","postFetchAt":"2026-08-20T11:01:44.347Z","postUpdatedAt":"2026-08-20T11:01:44.049Z","proposedTitleSha256":"3197d4c558ec40f05879656ee185fd8ece13a4af7ecdde2bd829f66f613e0455","actualTitleSha256":"3197d4c558ec40f05879656ee185fd8ece13a4af7ecdde2bd829f66f613e0455","proposedBodySha256":"7007aa7a3cf88c13c6d8712425b5ef60a16e648f0339551fcafc3cc79ce6cd68","actualBodySha256":"7007aa7a3cf88c13c6d8712425b5ef60a16e648f0339551fcafc3cc79ce6cd68","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1424","url":"https://linear.app/hash/issue/FE-1424/the-documentation-protocol-runs-end-to-end-inbox-settled-planning","preFetchAt":"2026-08-20T11:01:44.574Z","frozenSourceUpdatedAt":"2026-08-20T08:02:56.935Z","sourceUpdatedAt":"2026-08-20T08:02:56.935Z","sourceTitleSha256":"4ba41dff52f65d73eb3a91251a5f2f5593dd20f33ee17b935bd4f4fa0a53f03c","sourceBodySha256":"25338da8862bddb99fe1703004fd71da6f90c544df8a3026c5ac2db39bdc8451","proposedTitleSha256":"9d149563aa0cf572f52328468ca36ce12d2d7847126bd31dcd3d4be88323db6d","proposedBodySha256":"de8465621d99b972d89f6685ad71a166b68dec925cea24bf093a1b22938feec3"} -{"event":"result","status":"pass","system":"linear","id":"FE-1424","url":"https://linear.app/hash/issue/FE-1424/complete-the-documentation-protocol","postFetchAt":"2026-08-20T11:01:45.149Z","postUpdatedAt":"2026-08-20T11:01:44.836Z","proposedTitleSha256":"9d149563aa0cf572f52328468ca36ce12d2d7847126bd31dcd3d4be88323db6d","actualTitleSha256":"9d149563aa0cf572f52328468ca36ce12d2d7847126bd31dcd3d4be88323db6d","proposedBodySha256":"de8465621d99b972d89f6685ad71a166b68dec925cea24bf093a1b22938feec3","actualBodySha256":"de8465621d99b972d89f6685ad71a166b68dec925cea24bf093a1b22938feec3","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1431","url":"https://linear.app/hash/issue/FE-1431/plugin-authoring-becomes-declarative-a-domain-is-two-schemas-and-two","preFetchAt":"2026-08-20T11:01:45.394Z","frozenSourceUpdatedAt":"2026-08-19T16:20:27.703Z","sourceUpdatedAt":"2026-08-19T16:20:27.703Z","sourceTitleSha256":"9a7bf9004f09c283ea30033b983064c6ad1e6bf7b90b688aacce0a9b641271f6","sourceBodySha256":"5d4914829c96abbd9cb90a8868e87372ae837b5ac05404f6824b4c7d97e6c381","proposedTitleSha256":"8adc89529c6163c0e84be414ec2bd0c409106370dc848093e1483df64faf5602","proposedBodySha256":"99871599b344a53e1a214bc3c1079e074d02cca22299fd8af4a6b19c0cb4cfef"} -{"event":"result","status":"pass","system":"linear","id":"FE-1431","url":"https://linear.app/hash/issue/FE-1431/define-declarative-plugin-authoring","postFetchAt":"2026-08-20T11:01:46.191Z","postUpdatedAt":"2026-08-20T11:01:45.761Z","proposedTitleSha256":"8adc89529c6163c0e84be414ec2bd0c409106370dc848093e1483df64faf5602","actualTitleSha256":"8adc89529c6163c0e84be414ec2bd0c409106370dc848093e1483df64faf5602","proposedBodySha256":"99871599b344a53e1a214bc3c1079e074d02cca22299fd8af4a6b19c0cb4cfef","actualBodySha256":"99871599b344a53e1a214bc3c1079e074d02cca22299fd8af4a6b19c0cb4cfef","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1432","url":"https://linear.app/hash/issue/FE-1432/the-stacks-open-review-threads-are-adjudicated-fixed-owned-or-refused","preFetchAt":"2026-08-20T11:01:46.454Z","frozenSourceUpdatedAt":"2026-08-20T08:02:56.984Z","sourceUpdatedAt":"2026-08-20T08:02:56.984Z","sourceTitleSha256":"87232791cdfbe95bb69b3bcb846ca1a9b0057306d8b5c372fcd11b3336da9a43","sourceBodySha256":"4280899d06e4ac85fef832eaaaa5f9bb08c388105125b5369808b5c7354d0688","proposedTitleSha256":"da13f8170a751acd91029656dec286426fa2b75ae5b8d0d1b0abcbf657d2c3fa","proposedBodySha256":"f482b84dd957b5b765c4890f0414c8026a1d973c2dd1fbd0736cf905394057fb"} -{"event":"result","status":"pass","system":"linear","id":"FE-1432","url":"https://linear.app/hash/issue/FE-1432/resolve-the-stacks-open-review-threads","postFetchAt":"2026-08-20T11:01:47.149Z","postUpdatedAt":"2026-08-20T11:01:46.819Z","proposedTitleSha256":"da13f8170a751acd91029656dec286426fa2b75ae5b8d0d1b0abcbf657d2c3fa","actualTitleSha256":"da13f8170a751acd91029656dec286426fa2b75ae5b8d0d1b0abcbf657d2c3fa","proposedBodySha256":"f482b84dd957b5b765c4890f0414c8026a1d973c2dd1fbd0736cf905394057fb","actualBodySha256":"f482b84dd957b5b765c4890f0414c8026a1d973c2dd1fbd0736cf905394057fb","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1433","url":"https://linear.app/hash/issue/FE-1433/the-elicitor-serves-demopetrinautorgs-chat-panel-from-a-remote-brunch","preFetchAt":"2026-08-20T11:01:47.393Z","frozenSourceUpdatedAt":"2026-08-20T08:02:57.029Z","sourceUpdatedAt":"2026-08-20T08:02:57.029Z","sourceTitleSha256":"281967069fd5f9b5bfe6d27dabf982004c1f5b8286312ca546001f8466c87c7e","sourceBodySha256":"327f6f24386c6636bd8b52e557f1164e6483a8bd4804355cdca32d1f612d1b1d","proposedTitleSha256":"a0a676a0955943bf0c3ba501347dd8ffdd05dbe8c62bb3660f31fc449280e5e2","proposedBodySha256":"a1c39a62c72181356f54ea088fc917134242eb5cf78b222ffd0a8f86256c8b28"} -{"event":"result","status":"verification-failed","system":"linear","id":"FE-1433","url":"https://linear.app/hash/issue/FE-1433/deliver-the-remote-petrinaut-elicitor-integration","postFetchAt":"2026-08-20T11:01:48.986Z","postUpdatedAt":"2026-08-20T11:01:48.457Z","proposedTitleSha256":"a0a676a0955943bf0c3ba501347dd8ffdd05dbe8c62bb3660f31fc449280e5e2","actualTitleSha256":"a0a676a0955943bf0c3ba501347dd8ffdd05dbe8c62bb3660f31fc449280e5e2","proposedBodySha256":"a1c39a62c72181356f54ea088fc917134242eb5cf78b222ffd0a8f86256c8b28","actualBodySha256":"7ae7b7bace6b2c93833b7c71c5dbf7625d409543b73f451cbeaf52d995e727f0","writeExitCode":0,"writeStderr":""} -{"event":"recovery","status":"restored-to-source","system":"linear","id":"FE-1433","url":"https://linear.app/hash/issue/FE-1433/the-elicitor-serves-demopetrinautorgs-chat-panel-from-a-remote-brunch","recordedAt":"2026-08-20T11:03:34Z","frozenSourceUpdatedAt":"2026-08-20T08:02:57.029Z","restoredUpdatedAt":"2026-08-20T11:03:19.733Z","sourceTitleSha256":"281967069fd5f9b5bfe6d27dabf982004c1f5b8286312ca546001f8466c87c7e","restoredTitleSha256":"281967069fd5f9b5bfe6d27dabf982004c1f5b8286312ca546001f8466c87c7e","sourceBodySha256":"327f6f24386c6636bd8b52e557f1164e6483a8bd4804355cdca32d1f612d1b1d","restoredBodySha256":"327f6f24386c6636bd8b52e557f1164e6483a8bd4804355cdca32d1f612d1b1d","cause":"Linear canonicalized the bare demo.petrinaut.org domain in the approved outer prose; the frozen title and body were restored byte-exactly before adding the two known outer-domain target replacements"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1357","url":"https://linear.app/hash/issue/FE-1357/plan-the-september-elicitation-demo-and-plugin-specification","fetchAt":"2026-08-20T11:06:24.073Z","postUpdatedAt":"2026-08-20T11:00:48.864Z","proposedTitleSha256":"edb56b437c811cec397eeb6940f63cac99f3ebd29bbf371c3378e0184e50bffe","actualTitleSha256":"edb56b437c811cec397eeb6940f63cac99f3ebd29bbf371c3378e0184e50bffe","proposedBodySha256":"af21a19b276419b7f351b3edb6c48bf3f598dd25934f835e971cc7a908d60f98","actualBodySha256":"af21a19b276419b7f351b3edb6c48bf3f598dd25934f835e971cc7a908d60f98"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1358","url":"https://linear.app/hash/issue/FE-1358/survey-petrinaut-for-the-september-integration","fetchAt":"2026-08-20T11:06:24.302Z","postUpdatedAt":"2026-08-20T11:00:50.413Z","proposedTitleSha256":"92a7187c58c6662981c602d001ed28e1088a40f24a05befdaae9b399816cb9c4","actualTitleSha256":"92a7187c58c6662981c602d001ed28e1088a40f24a05befdaae9b399816cb9c4","proposedBodySha256":"08c91e46efd7697eb722cc04373be6680de6591dc6ad4ed9c35f971f6ca55899","actualBodySha256":"08c91e46efd7697eb722cc04373be6680de6591dc6ad4ed9c35f971f6ca55899"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1359","url":"https://linear.app/hash/issue/FE-1359/decide-whether-voice-changes-the-elicitor-architecture","fetchAt":"2026-08-20T11:06:24.593Z","postUpdatedAt":"2026-08-20T11:00:51.481Z","proposedTitleSha256":"24fe52139a5192afa3a13f0264dd052c25b64dd721942d2e3bfd24b1f0d4137c","actualTitleSha256":"24fe52139a5192afa3a13f0264dd052c25b64dd721942d2e3bfd24b1f0d4137c","proposedBodySha256":"5229603b3d41374e36d31d761c83528f7ba5291963ac84c843d60a138d56652b","actualBodySha256":"5229603b3d41374e36d31d761c83528f7ba5291963ac84c843d60a138d56652b"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1360","url":"https://linear.app/hash/issue/FE-1360/derive-elicitation-guidance-from-published-research","fetchAt":"2026-08-20T11:06:24.864Z","postUpdatedAt":"2026-08-20T11:00:53.056Z","proposedTitleSha256":"7e705b33a3fc5c880137fa49b8805afc4d1d547dd75063206fbb9c64d867c97d","actualTitleSha256":"7e705b33a3fc5c880137fa49b8805afc4d1d547dd75063206fbb9c64d867c97d","proposedBodySha256":"a6d7709d6fad560785f2c4d8d5d4d93731ff5d6c8e2b16dcf2bc384ac5daae19","actualBodySha256":"a6d7709d6fad560785f2c4d8d5d4d93731ff5d6c8e2b16dcf2bc384ac5daae19"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1361","url":"https://linear.app/hash/issue/FE-1361/measure-the-one-shot-ai-elicitation-baseline","fetchAt":"2026-08-20T11:06:25.100Z","postUpdatedAt":"2026-08-20T11:00:53.931Z","proposedTitleSha256":"af5b3693ff1fe6a3345539ae08530bafa9b2df8a60e5ba708d7833ba963d2a71","actualTitleSha256":"af5b3693ff1fe6a3345539ae08530bafa9b2df8a60e5ba708d7833ba963d2a71","proposedBodySha256":"5bd4de3670bdb15872c5826c6d053fcf0e4bbc17542313a21640b6732ae941e8","actualBodySha256":"5bd4de3670bdb15872c5826c6d053fcf0e4bbc17542313a21640b6732ae941e8"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1362","url":"https://linear.app/hash/issue/FE-1362/decide-the-september-demo-architecture","fetchAt":"2026-08-20T11:06:25.344Z","postUpdatedAt":"2026-08-20T11:00:54.835Z","proposedTitleSha256":"703de24bb88a4bc6802a75b8dca0e0c7d4a09fb93f72aa8c532d8c5853326959","actualTitleSha256":"703de24bb88a4bc6802a75b8dca0e0c7d4a09fb93f72aa8c532d8c5853326959","proposedBodySha256":"9576d5d00ff1e5cc3e8d8ee8d61c762e297bb63889aaea2469a7a778418d9993","actualBodySha256":"9576d5d00ff1e5cc3e8d8ee8d61c762e297bb63889aaea2469a7a778418d9993"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1363","url":"https://linear.app/hash/issue/FE-1363/choose-the-demo-use-case-and-modelling-criteria","fetchAt":"2026-08-20T11:06:25.600Z","postUpdatedAt":"2026-08-20T11:00:56.060Z","proposedTitleSha256":"d14ab7a091020ba12c4c7eeacc619c18f3099e57703e8ba5cf68efbe17328b8a","actualTitleSha256":"d14ab7a091020ba12c4c7eeacc619c18f3099e57703e8ba5cf68efbe17328b8a","proposedBodySha256":"4cc749b9cfdeeec3960bba78110fe36874827ca888085991a97f232009e89d97","actualBodySha256":"4cc749b9cfdeeec3960bba78110fe36874827ca888085991a97f232009e89d97"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1364","url":"https://linear.app/hash/issue/FE-1364/define-the-process-model-elicitation-representation","fetchAt":"2026-08-20T11:06:26.025Z","postUpdatedAt":"2026-08-20T11:00:56.995Z","proposedTitleSha256":"7ca4533b4bb0e42d0a310e72c5a9a8aae181115af2ad1ffcc85587f49437a3a1","actualTitleSha256":"7ca4533b4bb0e42d0a310e72c5a9a8aae181115af2ad1ffcc85587f49437a3a1","proposedBodySha256":"454e2287159c78a31d09cbdea9402be4594916e53106026ec42944445d768a24","actualBodySha256":"454e2287159c78a31d09cbdea9402be4594916e53106026ec42944445d768a24"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1366","url":"https://linear.app/hash/issue/FE-1366/document-the-elicitation-harness-architecture","fetchAt":"2026-08-20T11:06:26.291Z","postUpdatedAt":"2026-08-20T11:00:58.023Z","proposedTitleSha256":"ecd6270308cb53c1cddecbd489543f583a9d2850c1bcb29aa919e7fcf0c588dd","actualTitleSha256":"ecd6270308cb53c1cddecbd489543f583a9d2850c1bcb29aa919e7fcf0c588dd","proposedBodySha256":"d07ec0b6e6bfdb01e9dbcc57de35dbfeeae65c80d5c688a0fb4b0f855c05739d","actualBodySha256":"d07ec0b6e6bfdb01e9dbcc57de35dbfeeae65c80d5c688a0fb4b0f855c05739d"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1367","url":"https://linear.app/hash/issue/FE-1367/define-how-the-elicitation-harness-uses-flue","fetchAt":"2026-08-20T11:06:26.735Z","postUpdatedAt":"2026-08-20T11:00:59.003Z","proposedTitleSha256":"56a728873a351ffa5abcb9e165694d1c6a882e43fc29d126ab46598a482465aa","actualTitleSha256":"56a728873a351ffa5abcb9e165694d1c6a882e43fc29d126ab46598a482465aa","proposedBodySha256":"0d564e0427b73c79440b31b09c85a11d83bdada98759c0c0571531e1b7eacdc2","actualBodySha256":"0d564e0427b73c79440b31b09c85a11d83bdada98759c0c0571531e1b7eacdc2"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1368","url":"https://linear.app/hash/issue/FE-1368/assess-zil-lean-as-an-elicitation-subject","fetchAt":"2026-08-20T11:06:26.988Z","postUpdatedAt":"2026-08-20T11:01:00.028Z","proposedTitleSha256":"671c8d7d2ec1d02dd04e6791f04e91d907c514f38ea3e8bbf371757bcd44d183","actualTitleSha256":"671c8d7d2ec1d02dd04e6791f04e91d907c514f38ea3e8bbf371757bcd44d183","proposedBodySha256":"58eac8c7f4da2110cff73432764c20d71576fbedbed167d88a7c0ccaacfe8b70","actualBodySha256":"58eac8c7f4da2110cff73432764c20d71576fbedbed167d88a7c0ccaacfe8b70"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1369","url":"https://linear.app/hash/issue/FE-1369/classify-brunch-exchange-structures-for-reuse","fetchAt":"2026-08-20T11:06:27.233Z","postUpdatedAt":"2026-08-20T11:01:01.421Z","proposedTitleSha256":"ad51b1463946bcbcf00e57b7bffac75180cde408d6db865a9c2bf8349597a4d2","actualTitleSha256":"ad51b1463946bcbcf00e57b7bffac75180cde408d6db865a9c2bf8349597a4d2","proposedBodySha256":"f9acc9c49a910e07ea12eee26f5156578029354e5b695d3b6f679744a4d30d3d","actualBodySha256":"f9acc9c49a910e07ea12eee26f5156578029354e5b695d3b6f679744a4d30d3d"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1370","url":"https://linear.app/hash/issue/FE-1370/define-the-harness-and-plugin-responsibilities","fetchAt":"2026-08-20T11:06:27.489Z","postUpdatedAt":"2026-08-20T11:01:02.574Z","proposedTitleSha256":"fff1e6cd68dee8290e99e3b5a27ef3e63a5a20a6f634b058724ca33d08cb32aa","actualTitleSha256":"fff1e6cd68dee8290e99e3b5a27ef3e63a5a20a6f634b058724ca33d08cb32aa","proposedBodySha256":"586900fec359fdea19b40355ce70ada6f4dc6cc1a4b318a5266fd04925e9330b","actualBodySha256":"586900fec359fdea19b40355ce70ada6f4dc6cc1a4b318a5266fd04925e9330b"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1371","url":"https://linear.app/hash/issue/FE-1371/define-structured-questions-within-conversation","fetchAt":"2026-08-20T11:06:27.735Z","postUpdatedAt":"2026-08-20T11:01:04.082Z","proposedTitleSha256":"6df3270da2d36b2ce6b9f28706d1d60830de04f2a7284ce91e04bab6c5440116","actualTitleSha256":"6df3270da2d36b2ce6b9f28706d1d60830de04f2a7284ce91e04bab6c5440116","proposedBodySha256":"8e34e41697c46c46a81ea4c5ab48edcb49de55973b2b11732bcf451b8191a17f","actualBodySha256":"8e34e41697c46c46a81ea4c5ab48edcb49de55973b2b11732bcf451b8191a17f"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1372","url":"https://linear.app/hash/issue/FE-1372/choose-how-the-elicitation-harness-ships","fetchAt":"2026-08-20T11:06:27.980Z","postUpdatedAt":"2026-08-20T11:01:05.224Z","proposedTitleSha256":"f62fc5f62252c5cd20b94fe9527ab32111d710f20b95513c430b0b89e389a89d","actualTitleSha256":"f62fc5f62252c5cd20b94fe9527ab32111d710f20b95513c430b0b89e389a89d","proposedBodySha256":"4ceed9ff828af36baeb0d9fe3be38f70f48b89310c8f6eb86afb98ba9ea34d1e","actualBodySha256":"4ceed9ff828af36baeb0d9fe3be38f70f48b89310c8f6eb86afb98ba9ea34d1e"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1373","url":"https://linear.app/hash/issue/FE-1373/choose-the-initial-elicitation-subjects","fetchAt":"2026-08-20T11:06:28.226Z","postUpdatedAt":"2026-08-20T11:01:06.262Z","proposedTitleSha256":"49526b09da7cea4f956b0fc97da3a537287b6e1dc0a5599a0dc744273bbd4057","actualTitleSha256":"49526b09da7cea4f956b0fc97da3a537287b6e1dc0a5599a0dc744273bbd4057","proposedBodySha256":"3427cd321d38c72b7abb00985cdc8eb1d4de70b2b1cf4be1bad6537edf468d03","actualBodySha256":"3427cd321d38c72b7abb00985cdc8eb1d4de70b2b1cf4be1bad6537edf468d03"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1374","url":"https://linear.app/hash/issue/FE-1374/assemble-the-elicitation-harness-specification","fetchAt":"2026-08-20T11:06:28.478Z","postUpdatedAt":"2026-08-20T11:01:07.086Z","proposedTitleSha256":"a8d6e8af11c31f8e0b937ba43b6d758e79988d751f29a1222bb5097e33ce4f78","actualTitleSha256":"a8d6e8af11c31f8e0b937ba43b6d758e79988d751f29a1222bb5097e33ce4f78","proposedBodySha256":"cb0436794a3e2c782938939475a1fa2a0b68e3969b1e1ff03eb8fa4306d4abc0","actualBodySha256":"cb0436794a3e2c782938939475a1fa2a0b68e3969b1e1ff03eb8fa4306d4abc0"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1375","url":"https://linear.app/hash/issue/FE-1375/align-the-assurance-subject-with-verification-practice","fetchAt":"2026-08-20T11:06:28.716Z","postUpdatedAt":"2026-08-20T11:01:08.093Z","proposedTitleSha256":"1014535547f1f5612dda184fea1218227d4b4674f4bf9b18fa3c9d9841c9740a","actualTitleSha256":"1014535547f1f5612dda184fea1218227d4b4674f4bf9b18fa3c9d9841c9740a","proposedBodySha256":"f1e803bca9e75caf52ea4346c7d8fe2c859523a1de616367c56b1dc58da2d82c","actualBodySha256":"f1e803bca9e75caf52ea4346c7d8fe2c859523a1de616367c56b1dc58da2d82c"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1376","url":"https://linear.app/hash/issue/FE-1376/prove-a-flue-question-round-trip","fetchAt":"2026-08-20T11:06:28.969Z","postUpdatedAt":"2026-08-20T11:01:09.911Z","proposedTitleSha256":"780a9ff8453c76d6b8fd253a634ef6049f1ef97808e74451361e164bac65ab98","actualTitleSha256":"780a9ff8453c76d6b8fd253a634ef6049f1ef97808e74451361e164bac65ab98","proposedBodySha256":"3a752cf0d3362718f9364083a2469b501e3d5037dbfd8fde747216b4e53e36ec","actualBodySha256":"3a752cf0d3362718f9364083a2469b501e3d5037dbfd8fde747216b4e53e36ec"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1377","url":"https://linear.app/hash/issue/FE-1377/prove-repeatable-conversation-capture","fetchAt":"2026-08-20T11:06:29.207Z","postUpdatedAt":"2026-08-20T11:01:10.954Z","proposedTitleSha256":"5679461ba36f1b7ddc1d98c64e7f54b59038d3e217d0722c2953b1f2044b99bd","actualTitleSha256":"5679461ba36f1b7ddc1d98c64e7f54b59038d3e217d0722c2953b1f2044b99bd","proposedBodySha256":"fac78c76da54522de3bfa0777b4f048279581c51300c3fc74bcf80548c0d8cba","actualBodySha256":"fac78c76da54522de3bfa0777b4f048279581c51300c3fc74bcf80548c0d8cba"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1378","url":"https://linear.app/hash/issue/FE-1378/define-durable-state-across-elicitation-sessions","fetchAt":"2026-08-20T11:06:29.446Z","postUpdatedAt":"2026-08-20T11:01:11.912Z","proposedTitleSha256":"442998135a28eb368d03b58a5ec32ff9fbd4ee0743b7afc16030e3dac70494cf","actualTitleSha256":"442998135a28eb368d03b58a5ec32ff9fbd4ee0743b7afc16030e3dac70494cf","proposedBodySha256":"77648b3f761ba84c2837ad23b418104e13c2182772006d3e175f63b0066cf065","actualBodySha256":"77648b3f761ba84c2837ad23b418104e13c2182772006d3e175f63b0066cf065"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1379","url":"https://linear.app/hash/issue/FE-1379/prove-the-remaining-flue-capabilities","fetchAt":"2026-08-20T11:06:29.720Z","postUpdatedAt":"2026-08-20T11:01:12.831Z","proposedTitleSha256":"d11a2dcabcaa56f89789e629bc0d41d5d4756ee3702e7cff79bea4e6f2074ad5","actualTitleSha256":"d11a2dcabcaa56f89789e629bc0d41d5d4756ee3702e7cff79bea4e6f2074ad5","proposedBodySha256":"3b61f9e6a64f171a18eb0482da780e41dab67f44bfd7c7568acca05f5b39c85c","actualBodySha256":"3b61f9e6a64f171a18eb0482da780e41dab67f44bfd7c7568acca05f5b39c85c"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1382","url":"https://linear.app/hash/issue/FE-1382/compile-the-truck-fleet-source-dossier","fetchAt":"2026-08-20T11:06:30.527Z","postUpdatedAt":"2026-08-20T11:01:13.930Z","proposedTitleSha256":"cca762a296ff0a645b3398b349a1aba34b009e2a72520cd6ca50a1995ecbf782","actualTitleSha256":"cca762a296ff0a645b3398b349a1aba34b009e2a72520cd6ca50a1995ecbf782","proposedBodySha256":"dcbee28f52b04684d16de429518666c033f3d5dc2ace992d55b5fe49f5180e7f","actualBodySha256":"dcbee28f52b04684d16de429518666c033f3d5dc2ace992d55b5fe49f5180e7f"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1383","url":"https://linear.app/hash/issue/FE-1383/build-the-first-complete-elicitation-interview","fetchAt":"2026-08-20T11:06:30.772Z","postUpdatedAt":"2026-08-20T11:01:14.845Z","proposedTitleSha256":"e16c710e0bf421f6afd4e73b0924ece8d021e65511fe57aef680f3f7854d0577","actualTitleSha256":"e16c710e0bf421f6afd4e73b0924ece8d021e65511fe57aef680f3f7854d0577","proposedBodySha256":"3e7b2d47ea437880f995a53d223fb9380873e02ae5adac83ceafd9d38a6f55cb","actualBodySha256":"3e7b2d47ea437880f995a53d223fb9380873e02ae5adac83ceafd9d38a6f55cb"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1384","url":"https://linear.app/hash/issue/FE-1384/generate-replay-tests-for-the-harness-rules","fetchAt":"2026-08-20T11:06:31.018Z","postUpdatedAt":"2026-08-20T11:01:15.906Z","proposedTitleSha256":"d578f097f2c89e3b8d52f319f960726bafe7bdf786a8ee7cd7613c52cc1708ed","actualTitleSha256":"d578f097f2c89e3b8d52f319f960726bafe7bdf786a8ee7cd7613c52cc1708ed","proposedBodySha256":"bf5b66bf1a18287a64f583e0f7df1761c1d3e13278ff349998410d5aa425156c","actualBodySha256":"bf5b66bf1a18287a64f583e0f7df1761c1d3e13278ff349998410d5aa425156c"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1385","url":"https://linear.app/hash/issue/FE-1385/expand-the-dev-app-into-a-target-gallery-and-diagnostic-view","fetchAt":"2026-08-20T11:06:31.254Z","postUpdatedAt":"2026-08-20T11:01:16.969Z","proposedTitleSha256":"735ae093eeaea26baacee13aa32bf1a2903b9ea666421c903d460c1987a63543","actualTitleSha256":"735ae093eeaea26baacee13aa32bf1a2903b9ea666421c903d460c1987a63543","proposedBodySha256":"9a9849fe884a4047d00b0d673f085c3ca401643df2647eb3a9da07e1530991c6","actualBodySha256":"9a9849fe884a4047d00b0d673f085c3ca401643df2647eb3a9da07e1530991c6"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1386","url":"https://linear.app/hash/issue/FE-1386/test-durable-history-across-transcript-compaction","fetchAt":"2026-08-20T11:06:31.490Z","postUpdatedAt":"2026-08-20T11:01:17.820Z","proposedTitleSha256":"828d7efb7c4bde98357f8ea410087cc82d009524241bc6f8a4dbab46d701c2a0","actualTitleSha256":"828d7efb7c4bde98357f8ea410087cc82d009524241bc6f8a4dbab46d701c2a0","proposedBodySha256":"6714044cb3c8c5e5f5a511ec8339ec15f8d639b78814cd2f430d068265c4443f","actualBodySha256":"6714044cb3c8c5e5f5a511ec8339ec15f8d639b78814cd2f430d068265c4443f"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1387","url":"https://linear.app/hash/issue/FE-1387/choose-a-second-target-and-stabilize-the-plugin-interface","fetchAt":"2026-08-20T11:06:31.720Z","postUpdatedAt":"2026-08-20T11:01:18.698Z","proposedTitleSha256":"37bf181908327ddd86b0e09bc446b5eb02383a441693d6a9fa604d7455756205","actualTitleSha256":"37bf181908327ddd86b0e09bc446b5eb02383a441693d6a9fa604d7455756205","proposedBodySha256":"e02aff72a2640dbe03ff1027ec7ec203c67be06796834148e5ff7b577cbfd66a","actualBodySha256":"e02aff72a2640dbe03ff1027ec7ec203c67be06796834148e5ff7b577cbfd66a"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1388","url":"https://linear.app/hash/issue/FE-1388/create-the-bun-workspace-and-enforce-dependency-boundaries","fetchAt":"2026-08-20T11:06:31.970Z","postUpdatedAt":"2026-08-20T11:01:19.623Z","proposedTitleSha256":"cba702bfe08fbfe6c118f53f94058b89f4e279b1e65f7eaec8f8a764dcc99249","actualTitleSha256":"cba702bfe08fbfe6c118f53f94058b89f4e279b1e65f7eaec8f8a764dcc99249","proposedBodySha256":"09f30e8f38b786bb7d9645dec9b45137931774e2dbe8d033acabf410fac84a7b","actualBodySha256":"09f30e8f38b786bb7d9645dec9b45137931774e2dbe8d033acabf410fac84a7b"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1389","url":"https://linear.app/hash/issue/FE-1389/implement-the-first-suspended-free-text-question","fetchAt":"2026-08-20T11:06:32.221Z","postUpdatedAt":"2026-08-20T11:01:20.583Z","proposedTitleSha256":"671d58f11fdffe9a2aef0c9355b44e9b1e5c66d0c25504369b6a25de54e0a6b3","actualTitleSha256":"671d58f11fdffe9a2aef0c9355b44e9b1e5c66d0c25504369b6a25de54e0a6b3","proposedBodySha256":"61d11a095b9ca88dfcd274a2d50e1d1e337713abfce81dc75cf29cbd1dad3c80","actualBodySha256":"61d11a095b9ca88dfcd274a2d50e1d1e337713abfce81dc75cf29cbd1dad3c80"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1390","url":"https://linear.app/hash/issue/FE-1390/implement-capture-history-and-local-persistence","fetchAt":"2026-08-20T11:06:33.054Z","postUpdatedAt":"2026-08-20T11:01:21.541Z","proposedTitleSha256":"4f9a2a6a52ee76fc88cf973ed854c90d5a3425c1b81d444fdee080bb2736c392","actualTitleSha256":"4f9a2a6a52ee76fc88cf973ed854c90d5a3425c1b81d444fdee080bb2736c392","proposedBodySha256":"6cf8d9572068580ce883faeed54d1ef0ac20d8c60666d32d810c1ff45b56a0ad","actualBodySha256":"6cf8d9572068580ce883faeed54d1ef0ac20d8c60666d32d810c1ff45b56a0ad"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1391","url":"https://linear.app/hash/issue/FE-1391/resolve-evidence-quotes-to-durable-conversation-entries","fetchAt":"2026-08-20T11:06:33.306Z","postUpdatedAt":"2026-08-20T11:01:22.808Z","proposedTitleSha256":"cc41527c73a8817b0d2f23773f40b6d72de92f3d91a5a2c6b5259a68861de1c8","actualTitleSha256":"cc41527c73a8817b0d2f23773f40b6d72de92f3d91a5a2c6b5259a68861de1c8","proposedBodySha256":"a10cd59d9b58e0903965106cfc42cace3aa01a8366c414a76196c867c46c5683","actualBodySha256":"a10cd59d9b58e0903965106cfc42cace3aa01a8366c414a76196c867c46c5683"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1392","url":"https://linear.app/hash/issue/FE-1392/capture-settled-conversation-statements-safely","fetchAt":"2026-08-20T11:06:33.566Z","postUpdatedAt":"2026-08-20T11:01:23.655Z","proposedTitleSha256":"e5bc000d22834d39bfc4daefdec98c8bca2cb0883dc5d4e2a4e44b0f47e7d8a6","actualTitleSha256":"e5bc000d22834d39bfc4daefdec98c8bca2cb0883dc5d4e2a4e44b0f47e7d8a6","proposedBodySha256":"71902c740537f89d9251bcdab39d0569d59e2445e9198305e578100b9eafe77d","actualBodySha256":"71902c740537f89d9251bcdab39d0569d59e2445e9198305e578100b9eafe77d"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1393","url":"https://linear.app/hash/issue/FE-1393/produce-the-first-gherkin-artifact-through-the-plugin-sdk","fetchAt":"2026-08-20T11:06:33.809Z","postUpdatedAt":"2026-08-20T11:01:24.693Z","proposedTitleSha256":"a7ae805905d5b1a216a522c1492669e21aca11ab15eb0892cbcd4a44e84d6dec","actualTitleSha256":"a7ae805905d5b1a216a522c1492669e21aca11ab15eb0892cbcd4a44e84d6dec","proposedBodySha256":"9cc7f0cedf11c039e9e6c55f9904fd35340c2cb329d275cdefa77e022695fffe","actualBodySha256":"9cc7f0cedf11c039e9e6c55f9904fd35340c2cb329d275cdefa77e022695fffe"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1394","url":"https://linear.app/hash/issue/FE-1394/preserve-conflicts-until-the-user-resolves-them","fetchAt":"2026-08-20T11:06:34.038Z","postUpdatedAt":"2026-08-20T11:01:25.600Z","proposedTitleSha256":"8e8cc690f434c0e6b827fec9acd5887087cd5e638347b55a486eab0b5139a497","actualTitleSha256":"8e8cc690f434c0e6b827fec9acd5887087cd5e638347b55a486eab0b5139a497","proposedBodySha256":"3a738a9370e3a1698e0c0d0fd6c966d15e7635f9be498470ac8c8ef37e8297b1","actualBodySha256":"3a738a9370e3a1698e0c0d0fd6c966d15e7635f9be498470ac8c8ef37e8297b1"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1395","url":"https://linear.app/hash/issue/FE-1395/add-choices-questionnaires-and-explicit-absence-replies","fetchAt":"2026-08-20T11:06:34.342Z","postUpdatedAt":"2026-08-20T11:01:26.966Z","proposedTitleSha256":"9142527acadbf1d1d8d0a777a129404cc15df6c4471aacc2fe91601b2c470397","actualTitleSha256":"9142527acadbf1d1d8d0a777a129404cc15df6c4471aacc2fe91601b2c470397","proposedBodySha256":"325fb0866d84fc3386d7dadd18e1fc5140e0f02bc64508826dada5a514c79318","actualBodySha256":"325fb0866d84fc3386d7dadd18e1fc5140e0f02bc64508826dada5a514c79318"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1396","url":"https://linear.app/hash/issue/FE-1396/restore-interview-context-after-resume-and-restart","fetchAt":"2026-08-20T11:06:34.592Z","postUpdatedAt":"2026-08-20T11:01:28.090Z","proposedTitleSha256":"66b2c12e3036f132c63ee071cd1e98f126bccccf9223062effe4e2f9c3658084","actualTitleSha256":"66b2c12e3036f132c63ee071cd1e98f126bccccf9223062effe4e2f9c3658084","proposedBodySha256":"60a1aa6eb6aacbb63940637435b2ecd8dcbc8d38ab4e8057976301ba89665b59","actualBodySha256":"60a1aa6eb6aacbb63940637435b2ecd8dcbc8d38ab4e8057976301ba89665b59"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1397","url":"https://linear.app/hash/issue/FE-1397/validate-the-generic-ir-against-worked-plugin-payloads","fetchAt":"2026-08-20T11:06:34.821Z","postUpdatedAt":"2026-08-20T11:01:28.974Z","proposedTitleSha256":"f07438c59634073e97ad1225c8b971ececa39d27ebd19aa27958da8e4eb11bf6","actualTitleSha256":"f07438c59634073e97ad1225c8b971ececa39d27ebd19aa27958da8e4eb11bf6","proposedBodySha256":"728d19cef7d300fa63be05be9ba870290863be42072f08e865cee254f2ec9cbf","actualBodySha256":"728d19cef7d300fa63be05be9ba870290863be42072f08e865cee254f2ec9cbf"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1399","url":"https://linear.app/hash/issue/FE-1399/fix-verified-silent-failures-in-ci-and-the-dev-app","fetchAt":"2026-08-20T11:06:35.233Z","postUpdatedAt":"2026-08-20T11:01:29.815Z","proposedTitleSha256":"a376b90e615f99d8f593ca82d7aebcf143ae49697119f3126cf511dea4242c64","actualTitleSha256":"a376b90e615f99d8f593ca82d7aebcf143ae49697119f3126cf511dea4242c64","proposedBodySha256":"93f520f9c2e08c999d5105fdd07a770dbe8a20b6f8bea6c1995067bcd44cf221","actualBodySha256":"93f520f9c2e08c999d5105fdd07a770dbe8a20b6f8bea6c1995067bcd44cf221"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1400","url":"https://linear.app/hash/issue/FE-1400/strengthen-verification-dev-storage-and-the-baseline-runner","fetchAt":"2026-08-20T11:06:35.475Z","postUpdatedAt":"2026-08-20T11:01:30.774Z","proposedTitleSha256":"26fb6f297b802aab16e250ef0aa30c16815e5b2f894b7c6dbc8666e569344e65","actualTitleSha256":"26fb6f297b802aab16e250ef0aa30c16815e5b2f894b7c6dbc8666e569344e65","proposedBodySha256":"096ccde21fd800d9c0cbb751b580ad160b998666313b43702030a78056471204","actualBodySha256":"096ccde21fd800d9c0cbb751b580ad160b998666313b43702030a78056471204"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1401","url":"https://linear.app/hash/issue/FE-1401/resolve-the-stack-legibility-follow-ups","fetchAt":"2026-08-20T11:06:35.936Z","postUpdatedAt":"2026-08-20T11:01:33.059Z","proposedTitleSha256":"b8874647b7f094aff4db44c720b9053d7f2c155ea641aea541bcc35728b18b60","actualTitleSha256":"b8874647b7f094aff4db44c720b9053d7f2c155ea641aea541bcc35728b18b60","proposedBodySha256":"9e51a20089190e2e34e70e0a4b6faa35b73c8818a11eb8dce1fcd5402c652a20","actualBodySha256":"9e51a20089190e2e34e70e0a4b6faa35b73c8818a11eb8dce1fcd5402c652a20"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1402","url":"https://linear.app/hash/issue/FE-1402/define-and-rehearse-the-elicitation-completion-contract","fetchAt":"2026-08-20T11:06:36.202Z","postUpdatedAt":"2026-08-20T11:01:35.015Z","proposedTitleSha256":"91d4dd660323d85121bb7cc935e581934239bac3ec00303ec944077276a33f34","actualTitleSha256":"91d4dd660323d85121bb7cc935e581934239bac3ec00303ec944077276a33f34","proposedBodySha256":"b2abd827fdba53895fdccc6d227d8981e7615a103564a8b5955108faf44a49fa","actualBodySha256":"b2abd827fdba53895fdccc6d227d8981e7615a103564a8b5955108faf44a49fa"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1403","url":"https://linear.app/hash/issue/FE-1403/assemble-and-test-the-cps-interview-guidance","fetchAt":"2026-08-20T11:06:36.436Z","postUpdatedAt":"2026-08-20T11:01:35.971Z","proposedTitleSha256":"c2dd05ba5210b2e540bc4f337998070f3341f1bdaf3d14bdfdeca4526924de72","actualTitleSha256":"c2dd05ba5210b2e540bc4f337998070f3341f1bdaf3d14bdfdeca4526924de72","proposedBodySha256":"d4f1804674241c2b523f122d3a510d4730eabaa564dc2a589e63886e48b94240","actualBodySha256":"d4f1804674241c2b523f122d3a510d4730eabaa564dc2a589e63886e48b94240"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1404","url":"https://linear.app/hash/issue/FE-1404/run-the-third-baseline-with-completion-and-interview-guidance","fetchAt":"2026-08-20T11:06:36.665Z","postUpdatedAt":"2026-08-20T11:01:37.001Z","proposedTitleSha256":"cbed4c76e448b5ccb190c628bbd109a0c35dbfabe879716ff24ebb8c061e732b","actualTitleSha256":"cbed4c76e448b5ccb190c628bbd109a0c35dbfabe879716ff24ebb8c061e732b","proposedBodySha256":"0f61b1baed5eb374bd7366fc51344fd21a65f900ce7a4fc273241ef919819b9d","actualBodySha256":"0f61b1baed5eb374bd7366fc51344fd21a65f900ce7a4fc273241ef919819b9d"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1405","url":"https://linear.app/hash/issue/FE-1405/draft-and-test-the-cps-payload-schemas","fetchAt":"2026-08-20T11:06:36.895Z","postUpdatedAt":"2026-08-20T11:01:37.874Z","proposedTitleSha256":"67364fd5e5963238cb0aea5624374d9a6c0d81adeb00f213467ca08100955034","actualTitleSha256":"67364fd5e5963238cb0aea5624374d9a6c0d81adeb00f213467ca08100955034","proposedBodySha256":"cf4bbd9f6c5f81cac7a38ae614a1a87011efdf7ca19020c95e7e14f177fc6517","actualBodySha256":"cf4bbd9f6c5f81cac7a38ae614a1a87011efdf7ca19020c95e7e14f177fc6517"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1406","url":"https://linear.app/hash/issue/FE-1406/design-reusable-elicitation-strategies","fetchAt":"2026-08-20T11:06:37.136Z","postUpdatedAt":"2026-08-20T11:01:38.757Z","proposedTitleSha256":"9e9eb8ab449ad16c522398efabb98d5eff616e2e1e7cd8c81b99ff2b54d86ba2","actualTitleSha256":"9e9eb8ab449ad16c522398efabb98d5eff616e2e1e7cd8c81b99ff2b54d86ba2","proposedBodySha256":"25af8c35ff021b508612a58d96316c4ec10415e9abed4f2e32e0ccb1fe4100c4","actualBodySha256":"25af8c35ff021b508612a58d96316c4ec10415e9abed4f2e32e0ccb1fe4100c4"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1407","url":"https://linear.app/hash/issue/FE-1407/catalogue-elicitor-failures-that-published-measures-miss","fetchAt":"2026-08-20T11:06:37.376Z","postUpdatedAt":"2026-08-20T11:01:39.687Z","proposedTitleSha256":"168b66b77c82a14116e0f84ad35b26a630d9d70155415c6117cac5b8cabcf3de","actualTitleSha256":"168b66b77c82a14116e0f84ad35b26a630d9d70155415c6117cac5b8cabcf3de","proposedBodySha256":"58bff2297ba46cb4a223217bb178784550174cb0d72b951d0a4b631e97bffe9f","actualBodySha256":"58bff2297ba46cb4a223217bb178784550174cb0d72b951d0a4b631e97bffe9f"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1419","url":"https://linear.app/hash/issue/FE-1419/align-capture-store-rules-and-verification-claims","fetchAt":"2026-08-20T11:06:37.620Z","postUpdatedAt":"2026-08-20T11:01:40.563Z","proposedTitleSha256":"bee550820a6ba0ba0d693aa7f58f0ade1b3deeb5c0871290f3ebbd71a16fe342","actualTitleSha256":"bee550820a6ba0ba0d693aa7f58f0ade1b3deeb5c0871290f3ebbd71a16fe342","proposedBodySha256":"cb43c18d19933fb4d41b43b6ba6ebf2a0bbb2ee0bf57a1837ea7cb6045bccc8e","actualBodySha256":"cb43c18d19933fb4d41b43b6ba6ebf2a0bbb2ee0bf57a1837ea7cb6045bccc8e"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1420","url":"https://linear.app/hash/issue/FE-1420/make-affordance-handling-safe-under-retries-and-abandonment","fetchAt":"2026-08-20T11:06:37.870Z","postUpdatedAt":"2026-08-20T11:01:41.516Z","proposedTitleSha256":"3520d374d5f0b363f505c96c37efeeed65db0724230dccf71150240faec331c5","actualTitleSha256":"3520d374d5f0b363f505c96c37efeeed65db0724230dccf71150240faec331c5","proposedBodySha256":"316d82c70192905999f13c7cb3a972faeca36f024e8e87de11ddd7ed3822536f","actualBodySha256":"316d82c70192905999f13c7cb3a972faeca36f024e8e87de11ddd7ed3822536f"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1422","url":"https://linear.app/hash/issue/FE-1422/move-the-portable-ask-protocol-into-core","fetchAt":"2026-08-20T11:06:38.114Z","postUpdatedAt":"2026-08-20T11:01:43.248Z","proposedTitleSha256":"21ba840643993170f966a14395f4216a18910b1215ddb901ddef073977bbeded","actualTitleSha256":"21ba840643993170f966a14395f4216a18910b1215ddb901ddef073977bbeded","proposedBodySha256":"e12e25753221ac1b0052d013d53da4cdb98ed42d2b4d2bfbbc0cc3c7725c4029","actualBodySha256":"e12e25753221ac1b0052d013d53da4cdb98ed42d2b4d2bfbbc0cc3c7725c4029"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1423","url":"https://linear.app/hash/issue/FE-1423/require-safe-remote-access-to-the-elicitor-server","fetchAt":"2026-08-20T11:06:38.366Z","postUpdatedAt":"2026-08-20T11:01:44.049Z","proposedTitleSha256":"3197d4c558ec40f05879656ee185fd8ece13a4af7ecdde2bd829f66f613e0455","actualTitleSha256":"3197d4c558ec40f05879656ee185fd8ece13a4af7ecdde2bd829f66f613e0455","proposedBodySha256":"7007aa7a3cf88c13c6d8712425b5ef60a16e648f0339551fcafc3cc79ce6cd68","actualBodySha256":"7007aa7a3cf88c13c6d8712425b5ef60a16e648f0339551fcafc3cc79ce6cd68"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1424","url":"https://linear.app/hash/issue/FE-1424/complete-the-documentation-protocol","fetchAt":"2026-08-20T11:06:38.667Z","postUpdatedAt":"2026-08-20T11:01:44.836Z","proposedTitleSha256":"9d149563aa0cf572f52328468ca36ce12d2d7847126bd31dcd3d4be88323db6d","actualTitleSha256":"9d149563aa0cf572f52328468ca36ce12d2d7847126bd31dcd3d4be88323db6d","proposedBodySha256":"de8465621d99b972d89f6685ad71a166b68dec925cea24bf093a1b22938feec3","actualBodySha256":"de8465621d99b972d89f6685ad71a166b68dec925cea24bf093a1b22938feec3"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1431","url":"https://linear.app/hash/issue/FE-1431/define-declarative-plugin-authoring","fetchAt":"2026-08-20T11:06:38.919Z","postUpdatedAt":"2026-08-20T11:01:45.761Z","proposedTitleSha256":"8adc89529c6163c0e84be414ec2bd0c409106370dc848093e1483df64faf5602","actualTitleSha256":"8adc89529c6163c0e84be414ec2bd0c409106370dc848093e1483df64faf5602","proposedBodySha256":"99871599b344a53e1a214bc3c1079e074d02cca22299fd8af4a6b19c0cb4cfef","actualBodySha256":"99871599b344a53e1a214bc3c1079e074d02cca22299fd8af4a6b19c0cb4cfef"} -{"event":"resume-check","status":"pass","system":"linear","id":"FE-1432","url":"https://linear.app/hash/issue/FE-1432/resolve-the-stacks-open-review-threads","fetchAt":"2026-08-20T11:06:39.161Z","postUpdatedAt":"2026-08-20T11:01:46.819Z","proposedTitleSha256":"da13f8170a751acd91029656dec286426fa2b75ae5b8d0d1b0abcbf657d2c3fa","actualTitleSha256":"da13f8170a751acd91029656dec286426fa2b75ae5b8d0d1b0abcbf657d2c3fa","proposedBodySha256":"f482b84dd957b5b765c4890f0414c8026a1d973c2dd1fbd0736cf905394057fb","actualBodySha256":"f482b84dd957b5b765c4890f0414c8026a1d973c2dd1fbd0736cf905394057fb"} -{"event":"attempt","system":"linear","id":"FE-1433","url":"https://linear.app/hash/issue/FE-1433/the-elicitor-serves-demopetrinautorgs-chat-panel-from-a-remote-brunch","preFetchAt":"2026-08-20T11:06:39.904Z","frozenSourceUpdatedAt":"2026-08-20T08:02:57.029Z","sourceUpdatedAt":"2026-08-20T11:03:19.733Z","sourceTitleSha256":"281967069fd5f9b5bfe6d27dabf982004c1f5b8286312ca546001f8466c87c7e","sourceBodySha256":"327f6f24386c6636bd8b52e557f1164e6483a8bd4804355cdca32d1f612d1b1d","proposedTitleSha256":"a0a676a0955943bf0c3ba501347dd8ffdd05dbe8c62bb3660f31fc449280e5e2","proposedBodySha256":"7ae7b7bace6b2c93833b7c71c5dbf7625d409543b73f451cbeaf52d995e727f0"} -{"event":"result","status":"pass","system":"linear","id":"FE-1433","url":"https://linear.app/hash/issue/FE-1433/deliver-the-remote-petrinaut-elicitor-integration","postFetchAt":"2026-08-20T11:06:40.505Z","postUpdatedAt":"2026-08-20T11:06:40.179Z","proposedTitleSha256":"a0a676a0955943bf0c3ba501347dd8ffdd05dbe8c62bb3660f31fc449280e5e2","actualTitleSha256":"a0a676a0955943bf0c3ba501347dd8ffdd05dbe8c62bb3660f31fc449280e5e2","proposedBodySha256":"7ae7b7bace6b2c93833b7c71c5dbf7625d409543b73f451cbeaf52d995e727f0","actualBodySha256":"7ae7b7bace6b2c93833b7c71c5dbf7625d409543b73f451cbeaf52d995e727f0","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1434","url":"https://linear.app/hash/issue/FE-1434/spike-does-flue-turn-suspension-carry-batched-client-tool-round-trips","preFetchAt":"2026-08-20T11:06:40.733Z","frozenSourceUpdatedAt":"2026-08-20T08:02:57.068Z","sourceUpdatedAt":"2026-08-20T08:02:57.068Z","sourceTitleSha256":"c10af0cac7faccaeac3f161ec242d94fc92549f164b8e885a6305159115d58c8","sourceBodySha256":"fac188333116b05f6764814ccb6ed97bd003e182bc45132c2d39c932440b5bb2","proposedTitleSha256":"5e494b4c27b36bb0ae657fde9e024ae844cd63d2318cc373af2544753be56168","proposedBodySha256":"f997a348580b20b35f3c843b022988b459306c159d91a012fce1170a2ae80a6f"} -{"event":"result","status":"pass","system":"linear","id":"FE-1434","url":"https://linear.app/hash/issue/FE-1434/test-whether-flue-resumes-batched-client-tool-results","postFetchAt":"2026-08-20T11:06:41.287Z","postUpdatedAt":"2026-08-20T11:06:40.981Z","proposedTitleSha256":"5e494b4c27b36bb0ae657fde9e024ae844cd63d2318cc373af2544753be56168","actualTitleSha256":"5e494b4c27b36bb0ae657fde9e024ae844cd63d2318cc373af2544753be56168","proposedBodySha256":"f997a348580b20b35f3c843b022988b459306c159d91a012fce1170a2ae80a6f","actualBodySha256":"f997a348580b20b35f3c843b022988b459306c159d91a012fce1170a2ae80a6f","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1435","url":"https://linear.app/hash/issue/FE-1435/spike-does-a-harness-driven-stream-drive-petrinauts-real-chat-panel","preFetchAt":"2026-08-20T11:06:41.580Z","frozenSourceUpdatedAt":"2026-08-20T08:02:57.126Z","sourceUpdatedAt":"2026-08-20T08:02:57.126Z","sourceTitleSha256":"2c301f3965071e2bfb773497b119bfe4e616ef41902a611005f2061854523f84","sourceBodySha256":"a157fa152e18f4783339cc49e56ac61a608e57ce097cac34502e7a3acec17698","proposedTitleSha256":"d84da3a82a51c50109aa4457adbf40e581fd49c4a2f24b73f6772a063c70196f","proposedBodySha256":"14076be75535ff5dbf12d8f68bb41a8292fed3a98bb41f1eb65eea02de3cf524"} -{"event":"result","status":"pass","system":"linear","id":"FE-1435","url":"https://linear.app/hash/issue/FE-1435/test-whether-the-elicitor-stream-drives-petrinauts-chat-panel","postFetchAt":"2026-08-20T11:06:42.305Z","postUpdatedAt":"2026-08-20T11:06:41.948Z","proposedTitleSha256":"d84da3a82a51c50109aa4457adbf40e581fd49c4a2f24b73f6772a063c70196f","actualTitleSha256":"d84da3a82a51c50109aa4457adbf40e581fd49c4a2f24b73f6772a063c70196f","proposedBodySha256":"14076be75535ff5dbf12d8f68bb41a8292fed3a98bb41f1eb65eea02de3cf524","actualBodySha256":"14076be75535ff5dbf12d8f68bb41a8292fed3a98bb41f1eb65eea02de3cf524","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1436","url":"https://linear.app/hash/issue/FE-1436/the-elicitor-answers-conversation-turns-in-petrinauts-real-chat-panel","preFetchAt":"2026-08-20T11:06:42.555Z","frozenSourceUpdatedAt":"2026-08-20T08:02:57.380Z","sourceUpdatedAt":"2026-08-20T08:02:57.380Z","sourceTitleSha256":"a5958e22310b6c5fa71520b3c9e0c5e27b41bf557cbcf54c0d423297a0a9a6b4","sourceBodySha256":"b07965244f137a1844adffb0b30dce5130cd4e8555f2e139579a6bc8456d0e69","proposedTitleSha256":"c12d85230f11c97cfb9a097351dbfb248e936c364441c6e63410c6f285693717","proposedBodySha256":"8f8e69c34fc30483f729716e87f695f62bfcf63cb383250b0635a809ef478103"} -{"event":"result","status":"pass","system":"linear","id":"FE-1436","url":"https://linear.app/hash/issue/FE-1436/connect-the-elicitor-to-petrinauts-real-chat-panel","postFetchAt":"2026-08-20T11:06:43.150Z","postUpdatedAt":"2026-08-20T11:06:42.812Z","proposedTitleSha256":"c12d85230f11c97cfb9a097351dbfb248e936c364441c6e63410c6f285693717","actualTitleSha256":"c12d85230f11c97cfb9a097351dbfb248e936c364441c6e63410c6f285693717","proposedBodySha256":"8f8e69c34fc30483f729716e87f695f62bfcf63cb383250b0635a809ef478103","actualBodySha256":"8f8e69c34fc30483f729716e87f695f62bfcf63cb383250b0635a809ef478103","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1437","url":"https://linear.app/hash/issue/FE-1437/hashintelbrunch-agent-lives-in-hashintelhash-with-its-history","preFetchAt":"2026-08-20T11:06:43.395Z","frozenSourceUpdatedAt":"2026-08-19T16:20:27.220Z","sourceUpdatedAt":"2026-08-19T16:20:27.220Z","sourceTitleSha256":"75970ac9d4456f223830b137b2f74eab7999fb8e35f2216057966b99c9bc03ce","sourceBodySha256":"cbe7b6d050e3486f8e1649848516b240c282e85f601a56f693f095059902a476","proposedTitleSha256":"c44d0b68dd8c9f0f8f5bd048e952056d7fd85af4407a9faaea78b4481e9828d1","proposedBodySha256":"ca9ca144d3aa18223c7141542955d1c39b805323030bce4a25b948a854de5bbf"} -{"event":"result","status":"pass","system":"linear","id":"FE-1437","url":"https://linear.app/hash/issue/FE-1437/move-brunch-agent-into-hashintelhash-with-its-history","postFetchAt":"2026-08-20T11:06:44.001Z","postUpdatedAt":"2026-08-20T11:06:43.671Z","proposedTitleSha256":"c44d0b68dd8c9f0f8f5bd048e952056d7fd85af4407a9faaea78b4481e9828d1","actualTitleSha256":"c44d0b68dd8c9f0f8f5bd048e952056d7fd85af4407a9faaea78b4481e9828d1","proposedBodySha256":"ca9ca144d3aa18223c7141542955d1c39b805323030bce4a25b948a854de5bbf","actualBodySha256":"ca9ca144d3aa18223c7141542955d1c39b805323030bce4a25b948a854de5bbf","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1438","url":"https://linear.app/hash/issue/FE-1438/the-elicitor-builds-and-repairs-the-net-through-petrinauts-client","preFetchAt":"2026-08-20T11:06:44.236Z","frozenSourceUpdatedAt":"2026-08-19T16:26:26.410Z","sourceUpdatedAt":"2026-08-19T16:26:26.410Z","sourceTitleSha256":"fab2a16c55c83622257f80bc3c8d2bafe38e778c7618766f473a51fbbdbfde5c","sourceBodySha256":"4b1de406e41f3b22fd6fa75888723caa04bceb2fd97bf01d359a4f52cb9a8c0f","proposedTitleSha256":"eb60af0a2f4f44163191b1254c09757ced2afa1b55d98ac566771107ff95dbd9","proposedBodySha256":"47857e1be8bc182f3954b31aac4e7cd10265e00458549bf0bcc47484cf2d6410"} -{"event":"result","status":"pass","system":"linear","id":"FE-1438","url":"https://linear.app/hash/issue/FE-1438/build-and-repair-petrinaut-nets-through-client-tools","postFetchAt":"2026-08-20T11:06:45.505Z","postUpdatedAt":"2026-08-20T11:06:45.008Z","proposedTitleSha256":"eb60af0a2f4f44163191b1254c09757ced2afa1b55d98ac566771107ff95dbd9","actualTitleSha256":"eb60af0a2f4f44163191b1254c09757ced2afa1b55d98ac566771107ff95dbd9","proposedBodySha256":"47857e1be8bc182f3954b31aac4e7cd10265e00458549bf0bcc47484cf2d6410","actualBodySha256":"47857e1be8bc182f3954b31aac4e7cd10265e00458549bf0bcc47484cf2d6410","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1439","url":"https://linear.app/hash/issue/FE-1439/elicitation-sessions-are-private-per-browser-and-survive-a-reload","preFetchAt":"2026-08-20T11:06:45.958Z","frozenSourceUpdatedAt":"2026-08-19T16:20:26.834Z","sourceUpdatedAt":"2026-08-19T16:20:26.834Z","sourceTitleSha256":"a52fa9f76201b88dac7f36951eb5cf70a0fbe75b77812e21ebc5ef0f904747bc","sourceBodySha256":"c98a5d0620887f1d30a07eb63f16d2d76d7dace03784a3b3d11e63f3874a8d5f","proposedTitleSha256":"83badee7b36397901779775d5c61784d988598414276c7fc7688524a5412816d","proposedBodySha256":"98c4a00fd0ed5d31948361198b25bdc575fd637e9f2553049511f9b5c7ddcc9a"} -{"event":"result","status":"pass","system":"linear","id":"FE-1439","url":"https://linear.app/hash/issue/FE-1439/keep-elicitation-sessions-private-and-durable-per-browser","postFetchAt":"2026-08-20T11:06:46.568Z","postUpdatedAt":"2026-08-20T11:06:46.228Z","proposedTitleSha256":"83badee7b36397901779775d5c61784d988598414276c7fc7688524a5412816d","actualTitleSha256":"83badee7b36397901779775d5c61784d988598414276c7fc7688524a5412816d","proposedBodySha256":"98c4a00fd0ed5d31948361198b25bdc575fd637e9f2553049511f9b5c7ddcc9a","actualBodySha256":"98c4a00fd0ed5d31948361198b25bdc575fd637e9f2553049511f9b5c7ddcc9a","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1440","url":"https://linear.app/hash/issue/FE-1440/demopetrinautorg-ships-the-elicitor-behind-its-chat-panel","preFetchAt":"2026-08-20T11:06:46.796Z","frozenSourceUpdatedAt":"2026-08-19T16:20:26.864Z","sourceUpdatedAt":"2026-08-19T16:20:26.864Z","sourceTitleSha256":"c963a4b6c2fd98029b5e8c57e30e341a983769da62cec64af0236ba081293204","sourceBodySha256":"697595134f5edfbfb98ca3046f039abca72047f4bf214916d4abcff9d41f04e5","proposedTitleSha256":"d882e519616af05bc1151622a8b41998f60086c3f34c03054700d640abb8639b","proposedBodySha256":"1770a1db71c34b2e7f1de875342f58b3212cdcd7d8c386653675f87b80f34622"} -{"event":"result","status":"pass","system":"linear","id":"FE-1440","url":"https://linear.app/hash/issue/FE-1440/ship-the-elicitor-in-demopetrinautorgs-chat-panel","postFetchAt":"2026-08-20T11:06:47.404Z","postUpdatedAt":"2026-08-20T11:06:47.047Z","proposedTitleSha256":"d882e519616af05bc1151622a8b41998f60086c3f34c03054700d640abb8639b","actualTitleSha256":"d882e519616af05bc1151622a8b41998f60086c3f34c03054700d640abb8639b","proposedBodySha256":"1770a1db71c34b2e7f1de875342f58b3212cdcd7d8c386653675f87b80f34622","actualBodySha256":"1770a1db71c34b2e7f1de875342f58b3212cdcd7d8c386653675f87b80f34622","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1441","url":"https://linear.app/hash/issue/FE-1441/the-elicitor-server-runs-on-hash-infrastructure-behind-the-pre-remote","preFetchAt":"2026-08-20T11:06:47.654Z","frozenSourceUpdatedAt":"2026-08-19T16:20:26.907Z","sourceUpdatedAt":"2026-08-19T16:20:26.907Z","sourceTitleSha256":"7857490e95bf278812b8552e2ed55d21146842212873364d9369fb83a55afeb2","sourceBodySha256":"2ddc691ac9f44ccc31fab6071cefa9c67942c94a358e3f4805da80d61fc19eb0","proposedTitleSha256":"2a684083abe883d56c05cfb528991e06968ddc3ae642725a685a2b8856552838","proposedBodySha256":"56da85ce45688f08c3c9bcc75a1feb6c66c33c364d7d956ab864b4dc6ac7e09c"} -{"event":"result","status":"pass","system":"linear","id":"FE-1441","url":"https://linear.app/hash/issue/FE-1441/deploy-the-elicitor-server-behind-the-remote-release-checks","postFetchAt":"2026-08-20T11:06:48.297Z","postUpdatedAt":"2026-08-20T11:06:47.912Z","proposedTitleSha256":"2a684083abe883d56c05cfb528991e06968ddc3ae642725a685a2b8856552838","actualTitleSha256":"2a684083abe883d56c05cfb528991e06968ddc3ae642725a685a2b8856552838","proposedBodySha256":"56da85ce45688f08c3c9bcc75a1feb6c66c33c364d7d956ab864b4dc6ac7e09c","actualBodySha256":"56da85ce45688f08c3c9bcc75a1feb6c66c33c364d7d956ab864b4dc6ac7e09c","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1442","url":"https://linear.app/hash/issue/FE-1442/captures-and-completion-accounting-render-live-in-the-demo-site","preFetchAt":"2026-08-20T11:06:48.546Z","frozenSourceUpdatedAt":"2026-08-19T16:20:27.003Z","sourceUpdatedAt":"2026-08-19T16:20:27.003Z","sourceTitleSha256":"aed7b19ffd731679630103fc8bee5d7cc962a5e2a54f6fb0d7336651c883bc1a","sourceBodySha256":"864ae481d518556b75611b62baf28fb2de7df4d8db340d87de86942ccea9130f","proposedTitleSha256":"5616c1839456f14a8a810e55e73ab6b6cd733e9a7a7734c09e4237b70fedd226","proposedBodySha256":"e9c8f5050856192989f953358829fce6ace7b563471ee3e2661d9f2fd80b050e"} -{"event":"result","status":"pass","system":"linear","id":"FE-1442","url":"https://linear.app/hash/issue/FE-1442/show-live-captures-and-completion-accounting-in-the-demo","postFetchAt":"2026-08-20T11:06:49.096Z","postUpdatedAt":"2026-08-20T11:06:48.796Z","proposedTitleSha256":"5616c1839456f14a8a810e55e73ab6b6cd733e9a7a7734c09e4237b70fedd226","actualTitleSha256":"5616c1839456f14a8a810e55e73ab6b6cd733e9a7a7734c09e4237b70fedd226","proposedBodySha256":"e9c8f5050856192989f953358829fce6ace7b563471ee3e2661d9f2fd80b050e","actualBodySha256":"e9c8f5050856192989f953358829fce6ace7b563471ee3e2661d9f2fd80b050e","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1448","url":"https://linear.app/hash/issue/FE-1448/petrinaut-hosts-render-their-own-interactive-chat-tools","preFetchAt":"2026-08-20T11:06:49.330Z","frozenSourceUpdatedAt":"2026-08-19T16:20:27.813Z","sourceUpdatedAt":"2026-08-19T16:20:27.813Z","sourceTitleSha256":"675c39fa590ebda855c128a3837f13a98d557cce9fda8da734e5e4efe36d9b18","sourceBodySha256":"d3722991fd835760a4d8a24c5822e68439a3860fb2308a354812cfafea0bf73e","proposedTitleSha256":"58f4d49b2319314a9ecad6dc0804e4883addf7a1135a04cd778f1a3808639503","proposedBodySha256":"95b45fea18fcca04ab562f60798aed01189c4b3010184d8eb011bd026c701d37"} -{"event":"result","status":"pass","system":"linear","id":"FE-1448","url":"https://linear.app/hash/issue/FE-1448/let-petrinaut-hosts-render-interactive-chat-tools","postFetchAt":"2026-08-20T11:06:50.015Z","postUpdatedAt":"2026-08-20T11:06:49.651Z","proposedTitleSha256":"58f4d49b2319314a9ecad6dc0804e4883addf7a1135a04cd778f1a3808639503","actualTitleSha256":"58f4d49b2319314a9ecad6dc0804e4883addf7a1135a04cd778f1a3808639503","proposedBodySha256":"95b45fea18fcca04ab562f60798aed01189c4b3010184d8eb011bd026c701d37","actualBodySha256":"95b45fea18fcca04ab562f60798aed01189c4b3010184d8eb011bd026c701d37","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1449","url":"https://linear.app/hash/issue/FE-1449/a-structured-brunch-question-suspends-and-resumes-visibly-in-petrinaut","preFetchAt":"2026-08-20T11:06:50.260Z","frozenSourceUpdatedAt":"2026-08-19T16:20:28.606Z","sourceUpdatedAt":"2026-08-19T16:20:28.606Z","sourceTitleSha256":"2b666da1c16c40eefa3542811dce3f24da1d9e005261fba747aace65a9fdb67c","sourceBodySha256":"acd7ce7b5b6bf664d57404bd478ac63a98039fa232ad5832d2706f11a3a4dbd0","proposedTitleSha256":"0d9a62e5831bbe93393b16af03d8f817c49c7e96f21e17d1c8311599b7db78cd","proposedBodySha256":"1981c5120550d60e77ee6d16355b06cf7eeaacaeee9e7e16432422dfcb4606e3"} -{"event":"result","status":"pass","system":"linear","id":"FE-1449","url":"https://linear.app/hash/issue/FE-1449/prove-a-structured-brunch-question-suspends-and-resumes-in-petrinaut","postFetchAt":"2026-08-20T11:06:51.596Z","postUpdatedAt":"2026-08-20T11:06:50.620Z","proposedTitleSha256":"0d9a62e5831bbe93393b16af03d8f817c49c7e96f21e17d1c8311599b7db78cd","actualTitleSha256":"0d9a62e5831bbe93393b16af03d8f817c49c7e96f21e17d1c8311599b7db78cd","proposedBodySha256":"1981c5120550d60e77ee6d16355b06cf7eeaacaeee9e7e16432422dfcb4606e3","actualBodySha256":"1981c5120550d60e77ee6d16355b06cf7eeaacaeee9e7e16432422dfcb4606e3","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"linear","id":"FE-1451","url":"https://linear.app/hash/issue/FE-1451/keep-issues-comments-and-prs-easy-to-scan","preFetchAt":"2026-08-20T11:06:51.842Z","frozenSourceUpdatedAt":"2026-08-19T16:20:28.510Z","sourceUpdatedAt":"2026-08-19T16:20:28.510Z","sourceTitleSha256":"37219df7e036b6c5e63477976107b2c2a704596cc7fe084757fa42d906e26c5c","sourceBodySha256":"91b3b7773b3f664b22ace816a7d85d631020f9bf80348966cc6294c461dc8bcb","proposedTitleSha256":"37219df7e036b6c5e63477976107b2c2a704596cc7fe084757fa42d906e26c5c","proposedBodySha256":"91b3b7773b3f664b22ace816a7d85d631020f9bf80348966cc6294c461dc8bcb"} -{"event":"result","status":"pass-no-write","system":"linear","id":"FE-1451","url":"https://linear.app/hash/issue/FE-1451/keep-issues-comments-and-prs-easy-to-scan","postFetchAt":"2026-08-20T11:06:51.842Z","postUpdatedAt":"2026-08-19T16:20:28.510Z","proposedTitleSha256":"37219df7e036b6c5e63477976107b2c2a704596cc7fe084757fa42d906e26c5c","actualTitleSha256":"37219df7e036b6c5e63477976107b2c2a704596cc7fe084757fa42d906e26c5c","proposedBodySha256":"91b3b7773b3f664b22ace816a7d85d631020f9bf80348966cc6294c461dc8bcb","actualBodySha256":"91b3b7773b3f664b22ace816a7d85d631020f9bf80348966cc6294c461dc8bcb"} -{"event":"attempt","system":"github","id":"1","url":"https://github.com/hashintel/brunch-lite/pull/1","preFetchAt":"2026-08-20T11:06:52.231Z","frozenSourceUpdatedAt":"2026-08-19T13:59:58Z","sourceUpdatedAt":"2026-08-19T13:59:58Z","sourceTitleSha256":"2349400d04eb80fe6d4f1253ea64eecc37cc01df5cc71141a92dbc1839396dbc","sourceBodySha256":"b034992e70ad0e9a8ab26ba321aaa87f91365d563c4bb4a021dd8c5cee3aed51","proposedTitleSha256":"a8f91f538eeab4311fa3335a16ca25fec9b7947f3fa92482ea23445e1fc16ee2","proposedBodySha256":"0979c93127052da2249f61275c1bdc3d9b77744bb39473f37b45268c9ba56e27"} -{"event":"result","status":"pass","system":"github","id":"1","url":"https://github.com/hashintel/brunch-lite/pull/1","postFetchAt":"2026-08-20T11:06:54.381Z","postUpdatedAt":"2026-08-20T11:06:53Z","proposedTitleSha256":"a8f91f538eeab4311fa3335a16ca25fec9b7947f3fa92482ea23445e1fc16ee2","actualTitleSha256":"a8f91f538eeab4311fa3335a16ca25fec9b7947f3fa92482ea23445e1fc16ee2","proposedBodySha256":"0979c93127052da2249f61275c1bdc3d9b77744bb39473f37b45268c9ba56e27","actualBodySha256":"0979c93127052da2249f61275c1bdc3d9b77744bb39473f37b45268c9ba56e27","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"github","id":"2","url":"https://github.com/hashintel/brunch-lite/pull/2","preFetchAt":"2026-08-20T11:06:54.768Z","frozenSourceUpdatedAt":"2026-08-19T14:00:01Z","sourceUpdatedAt":"2026-08-19T14:00:01Z","sourceTitleSha256":"213035e657b4ee4135ccdf4f4781e3b7a94d4a42d404ced75c5f821df9c4b371","sourceBodySha256":"def66364b5c84aaf3357de5f03701eea169d84972dceb6f35284fa16e281f6ce","proposedTitleSha256":"458af585ac43810b677bbdfe77441117a25d8071bfe5a8d069b3879b23fa0417","proposedBodySha256":"def66364b5c84aaf3357de5f03701eea169d84972dceb6f35284fa16e281f6ce"} -{"event":"result","status":"pass","system":"github","id":"2","url":"https://github.com/hashintel/brunch-lite/pull/2","postFetchAt":"2026-08-20T11:06:57.003Z","postUpdatedAt":"2026-08-20T11:06:55Z","proposedTitleSha256":"458af585ac43810b677bbdfe77441117a25d8071bfe5a8d069b3879b23fa0417","actualTitleSha256":"458af585ac43810b677bbdfe77441117a25d8071bfe5a8d069b3879b23fa0417","proposedBodySha256":"def66364b5c84aaf3357de5f03701eea169d84972dceb6f35284fa16e281f6ce","actualBodySha256":"def66364b5c84aaf3357de5f03701eea169d84972dceb6f35284fa16e281f6ce","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"github","id":"3","url":"https://github.com/hashintel/brunch-lite/pull/3","preFetchAt":"2026-08-20T11:06:57.383Z","frozenSourceUpdatedAt":"2026-08-19T14:00:02Z","sourceUpdatedAt":"2026-08-19T14:00:02Z","sourceTitleSha256":"479fc988f738df20272d535d4cdec59affba9e2c782f716ba8b2c1077966ece0","sourceBodySha256":"c11ea0f55f34cf925c4c4dd02834521dc97c1f1b2575c18b5b48e087b027fb00","proposedTitleSha256":"7598d7609bc76a5d78feb41eeac4bbcc1db8d0c985950181ae0fe83d6c6ba673","proposedBodySha256":"c11ea0f55f34cf925c4c4dd02834521dc97c1f1b2575c18b5b48e087b027fb00"} -{"event":"result","status":"pass","system":"github","id":"3","url":"https://github.com/hashintel/brunch-lite/pull/3","postFetchAt":"2026-08-20T11:06:59.286Z","postUpdatedAt":"2026-08-20T11:06:58Z","proposedTitleSha256":"7598d7609bc76a5d78feb41eeac4bbcc1db8d0c985950181ae0fe83d6c6ba673","actualTitleSha256":"7598d7609bc76a5d78feb41eeac4bbcc1db8d0c985950181ae0fe83d6c6ba673","proposedBodySha256":"c11ea0f55f34cf925c4c4dd02834521dc97c1f1b2575c18b5b48e087b027fb00","actualBodySha256":"c11ea0f55f34cf925c4c4dd02834521dc97c1f1b2575c18b5b48e087b027fb00","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"github","id":"4","url":"https://github.com/hashintel/brunch-lite/pull/4","preFetchAt":"2026-08-20T11:06:59.824Z","frozenSourceUpdatedAt":"2026-08-19T14:00:04Z","sourceUpdatedAt":"2026-08-19T14:00:04Z","sourceTitleSha256":"d1e15580b5ebf90f1be401019c3b33876f59f9aabe23a0424ee5b0688b15a628","sourceBodySha256":"b14b6372d8340e427843903ecd013bdf356c3adc8d00a4cec6f664672b67817d","proposedTitleSha256":"cc1738d69bc2a0bf6d4a0f4f5f784f693fd5e6dbe17dc6feb725d0a4e118c00c","proposedBodySha256":"b14b6372d8340e427843903ecd013bdf356c3adc8d00a4cec6f664672b67817d"} -{"event":"result","status":"pass","system":"github","id":"4","url":"https://github.com/hashintel/brunch-lite/pull/4","postFetchAt":"2026-08-20T11:07:01.775Z","postUpdatedAt":"2026-08-20T11:07:00Z","proposedTitleSha256":"cc1738d69bc2a0bf6d4a0f4f5f784f693fd5e6dbe17dc6feb725d0a4e118c00c","actualTitleSha256":"cc1738d69bc2a0bf6d4a0f4f5f784f693fd5e6dbe17dc6feb725d0a4e118c00c","proposedBodySha256":"b14b6372d8340e427843903ecd013bdf356c3adc8d00a4cec6f664672b67817d","actualBodySha256":"b14b6372d8340e427843903ecd013bdf356c3adc8d00a4cec6f664672b67817d","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"github","id":"5","url":"https://github.com/hashintel/brunch-lite/pull/5","preFetchAt":"2026-08-20T11:07:02.333Z","frozenSourceUpdatedAt":"2026-08-19T14:00:05Z","sourceUpdatedAt":"2026-08-19T14:00:05Z","sourceTitleSha256":"229ac27a91eab0e70254f9a283f3ef6a9f8d375bbf17fffdb8f7d04d2ac59603","sourceBodySha256":"e6f28eca8b261bb837c955a6a243db8b74fbb2b31d4033a230b131ffef570cbc","proposedTitleSha256":"7e6aa2e05b76ccab92408f3a06e373916fa4a381d00bcfb1ee2f35bebbbb2fce","proposedBodySha256":"e6f28eca8b261bb837c955a6a243db8b74fbb2b31d4033a230b131ffef570cbc"} -{"event":"result","status":"pass","system":"github","id":"5","url":"https://github.com/hashintel/brunch-lite/pull/5","postFetchAt":"2026-08-20T11:07:04.424Z","postUpdatedAt":"2026-08-20T11:07:03Z","proposedTitleSha256":"7e6aa2e05b76ccab92408f3a06e373916fa4a381d00bcfb1ee2f35bebbbb2fce","actualTitleSha256":"7e6aa2e05b76ccab92408f3a06e373916fa4a381d00bcfb1ee2f35bebbbb2fce","proposedBodySha256":"e6f28eca8b261bb837c955a6a243db8b74fbb2b31d4033a230b131ffef570cbc","actualBodySha256":"e6f28eca8b261bb837c955a6a243db8b74fbb2b31d4033a230b131ffef570cbc","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"github","id":"6","url":"https://github.com/hashintel/brunch-lite/pull/6","preFetchAt":"2026-08-20T11:07:04.927Z","frozenSourceUpdatedAt":"2026-08-19T14:00:06Z","sourceUpdatedAt":"2026-08-19T14:00:06Z","sourceTitleSha256":"49a7e617a4276a193a7ac78578d95fb09586d2fd660232472afb83eaa2c81739","sourceBodySha256":"a504f8f2a3ac48ec0599e2f1bf0a8d9968e94dbbd04eb6d8b23d200966e8bbb6","proposedTitleSha256":"bdc8e582d944ef44c03f78151f32661499c5215a2f327e39174752accf901a04","proposedBodySha256":"d83c7fcbbec74341c2a25153d3146f0bd579d0c4f60794b634a3aaf73c472bcf"} -{"event":"result","status":"pass","system":"github","id":"6","url":"https://github.com/hashintel/brunch-lite/pull/6","postFetchAt":"2026-08-20T11:07:06.908Z","postUpdatedAt":"2026-08-20T11:07:05Z","proposedTitleSha256":"bdc8e582d944ef44c03f78151f32661499c5215a2f327e39174752accf901a04","actualTitleSha256":"bdc8e582d944ef44c03f78151f32661499c5215a2f327e39174752accf901a04","proposedBodySha256":"d83c7fcbbec74341c2a25153d3146f0bd579d0c4f60794b634a3aaf73c472bcf","actualBodySha256":"d83c7fcbbec74341c2a25153d3146f0bd579d0c4f60794b634a3aaf73c472bcf","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"github","id":"7","url":"https://github.com/hashintel/brunch-lite/pull/7","preFetchAt":"2026-08-20T11:07:07.476Z","frozenSourceUpdatedAt":"2026-08-19T14:00:08Z","sourceUpdatedAt":"2026-08-19T14:00:08Z","sourceTitleSha256":"09929e03f4a76113a051454c63fd0fdad8466e536a83e0e3f13e4a817672c64c","sourceBodySha256":"23f9bc92607a7d5be6b55ee742f4669bf684245ce7a5c126d92f5fd65807c119","proposedTitleSha256":"9e63a535656da4e9bbe1c7957f4bfb59f90355f87f2ce4e9de37710a82389bf4","proposedBodySha256":"23f9bc92607a7d5be6b55ee742f4669bf684245ce7a5c126d92f5fd65807c119"} -{"event":"result","status":"pass","system":"github","id":"7","url":"https://github.com/hashintel/brunch-lite/pull/7","postFetchAt":"2026-08-20T11:07:09.303Z","postUpdatedAt":"2026-08-20T11:07:08Z","proposedTitleSha256":"9e63a535656da4e9bbe1c7957f4bfb59f90355f87f2ce4e9de37710a82389bf4","actualTitleSha256":"9e63a535656da4e9bbe1c7957f4bfb59f90355f87f2ce4e9de37710a82389bf4","proposedBodySha256":"23f9bc92607a7d5be6b55ee742f4669bf684245ce7a5c126d92f5fd65807c119","actualBodySha256":"23f9bc92607a7d5be6b55ee742f4669bf684245ce7a5c126d92f5fd65807c119","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"github","id":"8","url":"https://github.com/hashintel/brunch-lite/pull/8","preFetchAt":"2026-08-20T11:07:09.957Z","frozenSourceUpdatedAt":"2026-08-19T14:00:10Z","sourceUpdatedAt":"2026-08-19T14:00:10Z","sourceTitleSha256":"bb81d98a6ecb4ad55e7dddaf963f52da31c86a14e7cb652ee63e46396ee2867e","sourceBodySha256":"e8ed6d96887037c79160976cbc45078edc4c452c456ef50783cead160602a7f0","proposedTitleSha256":"bae06a837f10393b559487334471689c1caec1bc3b2679307399a9c3b2bf8dd6","proposedBodySha256":"e8ed6d96887037c79160976cbc45078edc4c452c456ef50783cead160602a7f0"} -{"event":"result","status":"pass","system":"github","id":"8","url":"https://github.com/hashintel/brunch-lite/pull/8","postFetchAt":"2026-08-20T11:07:12.738Z","postUpdatedAt":"2026-08-20T11:07:11Z","proposedTitleSha256":"bae06a837f10393b559487334471689c1caec1bc3b2679307399a9c3b2bf8dd6","actualTitleSha256":"bae06a837f10393b559487334471689c1caec1bc3b2679307399a9c3b2bf8dd6","proposedBodySha256":"e8ed6d96887037c79160976cbc45078edc4c452c456ef50783cead160602a7f0","actualBodySha256":"e8ed6d96887037c79160976cbc45078edc4c452c456ef50783cead160602a7f0","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"github","id":"9","url":"https://github.com/hashintel/brunch-lite/pull/9","preFetchAt":"2026-08-20T11:07:13.222Z","frozenSourceUpdatedAt":"2026-08-19T14:00:12Z","sourceUpdatedAt":"2026-08-19T14:00:12Z","sourceTitleSha256":"438c3f68d2d61c5975585c864f38610d5e0899e9f6d1aea28d3091b82f63bb71","sourceBodySha256":"3e7d50eae5e60d3152d16e1ecefb3de27934ea2f73e2e9f26d995921a67cc869","proposedTitleSha256":"faa8423c1089c9e54da238b96e560b9f51d59753911795f70e02404c4330c2ad","proposedBodySha256":"3e7d50eae5e60d3152d16e1ecefb3de27934ea2f73e2e9f26d995921a67cc869"} -{"event":"result","status":"pass","system":"github","id":"9","url":"https://github.com/hashintel/brunch-lite/pull/9","postFetchAt":"2026-08-20T11:07:15.457Z","postUpdatedAt":"2026-08-20T11:07:14Z","proposedTitleSha256":"faa8423c1089c9e54da238b96e560b9f51d59753911795f70e02404c4330c2ad","actualTitleSha256":"faa8423c1089c9e54da238b96e560b9f51d59753911795f70e02404c4330c2ad","proposedBodySha256":"3e7d50eae5e60d3152d16e1ecefb3de27934ea2f73e2e9f26d995921a67cc869","actualBodySha256":"3e7d50eae5e60d3152d16e1ecefb3de27934ea2f73e2e9f26d995921a67cc869","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"github","id":"10","url":"https://github.com/hashintel/brunch-lite/pull/10","preFetchAt":"2026-08-20T11:07:15.813Z","frozenSourceUpdatedAt":"2026-08-19T14:00:14Z","sourceUpdatedAt":"2026-08-19T14:00:14Z","sourceTitleSha256":"9aa634521196bc66ad8a36254eb995aee3b484177468d0bb0e007fb358ffb8b7","sourceBodySha256":"f5249295ba3564783719e2d9e870e7659b074482d3c86d20803c118308d26a69","proposedTitleSha256":"9f9d804c351ad92f5aa55b524422d7f06ebfe17f1303afed89d45504f08c24c5","proposedBodySha256":"f5249295ba3564783719e2d9e870e7659b074482d3c86d20803c118308d26a69"} -{"event":"result","status":"pass","system":"github","id":"10","url":"https://github.com/hashintel/brunch-lite/pull/10","postFetchAt":"2026-08-20T11:07:17.856Z","postUpdatedAt":"2026-08-20T11:07:16Z","proposedTitleSha256":"9f9d804c351ad92f5aa55b524422d7f06ebfe17f1303afed89d45504f08c24c5","actualTitleSha256":"9f9d804c351ad92f5aa55b524422d7f06ebfe17f1303afed89d45504f08c24c5","proposedBodySha256":"f5249295ba3564783719e2d9e870e7659b074482d3c86d20803c118308d26a69","actualBodySha256":"f5249295ba3564783719e2d9e870e7659b074482d3c86d20803c118308d26a69","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"github","id":"11","url":"https://github.com/hashintel/brunch-lite/pull/11","preFetchAt":"2026-08-20T11:07:18.221Z","frozenSourceUpdatedAt":"2026-08-19T14:00:15Z","sourceUpdatedAt":"2026-08-19T14:00:15Z","sourceTitleSha256":"710989fa7a8870a91ba1e36447c52327068378fbb13bc879314f367363dc3f6c","sourceBodySha256":"a58c82980be648e3b01142e3c0d0087a3c49b893c9547055bde27b98bf0bc5e0","proposedTitleSha256":"22595d4a7cabef17857419e2dbf83063fe24ac0901cd30e437194d4ed67a8760","proposedBodySha256":"a58c82980be648e3b01142e3c0d0087a3c49b893c9547055bde27b98bf0bc5e0"} -{"event":"result","status":"pass","system":"github","id":"11","url":"https://github.com/hashintel/brunch-lite/pull/11","postFetchAt":"2026-08-20T11:07:20.140Z","postUpdatedAt":"2026-08-20T11:07:19Z","proposedTitleSha256":"22595d4a7cabef17857419e2dbf83063fe24ac0901cd30e437194d4ed67a8760","actualTitleSha256":"22595d4a7cabef17857419e2dbf83063fe24ac0901cd30e437194d4ed67a8760","proposedBodySha256":"a58c82980be648e3b01142e3c0d0087a3c49b893c9547055bde27b98bf0bc5e0","actualBodySha256":"a58c82980be648e3b01142e3c0d0087a3c49b893c9547055bde27b98bf0bc5e0","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"github","id":"12","url":"https://github.com/hashintel/brunch-lite/pull/12","preFetchAt":"2026-08-20T11:07:20.543Z","frozenSourceUpdatedAt":"2026-08-19T14:00:17Z","sourceUpdatedAt":"2026-08-19T14:00:17Z","sourceTitleSha256":"0451f66a36602e806df9c2112c1121521b94245613ec8efffb621bb83695c9d0","sourceBodySha256":"cca722394c59d6fc2431b1d6619f0991782d6d1969121d2d9193b4bbe84af0c3","proposedTitleSha256":"7e725384f146f7ebb36106ec02c07afc099ce5cdc2f930ba4071360521075864","proposedBodySha256":"cca722394c59d6fc2431b1d6619f0991782d6d1969121d2d9193b4bbe84af0c3"} -{"event":"result","status":"pass","system":"github","id":"12","url":"https://github.com/hashintel/brunch-lite/pull/12","postFetchAt":"2026-08-20T11:07:22.630Z","postUpdatedAt":"2026-08-20T11:07:21Z","proposedTitleSha256":"7e725384f146f7ebb36106ec02c07afc099ce5cdc2f930ba4071360521075864","actualTitleSha256":"7e725384f146f7ebb36106ec02c07afc099ce5cdc2f930ba4071360521075864","proposedBodySha256":"cca722394c59d6fc2431b1d6619f0991782d6d1969121d2d9193b4bbe84af0c3","actualBodySha256":"cca722394c59d6fc2431b1d6619f0991782d6d1969121d2d9193b4bbe84af0c3","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"github","id":"13","url":"https://github.com/hashintel/brunch-lite/pull/13","preFetchAt":"2026-08-20T11:07:23.079Z","frozenSourceUpdatedAt":"2026-08-19T14:00:18Z","sourceUpdatedAt":"2026-08-19T14:00:18Z","sourceTitleSha256":"fb7a8ac27ea24f41a79d749ca20733e2c25c4b1cee170719bc02ce0d241ad260","sourceBodySha256":"f9ab772a065145e23ed733c85c63888b8319ec31e8b04db7b281917372d796d4","proposedTitleSha256":"c43385e89922f03a26c6c15c89a3c7dc189fb95b3ccf2a94a808413977a5a242","proposedBodySha256":"7c6d072833c15450fddbb3e102cf9b39e7aeb2af621c1090a990f5c6894e0b14"} -{"event":"result","status":"pass","system":"github","id":"13","url":"https://github.com/hashintel/brunch-lite/pull/13","postFetchAt":"2026-08-20T11:07:25.419Z","postUpdatedAt":"2026-08-20T11:07:24Z","proposedTitleSha256":"c43385e89922f03a26c6c15c89a3c7dc189fb95b3ccf2a94a808413977a5a242","actualTitleSha256":"c43385e89922f03a26c6c15c89a3c7dc189fb95b3ccf2a94a808413977a5a242","proposedBodySha256":"7c6d072833c15450fddbb3e102cf9b39e7aeb2af621c1090a990f5c6894e0b14","actualBodySha256":"7c6d072833c15450fddbb3e102cf9b39e7aeb2af621c1090a990f5c6894e0b14","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"github","id":"14","url":"https://github.com/hashintel/brunch-lite/pull/14","preFetchAt":"2026-08-20T11:07:25.832Z","frozenSourceUpdatedAt":"2026-08-19T14:00:20Z","sourceUpdatedAt":"2026-08-19T14:00:20Z","sourceTitleSha256":"23fbf383a72d5bb528910b8067ebd01c3aef2aad088e7404b1c19f5a196b894e","sourceBodySha256":"c3cec4fdb4fe1de796714e121acf420b6d2da0d14d743c2e9b147d705f2ac8a1","proposedTitleSha256":"4d0da7c37b1220355532b1062a9574928d56585c3adccc46db85993296d42775","proposedBodySha256":"c3cec4fdb4fe1de796714e121acf420b6d2da0d14d743c2e9b147d705f2ac8a1"} -{"event":"result","status":"pass","system":"github","id":"14","url":"https://github.com/hashintel/brunch-lite/pull/14","postFetchAt":"2026-08-20T11:07:27.663Z","postUpdatedAt":"2026-08-20T11:07:26Z","proposedTitleSha256":"4d0da7c37b1220355532b1062a9574928d56585c3adccc46db85993296d42775","actualTitleSha256":"4d0da7c37b1220355532b1062a9574928d56585c3adccc46db85993296d42775","proposedBodySha256":"c3cec4fdb4fe1de796714e121acf420b6d2da0d14d743c2e9b147d705f2ac8a1","actualBodySha256":"c3cec4fdb4fe1de796714e121acf420b6d2da0d14d743c2e9b147d705f2ac8a1","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"github","id":"15","url":"https://github.com/hashintel/brunch-lite/pull/15","preFetchAt":"2026-08-20T11:07:28.028Z","frozenSourceUpdatedAt":"2026-08-19T14:00:22Z","sourceUpdatedAt":"2026-08-19T14:00:22Z","sourceTitleSha256":"9b957cd8c760d786142e94469da03e59648c0835a5a54a7ec59204f86372f2dd","sourceBodySha256":"105a9f8da172e7bd653130a30dcbe55ae828672de2469449a8c3e8e2d99901fa","proposedTitleSha256":"e01c6144680b6cd34c16ff1fe903b519508ac67a92e8685b10c958da998646d0","proposedBodySha256":"105a9f8da172e7bd653130a30dcbe55ae828672de2469449a8c3e8e2d99901fa"} -{"event":"result","status":"pass","system":"github","id":"15","url":"https://github.com/hashintel/brunch-lite/pull/15","postFetchAt":"2026-08-20T11:07:29.914Z","postUpdatedAt":"2026-08-20T11:07:29Z","proposedTitleSha256":"e01c6144680b6cd34c16ff1fe903b519508ac67a92e8685b10c958da998646d0","actualTitleSha256":"e01c6144680b6cd34c16ff1fe903b519508ac67a92e8685b10c958da998646d0","proposedBodySha256":"105a9f8da172e7bd653130a30dcbe55ae828672de2469449a8c3e8e2d99901fa","actualBodySha256":"105a9f8da172e7bd653130a30dcbe55ae828672de2469449a8c3e8e2d99901fa","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"github","id":"16","url":"https://github.com/hashintel/brunch-lite/pull/16","preFetchAt":"2026-08-20T11:07:30.263Z","frozenSourceUpdatedAt":"2026-08-19T14:00:24Z","sourceUpdatedAt":"2026-08-19T14:00:24Z","sourceTitleSha256":"57533df404cf90eb95676d0223056011c73aa2ab89bacfeb6ebc12e5e91d1703","sourceBodySha256":"1fc9f55dcaded9b23be43b01e2fe6961b26a0bf5556f802ddfc7d1066846c05d","proposedTitleSha256":"c4f73648953a1017375d057a002e7e9e5e54ec125908d71ac88751df04e3cece","proposedBodySha256":"b8d8aeb75c6e9c3d29795bf1328907df38da7a1c5b213991077340cd533fef60"} -{"event":"result","status":"pass","system":"github","id":"16","url":"https://github.com/hashintel/brunch-lite/pull/16","postFetchAt":"2026-08-20T11:07:32.544Z","postUpdatedAt":"2026-08-20T11:07:31Z","proposedTitleSha256":"c4f73648953a1017375d057a002e7e9e5e54ec125908d71ac88751df04e3cece","actualTitleSha256":"c4f73648953a1017375d057a002e7e9e5e54ec125908d71ac88751df04e3cece","proposedBodySha256":"b8d8aeb75c6e9c3d29795bf1328907df38da7a1c5b213991077340cd533fef60","actualBodySha256":"b8d8aeb75c6e9c3d29795bf1328907df38da7a1c5b213991077340cd533fef60","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"github","id":"17","url":"https://github.com/hashintel/brunch-lite/pull/17","preFetchAt":"2026-08-20T11:07:33.041Z","frozenSourceUpdatedAt":"2026-08-19T14:00:26Z","sourceUpdatedAt":"2026-08-19T14:00:26Z","sourceTitleSha256":"8df88b0ce1d3b89975c44cc7efb9d680d2cbda6518cf08c0af135ab15e663dae","sourceBodySha256":"08e75c781979efa46ebd3537f207e71dc89e95cd002bf534696690d6eea06a84","proposedTitleSha256":"9954744d1e7f83e9cf2e0790c7f4fef2053bae828b5b404a6907e0d5fa7993d8","proposedBodySha256":"08e75c781979efa46ebd3537f207e71dc89e95cd002bf534696690d6eea06a84"} -{"event":"result","status":"pass","system":"github","id":"17","url":"https://github.com/hashintel/brunch-lite/pull/17","postFetchAt":"2026-08-20T11:07:35.132Z","postUpdatedAt":"2026-08-20T11:07:34Z","proposedTitleSha256":"9954744d1e7f83e9cf2e0790c7f4fef2053bae828b5b404a6907e0d5fa7993d8","actualTitleSha256":"9954744d1e7f83e9cf2e0790c7f4fef2053bae828b5b404a6907e0d5fa7993d8","proposedBodySha256":"08e75c781979efa46ebd3537f207e71dc89e95cd002bf534696690d6eea06a84","actualBodySha256":"08e75c781979efa46ebd3537f207e71dc89e95cd002bf534696690d6eea06a84","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"github","id":"18","url":"https://github.com/hashintel/brunch-lite/pull/18","preFetchAt":"2026-08-20T11:07:35.562Z","frozenSourceUpdatedAt":"2026-08-19T14:00:27Z","sourceUpdatedAt":"2026-08-19T14:00:27Z","sourceTitleSha256":"e2af4a86d0444c15a7135f477591b390b36fff479e0915de853efc36b4e36ec9","sourceBodySha256":"74deff20a1a55625cb86f64d8a867194c23f04df9057556314683a9420c14062","proposedTitleSha256":"f698116a5d502505cf06eee2c881152aacff9a5dc62dd50ce994097d89e26504","proposedBodySha256":"74deff20a1a55625cb86f64d8a867194c23f04df9057556314683a9420c14062"} -{"event":"result","status":"pass","system":"github","id":"18","url":"https://github.com/hashintel/brunch-lite/pull/18","postFetchAt":"2026-08-20T11:07:37.675Z","postUpdatedAt":"2026-08-20T11:07:36Z","proposedTitleSha256":"f698116a5d502505cf06eee2c881152aacff9a5dc62dd50ce994097d89e26504","actualTitleSha256":"f698116a5d502505cf06eee2c881152aacff9a5dc62dd50ce994097d89e26504","proposedBodySha256":"74deff20a1a55625cb86f64d8a867194c23f04df9057556314683a9420c14062","actualBodySha256":"74deff20a1a55625cb86f64d8a867194c23f04df9057556314683a9420c14062","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"github","id":"19","url":"https://github.com/hashintel/brunch-lite/pull/19","preFetchAt":"2026-08-20T11:07:38.047Z","frozenSourceUpdatedAt":"2026-08-19T14:00:29Z","sourceUpdatedAt":"2026-08-19T14:00:29Z","sourceTitleSha256":"8baa3f7952c774fe17d2c91f5700063fa8e11047b193813df55b11766fee8eab","sourceBodySha256":"ae7d1b251d8e1f3547dc5b55930a4e1372bd97f9af952c7e8d3e2319d46c0172","proposedTitleSha256":"8ccaf0c07e327041d4668ce81044910a1a8aad1a972fea9a58cb4712248b55d5","proposedBodySha256":"ae7d1b251d8e1f3547dc5b55930a4e1372bd97f9af952c7e8d3e2319d46c0172"} -{"event":"result","status":"pass","system":"github","id":"19","url":"https://github.com/hashintel/brunch-lite/pull/19","postFetchAt":"2026-08-20T11:07:39.948Z","postUpdatedAt":"2026-08-20T11:07:38Z","proposedTitleSha256":"8ccaf0c07e327041d4668ce81044910a1a8aad1a972fea9a58cb4712248b55d5","actualTitleSha256":"8ccaf0c07e327041d4668ce81044910a1a8aad1a972fea9a58cb4712248b55d5","proposedBodySha256":"ae7d1b251d8e1f3547dc5b55930a4e1372bd97f9af952c7e8d3e2319d46c0172","actualBodySha256":"ae7d1b251d8e1f3547dc5b55930a4e1372bd97f9af952c7e8d3e2319d46c0172","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"github","id":"20","url":"https://github.com/hashintel/brunch-lite/pull/20","preFetchAt":"2026-08-20T11:07:40.388Z","frozenSourceUpdatedAt":"2026-08-19T14:00:30Z","sourceUpdatedAt":"2026-08-19T14:00:30Z","sourceTitleSha256":"f994067680a1d677c1289baa403f1b2db4452f64845198006fd1c337648d7e81","sourceBodySha256":"c3efa637340c6fb14916825e3303b27da6733d7a9528c5bc10cdf065722c9b64","proposedTitleSha256":"d9f075f2ac5533ff46283090cb71614d94d646c35770bc576fc4e0ef56cc8fac","proposedBodySha256":"c3efa637340c6fb14916825e3303b27da6733d7a9528c5bc10cdf065722c9b64"} -{"event":"result","status":"pass","system":"github","id":"20","url":"https://github.com/hashintel/brunch-lite/pull/20","postFetchAt":"2026-08-20T11:07:42.332Z","postUpdatedAt":"2026-08-20T11:07:41Z","proposedTitleSha256":"d9f075f2ac5533ff46283090cb71614d94d646c35770bc576fc4e0ef56cc8fac","actualTitleSha256":"d9f075f2ac5533ff46283090cb71614d94d646c35770bc576fc4e0ef56cc8fac","proposedBodySha256":"c3efa637340c6fb14916825e3303b27da6733d7a9528c5bc10cdf065722c9b64","actualBodySha256":"c3efa637340c6fb14916825e3303b27da6733d7a9528c5bc10cdf065722c9b64","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"github","id":"21","url":"https://github.com/hashintel/brunch-lite/pull/21","preFetchAt":"2026-08-20T11:07:42.941Z","frozenSourceUpdatedAt":"2026-08-19T14:00:32Z","sourceUpdatedAt":"2026-08-19T14:00:32Z","sourceTitleSha256":"c47a125d640ac02cb20d9907f82c7d3612121c3ba6b574f4fa7560dc2d4e30d7","sourceBodySha256":"037a322196d9f97ed586af85f637e9ea51eb4f9a2e19911b5285b5ea7dddfac4","proposedTitleSha256":"9b1c3bca7e522d63f672b120d90915ffef92a352e80ff04cfa830132675fbaf8","proposedBodySha256":"037a322196d9f97ed586af85f637e9ea51eb4f9a2e19911b5285b5ea7dddfac4"} -{"event":"result","status":"pass","system":"github","id":"21","url":"https://github.com/hashintel/brunch-lite/pull/21","postFetchAt":"2026-08-20T11:07:45.001Z","postUpdatedAt":"2026-08-20T11:07:44Z","proposedTitleSha256":"9b1c3bca7e522d63f672b120d90915ffef92a352e80ff04cfa830132675fbaf8","actualTitleSha256":"9b1c3bca7e522d63f672b120d90915ffef92a352e80ff04cfa830132675fbaf8","proposedBodySha256":"037a322196d9f97ed586af85f637e9ea51eb4f9a2e19911b5285b5ea7dddfac4","actualBodySha256":"037a322196d9f97ed586af85f637e9ea51eb4f9a2e19911b5285b5ea7dddfac4","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"github","id":"22","url":"https://github.com/hashintel/brunch-lite/pull/22","preFetchAt":"2026-08-20T11:07:45.382Z","frozenSourceUpdatedAt":"2026-08-19T14:00:35Z","sourceUpdatedAt":"2026-08-19T14:00:35Z","sourceTitleSha256":"ea8d78aaeef51c451235b2dcd2e5aef2459dfa218dae80928968f5499c0408ea","sourceBodySha256":"985fc49182ce7429b93462cb72bd5124b6f77a2c66a3f554e63ebd1c5a3a44b5","proposedTitleSha256":"a41b16dba248a6166529bffabfe03285094f31eb035eb04f7fdde9ca318f4fe2","proposedBodySha256":"0027d14716564d6aa96a13fa64814e364773ecaac6b3a5e2fd3fbf5cf5f51049"} -{"event":"result","status":"pass","system":"github","id":"22","url":"https://github.com/hashintel/brunch-lite/pull/22","postFetchAt":"2026-08-20T11:07:47.636Z","postUpdatedAt":"2026-08-20T11:07:46Z","proposedTitleSha256":"a41b16dba248a6166529bffabfe03285094f31eb035eb04f7fdde9ca318f4fe2","actualTitleSha256":"a41b16dba248a6166529bffabfe03285094f31eb035eb04f7fdde9ca318f4fe2","proposedBodySha256":"0027d14716564d6aa96a13fa64814e364773ecaac6b3a5e2fd3fbf5cf5f51049","actualBodySha256":"0027d14716564d6aa96a13fa64814e364773ecaac6b3a5e2fd3fbf5cf5f51049","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"github","id":"23","url":"https://github.com/hashintel/brunch-lite/pull/23","preFetchAt":"2026-08-20T11:07:48.029Z","frozenSourceUpdatedAt":"2026-08-19T14:00:37Z","sourceUpdatedAt":"2026-08-19T14:00:37Z","sourceTitleSha256":"51da4c3d51152014c2866fb0114f20a4ae2f32653b7a64a20149ee0cd63fa59e","sourceBodySha256":"c0634fcf88f0fb7c9428da627183b68b53d13023a55153fb1649230f5717ede3","proposedTitleSha256":"51da4c3d51152014c2866fb0114f20a4ae2f32653b7a64a20149ee0cd63fa59e","proposedBodySha256":"4a6f614020fc7358da54c5ee3c60b5be98b61a8b38a6ea36d6ddcbbcc948cd3f"} -{"event":"result","status":"pass","system":"github","id":"23","url":"https://github.com/hashintel/brunch-lite/pull/23","postFetchAt":"2026-08-20T11:07:50.024Z","postUpdatedAt":"2026-08-20T11:07:49Z","proposedTitleSha256":"51da4c3d51152014c2866fb0114f20a4ae2f32653b7a64a20149ee0cd63fa59e","actualTitleSha256":"51da4c3d51152014c2866fb0114f20a4ae2f32653b7a64a20149ee0cd63fa59e","proposedBodySha256":"4a6f614020fc7358da54c5ee3c60b5be98b61a8b38a6ea36d6ddcbbcc948cd3f","actualBodySha256":"4a6f614020fc7358da54c5ee3c60b5be98b61a8b38a6ea36d6ddcbbcc948cd3f","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"github","id":"24","url":"https://github.com/hashintel/brunch-lite/pull/24","preFetchAt":"2026-08-20T11:07:50.407Z","frozenSourceUpdatedAt":"2026-08-19T14:00:38Z","sourceUpdatedAt":"2026-08-19T14:00:38Z","sourceTitleSha256":"bd9e156289c48be8bc090b55ed791baa949ca60fa375428e1ce44cefdee10367","sourceBodySha256":"e1027892dd6c1a87afc8f7922fc51a997066da09baf710a15c05aa24a9c5c283","proposedTitleSha256":"de0d1aadfb9abaa4ddf82a39384235745c19d1059cc27cf1abd1cb163ceb67d1","proposedBodySha256":"240284375b0b085d4b4315a13ffb2eed87affa8b36f03c73757cb6f23fc841ca"} -{"event":"result","status":"pass","system":"github","id":"24","url":"https://github.com/hashintel/brunch-lite/pull/24","postFetchAt":"2026-08-20T11:07:52.516Z","postUpdatedAt":"2026-08-20T11:07:51Z","proposedTitleSha256":"de0d1aadfb9abaa4ddf82a39384235745c19d1059cc27cf1abd1cb163ceb67d1","actualTitleSha256":"de0d1aadfb9abaa4ddf82a39384235745c19d1059cc27cf1abd1cb163ceb67d1","proposedBodySha256":"240284375b0b085d4b4315a13ffb2eed87affa8b36f03c73757cb6f23fc841ca","actualBodySha256":"240284375b0b085d4b4315a13ffb2eed87affa8b36f03c73757cb6f23fc841ca","writeExitCode":0,"writeStderr":""} -{"event":"attempt","system":"github","id":"25","url":"https://github.com/hashintel/brunch-lite/pull/25","preFetchAt":"2026-08-20T11:07:52.940Z","frozenSourceUpdatedAt":"2026-08-20T08:46:31Z","sourceUpdatedAt":"2026-08-20T08:46:31Z","sourceTitleSha256":"92298a4ff0ba866ac7de95fa5ae22a2be4d911f4e3ef9db4a562987660d684bb","sourceBodySha256":"37dedf722f51fb6dd12e5e690a1f843040a5c84edf5d8ed8187ee386980d3851","proposedTitleSha256":"ef0946c7b52dd74664a885cb423b8c3571349fe4b24b9390bde97b05c235a710","proposedBodySha256":"dbb0116f134378a4b1b42607319fe88abf91f7b1c618ccaaea20fada9a24dc72"} -{"event":"result","status":"pass","system":"github","id":"25","url":"https://github.com/hashintel/brunch-lite/pull/25","postFetchAt":"2026-08-20T11:07:55.173Z","postUpdatedAt":"2026-08-20T11:07:54Z","proposedTitleSha256":"ef0946c7b52dd74664a885cb423b8c3571349fe4b24b9390bde97b05c235a710","actualTitleSha256":"ef0946c7b52dd74664a885cb423b8c3571349fe4b24b9390bde97b05c235a710","proposedBodySha256":"dbb0116f134378a4b1b42607319fe88abf91f7b1c618ccaaea20fada9a24dc72","actualBodySha256":"dbb0116f134378a4b1b42607319fe88abf91f7b1c618ccaaea20fada9a24dc72","writeExitCode":0,"writeStderr":""} -{"event":"final-reconciliation","status":"pass","reconciledAt":"2026-08-20T11:13:01.978Z","linearTargets":67,"linearExcluded":6,"githubTargets":25,"integrationUpdatedLinearIssues":["FE-1361","FE-1362","FE-1363","FE-1364","FE-1374","FE-1388","FE-1389","FE-1390","FE-1391","FE-1392","FE-1397","FE-1399","FE-1400","FE-1401","FE-1405","FE-1419","FE-1422","FE-1424","FE-1432","FE-1433","FE-1434","FE-1435","FE-1436","FE-1449"],"metadataUpdatedExcludedLinearIssues":["FE-1333"],"finalStateSha256":"57aa5ae047cd041d203301877a7f6fb8afb7de878ae1f5d101e39d7b84afa535"} -{"event":"final-reconciliation","status":"pass","reconciledAt":"2026-08-20T11:16:44.819Z","linearTargets":67,"linearExcluded":6,"githubTargets":25,"integrationUpdatedLinearIssues":["FE-1361","FE-1362","FE-1363","FE-1364","FE-1374","FE-1388","FE-1389","FE-1390","FE-1391","FE-1392","FE-1397","FE-1399","FE-1400","FE-1401","FE-1405","FE-1419","FE-1422","FE-1424","FE-1432","FE-1433","FE-1434","FE-1435","FE-1436","FE-1449"],"metadataUpdatedExcludedLinearIssues":["FE-1333"],"finalStateSha256":"57aa5ae047cd041d203301877a7f6fb8afb7de878ae1f5d101e39d7b84afa535"} diff --git a/libs/@hashintel/brunch-agent/docs/archive/migrations/issue-pr-legibility-2026-08-20/data/github-proposals.json b/libs/@hashintel/brunch-agent/docs/archive/migrations/issue-pr-legibility-2026-08-20/data/github-proposals.json deleted file mode 100644 index 70bd0a6fb33..00000000000 --- a/libs/@hashintel/brunch-agent/docs/archive/migrations/issue-pr-legibility-2026-08-20/data/github-proposals.json +++ /dev/null @@ -1,656 +0,0 @@ -{ - "generatedAt": "2026-08-20T09:19:39.051896Z", - "prCount": 25, - "prs": [ - { - "number": 25, - "url": "https://github.com/hashintel/brunch-lite/pull/25", - "state": "OPEN", - "headRefName": "ln/fe-1449-structured-ask", - "linkedIssue": "FE-1449", - "sourceUpdatedAt": "2026-08-20T08:46:31Z", - "sourceTitle": "FE-1449: A structured brunch question suspends and resumes visibly in Petrinaut", - "sourceTitleSha256": "92298a4ff0ba866ac7de95fa5ae22a2be4d911f4e3ef9db4a562987660d684bb", - "sourceBodySha256": "37dedf722f51fb6dd12e5e690a1f843040a5c84edf5d8ed8187ee386980d3851", - "proposedTitle": "FE-1449: Prove a structured brunch question suspends and resumes in Petrinaut", - "oldOuter": "", - "proposedOuter": "This branch keeps a structured brunch question open on the AI SDK wire and resumes the same Flue conversation when the matching human answer arrives through the committed application route. It validates the reply against durable conversation history and rejects stale, duplicate, forged, malformed, and machine-only submissions before dispatch. Petrinaut component registration through FE-1448 remains required for full acceptance.", - "extractionMethod": "whole source body treated as authoritative inner record and wrapped once", - "sourceInnerRecord": "## Stack Context\n\nFE-1433 sequences the Petrinaut integration from the proven Flue suspension and AI SDK seams. This branch is the ask suspend/return slice, stacked on FE-1436's durable transport; its panel-side counterpart is the FE-1448 host interactive-tool API in hashintel/hash (PR hashintel/hash#9249).\n\n## What?\n\n- `transport-aisdk` holds a `brunch_ask` open on the wire: awaiting client tool with a stable `toolCallId`, the harness's minted affordance output withheld\n- the tool-result follow-up POST — previously refused wholesale — is admitted exactly when it carries the pending ask's correlated `{ answer }` submission\n- `@brunch/core` gains the pure protocol: `pendingAskAffordanceId`, `decideAskReplyAdmission`, and the `AskSubmission` submitted-output contract\n- the application's new `askReply` seam admits against durable Flue history before any dispatch, then resumes the conversation as a fresh user dispatch that the binding binds as the user-affordance reply\n- opt-in inspection gains `ask-await`, `ask-reply-admitted`, `ask-reply-refused`\n\n## Why?\n\nFE-1449 requires provenance to settle at the wire boundary: a human answer travels tool-output-shaped but is not a machine tool result. Only the currently pending ask's correlated submission may become user evidence — stale/duplicate submissions get `409 ask_not_pending`, forged ids `409 ask_mismatch`, malformed submissions `400 invalid_ask_submission`, and machine-only follow-ups (mutation outputs, diagnostics) keep FE-1436's `422`. Concurrent duplicates collapse at the substrate via the `{conversationId}:ask:{toolCallId}` idempotency key.\n\nRemaining for full FE-1449 acceptance: petrinaut-website registers the ask component through the FE-1448 `aiAssistant` API (hash-side branch); the implementation record names it.\n\n## Verification\n\n- `bun run smoke` — lint, fmt, typecheck, 163 tests\n- `bun run build`\n- wire contract tests for the translated ask part and each refusal class\n- end-to-end run over the committed application route: the actual elicitor asks, the correlated answer resumes the same Flue conversation, a replayed duplicate is refused before dispatch\n", - "sourceInnerSha256": "37dedf722f51fb6dd12e5e690a1f843040a5c84edf5d8ed8187ee386980d3851", - "innerRecord": "## Stack Context\n\nFE-1433 sequences the Petrinaut integration from the proven Flue suspension and AI SDK seams. This branch is the ask suspend/return slice, stacked on FE-1436's durable transport; its panel-side counterpart is the FE-1448 host interactive-tool API in hashintel/hash (PR hashintel/hash#9249).\n\n## What?\n\n- `transport-aisdk` holds a `brunch_ask` open on the wire: awaiting client tool with a stable `toolCallId`, the harness's minted affordance output withheld\n- the tool-result follow-up POST — previously refused wholesale — is admitted exactly when it carries the pending ask's correlated `{ answer }` submission\n- `@brunch/core` gains the pure protocol: `pendingAskAffordanceId`, `decideAskReplyAdmission`, and the `AskSubmission` submitted-output contract\n- the application's new `askReply` seam admits against durable Flue history before any dispatch, then resumes the conversation as a fresh user dispatch that the binding binds as the user-affordance reply\n- opt-in inspection gains `ask-await`, `ask-reply-admitted`, `ask-reply-refused`\n\n## Why?\n\nFE-1449 requires provenance to settle at the wire boundary: a human answer travels tool-output-shaped but is not a machine tool result. Only the currently pending ask's correlated submission may become user evidence — stale/duplicate submissions get `409 ask_not_pending`, forged ids `409 ask_mismatch`, malformed submissions `400 invalid_ask_submission`, and machine-only follow-ups (mutation outputs, diagnostics) keep FE-1436's `422`. Concurrent duplicates collapse at the substrate via the `{conversationId}:ask:{toolCallId}` idempotency key.\n\nRemaining for full FE-1449 acceptance: petrinaut-website registers the ask component through the FE-1448 `aiAssistant` API (hash-side branch); the implementation record names it.\n\n## Verification\n\n- `bun run smoke` — lint, fmt, typecheck, 163 tests\n- `bun run build`\n- wire contract tests for the translated ask part and each refusal class\n- end-to-end run over the committed application route: the actual elicitor asks, the correlated answer resumes the same Flue conversation, a replayed duplicate is refused before dispatch\n", - "innerSha256": "37dedf722f51fb6dd12e5e690a1f843040a5c84edf5d8ed8187ee386980d3851", - "normalizationNotes": "Added one canonical Agent-notes wrapper around the unchanged source body.", - "proposedBody": "This branch keeps a structured brunch question open on the AI SDK wire and resumes the same Flue conversation when the matching human answer arrives through the committed application route. It validates the reply against durable conversation history and rejects stale, duplicate, forged, malformed, and machine-only submissions before dispatch. Petrinaut component registration through FE-1448 remains required for full acceptance.\n\n
🏗️ Agent notes\n\n## Stack Context\n\nFE-1433 sequences the Petrinaut integration from the proven Flue suspension and AI SDK seams. This branch is the ask suspend/return slice, stacked on FE-1436's durable transport; its panel-side counterpart is the FE-1448 host interactive-tool API in hashintel/hash (PR hashintel/hash#9249).\n\n## What?\n\n- `transport-aisdk` holds a `brunch_ask` open on the wire: awaiting client tool with a stable `toolCallId`, the harness's minted affordance output withheld\n- the tool-result follow-up POST — previously refused wholesale — is admitted exactly when it carries the pending ask's correlated `{ answer }` submission\n- `@brunch/core` gains the pure protocol: `pendingAskAffordanceId`, `decideAskReplyAdmission`, and the `AskSubmission` submitted-output contract\n- the application's new `askReply` seam admits against durable Flue history before any dispatch, then resumes the conversation as a fresh user dispatch that the binding binds as the user-affordance reply\n- opt-in inspection gains `ask-await`, `ask-reply-admitted`, `ask-reply-refused`\n\n## Why?\n\nFE-1449 requires provenance to settle at the wire boundary: a human answer travels tool-output-shaped but is not a machine tool result. Only the currently pending ask's correlated submission may become user evidence — stale/duplicate submissions get `409 ask_not_pending`, forged ids `409 ask_mismatch`, malformed submissions `400 invalid_ask_submission`, and machine-only follow-ups (mutation outputs, diagnostics) keep FE-1436's `422`. Concurrent duplicates collapse at the substrate via the `{conversationId}:ask:{toolCallId}` idempotency key.\n\nRemaining for full FE-1449 acceptance: petrinaut-website registers the ask component through the FE-1448 `aiAssistant` API (hash-side branch); the implementation record names it.\n\n## Verification\n\n- `bun run smoke` — lint, fmt, typecheck, 163 tests\n- `bun run build`\n- wire contract tests for the translated ask part and each refusal class\n- end-to-end run over the committed application route: the actual elicitor asks, the correlated answer resumes the same Flue conversation, a replayed duplicate is refused before dispatch\n\n\n
", - "proposedBodySha256": "dbb0116f134378a4b1b42607319fe88abf91f7b1c618ccaaea20fada9a24dc72", - "bodyChanged": true, - "titleChanged": true, - "ambiguity": null, - "notes": "The source had no Agent-notes wrapper, as specified for this migration." - }, - { - "number": 24, - "url": "https://github.com/hashintel/brunch-lite/pull/24", - "state": "OPEN", - "headRefName": "ln/fe-1436-transport-aisdk", - "linkedIssue": "FE-1436", - "sourceUpdatedAt": "2026-08-19T14:00:38Z", - "sourceTitle": "FE-1436: The elicitor answers conversation turns in Petrinaut's real chat panel", - "sourceTitleSha256": "bd9e156289c48be8bc090b55ed791baa949ca60fa375428e1ce44cefdee10367", - "sourceBodySha256": "e1027892dd6c1a87afc8f7922fc51a997066da09baf710a15c05aa24a9c5c283", - "proposedTitle": "FE-1436: Connect the elicitor to Petrinaut’s real chat panel", - "oldOuter": "The spikes proved the required seams separately, but the elicitor still needed to answer real chat turns in the target panel. This branch connects that path and preserves the evidence needed to verify completed, failed, and cancelled turns.", - "proposedOuter": "Separate spikes proved Flue suspension and AI SDK panel streaming, but the elicitor still needed to answer real chat turns in the target panel. This branch connects that path and preserves the evidence needed to verify completed, failed, and cancelled turns.", - "extractionMethod": "single canonical wrapper extracted", - "sourceInnerRecord": "## Stack Context\n\nFE-1433 sequences a durable Petrinaut integration from the proven Flue suspension and AI SDK panel seams. This branch is the first production-intent transport slice, stacked on FE-1435.\n\n## What?\n\n- adds a substrate-neutral harness reply protocol and `transport-aisdk` encoder\n- mounts the actual elicitor behind Petrinaut's `/api/chat` contract\n- commits the local Petrinaut launcher, opt-in JSONL inspector, and wire fixtures\n- validates external chat requests with Valibot and keeps transport diagnostics outside user evidence\n- records one truthful terminal sequence for completed, failed, and aborted turns\n\n## Why?\n\nLater structured-ask and editor-tool slices need a durable, inspectable server path rather than the spike's disposable replay harness. The committed application-route golden now crosses the real application, Flue projector, and transport while normalizing only dynamic identifiers and delta segmentation.\n\n## Verification\n\n- `bun run lint`\n- `bun run fmt`\n- `bun run typecheck`\n- `bun test` — 152 passed, 0 failed\n- `bun run build`\n- independent standards/spec review plus focused remediation closure review", - "sourceInnerSha256": "10e4f1c874197ab56e237d4d4ffbe11ff3b8a286a3b0a7f3036cdbc4a23ff8aa", - "innerRecord": "## Stack Context\n\nFE-1433 sequences a durable Petrinaut integration from the proven Flue suspension and AI SDK panel seams. This branch is the first production-intent transport slice, stacked on FE-1435.\n\n## What?\n\n- adds a substrate-neutral harness reply protocol and `transport-aisdk` encoder\n- mounts the actual elicitor behind Petrinaut's `/api/chat` contract\n- commits the local Petrinaut launcher, opt-in JSONL inspector, and wire fixtures\n- validates external chat requests with Valibot and keeps transport diagnostics outside user evidence\n- records one truthful terminal sequence for completed, failed, and aborted turns\n\n## Why?\n\nLater structured-ask and editor-tool slices need a durable, inspectable server path rather than the spike's disposable replay harness. The committed application-route golden now crosses the real application, Flue projector, and transport while normalizing only dynamic identifiers and delta segmentation.\n\n## Verification\n\n- `bun run lint`\n- `bun run fmt`\n- `bun run typecheck`\n- `bun test` — 152 passed, 0 failed\n- `bun run build`\n- independent standards/spec review plus focused remediation closure review", - "innerSha256": "10e4f1c874197ab56e237d4d4ffbe11ff3b8a286a3b0a7f3036cdbc4a23ff8aa", - "normalizationNotes": "The inner record is unchanged; the outer was revised to remove an avoidable banned-word use.", - "proposedBody": "Separate spikes proved Flue suspension and AI SDK panel streaming, but the elicitor still needed to answer real chat turns in the target panel. This branch connects that path and preserves the evidence needed to verify completed, failed, and cancelled turns.\n\n
🏗️ Agent notes\n\n## Stack Context\n\nFE-1433 sequences a durable Petrinaut integration from the proven Flue suspension and AI SDK panel seams. This branch is the first production-intent transport slice, stacked on FE-1435.\n\n## What?\n\n- adds a substrate-neutral harness reply protocol and `transport-aisdk` encoder\n- mounts the actual elicitor behind Petrinaut's `/api/chat` contract\n- commits the local Petrinaut launcher, opt-in JSONL inspector, and wire fixtures\n- validates external chat requests with Valibot and keeps transport diagnostics outside user evidence\n- records one truthful terminal sequence for completed, failed, and aborted turns\n\n## Why?\n\nLater structured-ask and editor-tool slices need a durable, inspectable server path rather than the spike's disposable replay harness. The committed application-route golden now crosses the real application, Flue projector, and transport while normalizing only dynamic identifiers and delta segmentation.\n\n## Verification\n\n- `bun run lint`\n- `bun run fmt`\n- `bun run typecheck`\n- `bun test` — 152 passed, 0 failed\n- `bun run build`\n- independent standards/spec review plus focused remediation closure review\n\n
", - "proposedBodySha256": "240284375b0b085d4b4315a13ffb2eed87affa8b36f03c73757cb6f23fc841ca", - "bodyChanged": true, - "titleChanged": true, - "ambiguity": null, - "notes": "Replaced the unspecific “seams” with the proved mechanisms: Flue suspension and AI SDK panel streaming." - }, - { - "number": 23, - "url": "https://github.com/hashintel/brunch-lite/pull/23", - "state": "OPEN", - "headRefName": "ln/fe-1451-fold-protocol", - "linkedIssue": "FE-1451", - "sourceUpdatedAt": "2026-08-19T14:00:37Z", - "sourceTitle": "FE-1451: Keep issues, comments, and PRs easy to scan", - "sourceTitleSha256": "51da4c3d51152014c2866fb0114f20a4ae2f32653b7a64a20149ee0cd63fa59e", - "sourceBodySha256": "c0634fcf88f0fb7c9428da627183b68b53d13023a55153fb1649230f5717ede3", - "proposedTitle": "FE-1451: Keep issues, comments, and PRs easy to scan", - "oldOuter": "Agent-written issues, comments, and PR descriptions had become hard to scan. This branch sets a compact title format and a plain-language summary above collapsed agent working detail, then applies the rules to the issue that introduced them.", - "proposedOuter": "Agent-written issues, comments, and PR descriptions had become hard to scan. This branch sets a compact title format and a plain-language summary above collapsed agent working detail, then applies the rules to the issue that introduced them.", - "extractionMethod": "outer canonical wrapper extracted; nested standalone wrapper tags removed from source inner", - "sourceInnerRecord": "Agent-written issues, comments, and PR descriptions had become hard to scan. This PR gives issues compact, active-verb task titles and a human-owned plain-language summary, with agent working detail in a collapsed `🏗️ Agent notes` section. It applies the same rules to [FE-1451](https://linear.app/hash/issue/FE-1451/keep-issues-comments-and-prs-easy-to-scan), which serves as a live example.\n\n
🏗️ Agent notes\n\n## What changed\n\n- `docs/agents/issue-writing.md` defines compact task-shaped titles, the collapsed section, visible comments limited to one decision or change, and vocabulary rules by audience.\n- The human driving the work owns the visible summary. An agent fetches and preserves that text before writing on the person's behalf; `🏗️ Agent notes` remains agent-maintained.\n- Issues quote and link user, stakeholder, or teammate feedback when the issue's audience can read the source.\n- The org technical-writing rules apply to all prose. The earlier draft applied the word list only to visible summaries.\n- The coined name \"the fold\" was removed rather than added to the glossary. The house word list asks writers to use \"collapsed section,\" and formalizing the coined term would have preserved the conflict.\n- `docs/agents/issue-tracker.md` records the safe raw-description edit process and the shorter project-update format.\n- `AGENTS.md`, `docs/INDEX.md`, and `docs/planning/_shared/CONVERGENCE.md` point to the current rules and state.\n\n## Verification\n\n- `bun run smoke`: 138 tests passed.\n- `bun run build`: passed.\n- Focused document-index and formatting checks passed after the final wording changes.\n- Linear round-trip: FE-1451 retains its human-owned summary and collapsed `🏗️ Agent notes` after update and read-back.\n\n## Arc close\n\n- Inbox sweep: clean.\n- INDEX pass: the CONVERGENCE digest matches the seventh evaluation.\n- CONVERGENCE re-evaluation: FE-1451 changes reporting and does not change the delivery order.\n- Registry audit: FE-1451 is a child of FE-1401; this branch introduces no orphan.\n- Tense repair: the living record says the rules are implemented on this branch.\n- Plain-language review: the required forked reviewer could not start because the subagent service failed twice. A manual strain read found and repaired the coined term, the partial application of the house word list, and the unsupported claim that an audit already checks the label bytes.\n\n
", - "sourceInnerSha256": "7d0c7197002a2bd29907e73de006782e60a3104da9f54a8082d12c3d88fc3899", - "innerRecord": "Agent-written issues, comments, and PR descriptions had become hard to scan. This PR gives issues compact, active-verb task titles and a human-owned plain-language summary, with agent working detail in a collapsed `🏗️ Agent notes` section. It applies the same rules to [FE-1451](https://linear.app/hash/issue/FE-1451/keep-issues-comments-and-prs-easy-to-scan), which serves as a live example.\n\n\n## What changed\n\n- `docs/agents/issue-writing.md` defines compact task-shaped titles, the collapsed section, visible comments limited to one decision or change, and vocabulary rules by audience.\n- The human driving the work owns the visible summary. An agent fetches and preserves that text before writing on the person's behalf; `🏗️ Agent notes` remains agent-maintained.\n- Issues quote and link user, stakeholder, or teammate feedback when the issue's audience can read the source.\n- The org technical-writing rules apply to all prose. The earlier draft applied the word list only to visible summaries.\n- The coined name \"the fold\" was removed rather than added to the glossary. The house word list asks writers to use \"collapsed section,\" and formalizing the coined term would have preserved the conflict.\n- `docs/agents/issue-tracker.md` records the safe raw-description edit process and the shorter project-update format.\n- `AGENTS.md`, `docs/INDEX.md`, and `docs/planning/_shared/CONVERGENCE.md` point to the current rules and state.\n\n## Verification\n\n- `bun run smoke`: 138 tests passed.\n- `bun run build`: passed.\n- Focused document-index and formatting checks passed after the final wording changes.\n- Linear round-trip: FE-1451 retains its human-owned summary and collapsed `🏗️ Agent notes` after update and read-back.\n\n## Arc close\n\n- Inbox sweep: clean.\n- INDEX pass: the CONVERGENCE digest matches the seventh evaluation.\n- CONVERGENCE re-evaluation: FE-1451 changes reporting and does not change the delivery order.\n- Registry audit: FE-1451 is a child of FE-1401; this branch introduces no orphan.\n- Tense repair: the living record says the rules are implemented on this branch.\n- Plain-language review: the required forked reviewer could not start because the subagent service failed twice. A manual strain read found and repaired the coined term, the partial application of the house word list, and the unsupported claim that an audit already checks the label bytes.\n\n", - "innerSha256": "14c689899a8f5687c0f26293949b64974adc409de4379339d847a0356991b723", - "normalizationNotes": "Removed the nested standalone details opener and closer only. All non-structural bytes remain in source order; the final body has one canonical wrapper.", - "proposedBody": "Agent-written issues, comments, and PR descriptions had become hard to scan. This branch sets a compact title format and a plain-language summary above collapsed agent working detail, then applies the rules to the issue that introduced them.\n\n
🏗️ Agent notes\n\nAgent-written issues, comments, and PR descriptions had become hard to scan. This PR gives issues compact, active-verb task titles and a human-owned plain-language summary, with agent working detail in a collapsed `🏗️ Agent notes` section. It applies the same rules to [FE-1451](https://linear.app/hash/issue/FE-1451/keep-issues-comments-and-prs-easy-to-scan), which serves as a live example.\n\n\n## What changed\n\n- `docs/agents/issue-writing.md` defines compact task-shaped titles, the collapsed section, visible comments limited to one decision or change, and vocabulary rules by audience.\n- The human driving the work owns the visible summary. An agent fetches and preserves that text before writing on the person's behalf; `🏗️ Agent notes` remains agent-maintained.\n- Issues quote and link user, stakeholder, or teammate feedback when the issue's audience can read the source.\n- The org technical-writing rules apply to all prose. The earlier draft applied the word list only to visible summaries.\n- The coined name \"the fold\" was removed rather than added to the glossary. The house word list asks writers to use \"collapsed section,\" and formalizing the coined term would have preserved the conflict.\n- `docs/agents/issue-tracker.md` records the safe raw-description edit process and the shorter project-update format.\n- `AGENTS.md`, `docs/INDEX.md`, and `docs/planning/_shared/CONVERGENCE.md` point to the current rules and state.\n\n## Verification\n\n- `bun run smoke`: 138 tests passed.\n- `bun run build`: passed.\n- Focused document-index and formatting checks passed after the final wording changes.\n- Linear round-trip: FE-1451 retains its human-owned summary and collapsed `🏗️ Agent notes` after update and read-back.\n\n## Arc close\n\n- Inbox sweep: clean.\n- INDEX pass: the CONVERGENCE digest matches the seventh evaluation.\n- CONVERGENCE re-evaluation: FE-1451 changes reporting and does not change the delivery order.\n- Registry audit: FE-1451 is a child of FE-1401; this branch introduces no orphan.\n- Tense repair: the living record says the rules are implemented on this branch.\n- Plain-language review: the required forked reviewer could not start because the subagent service failed twice. A manual strain read found and repaired the coined term, the partial application of the house word list, and the unsupported claim that an audit already checks the label bytes.\n\n\n\n
", - "proposedBodySha256": "4a6f614020fc7358da54c5ee3c60b5be98b61a8b38a6ea36d6ddcbbcc948cd3f", - "bodyChanged": true, - "titleChanged": false, - "ambiguity": null, - "notes": "Raw source-inner and normalized-inner hashes are both recorded." - }, - { - "number": 22, - "url": "https://github.com/hashintel/brunch-lite/pull/22", - "state": "OPEN", - "headRefName": "ln/fe-1435-aisdk-panel-spike", - "linkedIssue": "FE-1435", - "sourceUpdatedAt": "2026-08-19T14:00:35Z", - "sourceTitle": "FE-1435: Spike: does a harness-driven stream drive Petrinaut's real chat panel?", - "sourceTitleSha256": "ea8d78aaeef51c451235b2dcd2e5aef2459dfa218dae80928968f5499c0408ea", - "sourceBodySha256": "985fc49182ce7429b93462cb72bd5124b6f77a2c66a3f554e63ebd1c5a3a44b5", - "proposedTitle": "FE-1435: Test whether the elicitor stream drives Petrinaut’s chat panel", - "oldOuter": "The integration needed proof that the real chat panel accepts the stream produced by the harness. This spike exercises that path and records the resulting wire evidence before the production slice builds on it.", - "proposedOuter": "The integration needed proof that the real chat panel accepts the stream produced by the harness. This spike exercises that path and records the resulting wire evidence before the production integration builds on it.", - "extractionMethod": "single canonical wrapper extracted", - "sourceInnerRecord": "## Outcome\n\nProves that harness-level parts translated to AI SDK v6 UI-message-stream frames can drive Petrinaut's real, unmodified chat panel at the pinned dependency versions.\n\nThe live panel rendered reasoning, text, and a provider-executed server tool; executed two client tools against the editor; posted both outputs together in one automatic follow-up; and added diagnostics context through its existing transport decorator. The complete two POST bodies and two SSE streams are frozen as fixtures with semantic golden tests.\n\nThe external `hashintel/hash` checkout remained clean at `1046b5c881cd00cf205b4895348b022934d66b4a`; no prototype source was retained.\n\n## Verification\n\n- `bun run lint`\n- `bun run fmt`\n- `bun run typecheck`\n- `bun test` (142 tests)\n- `bun run build`", - "sourceInnerSha256": "9e78b931b4c73c4fa061c49db1121b77958f68707520b3ba33ffeffba819e3fe", - "innerRecord": "## Outcome\n\nProves that harness-level parts translated to AI SDK v6 UI-message-stream frames can drive Petrinaut's real, unmodified chat panel at the pinned dependency versions.\n\nThe live panel rendered reasoning, text, and a provider-executed server tool; executed two client tools against the editor; posted both outputs together in one automatic follow-up; and added diagnostics context through its existing transport decorator. The complete two POST bodies and two SSE streams are frozen as fixtures with semantic golden tests.\n\nThe external `hashintel/hash` checkout remained clean at `1046b5c881cd00cf205b4895348b022934d66b4a`; no prototype source was retained.\n\n## Verification\n\n- `bun run lint`\n- `bun run fmt`\n- `bun run typecheck`\n- `bun test` (142 tests)\n- `bun run build`", - "innerSha256": "9e78b931b4c73c4fa061c49db1121b77958f68707520b3ba33ffeffba819e3fe", - "normalizationNotes": "The inner record is unchanged; the outer was revised to remove an avoidable banned-word use.", - "proposedBody": "The integration needed proof that the real chat panel accepts the stream produced by the harness. This spike exercises that path and records the resulting wire evidence before the production integration builds on it.\n\n
🏗️ Agent notes\n\n## Outcome\n\nProves that harness-level parts translated to AI SDK v6 UI-message-stream frames can drive Petrinaut's real, unmodified chat panel at the pinned dependency versions.\n\nThe live panel rendered reasoning, text, and a provider-executed server tool; executed two client tools against the editor; posted both outputs together in one automatic follow-up; and added diagnostics context through its existing transport decorator. The complete two POST bodies and two SSE streams are frozen as fixtures with semantic golden tests.\n\nThe external `hashintel/hash` checkout remained clean at `1046b5c881cd00cf205b4895348b022934d66b4a`; no prototype source was retained.\n\n## Verification\n\n- `bun run lint`\n- `bun run fmt`\n- `bun run typecheck`\n- `bun test` (142 tests)\n- `bun run build`\n\n
", - "proposedBodySha256": "0027d14716564d6aa96a13fa64814e364773ecaac6b3a5e2fd3fbf5cf5f51049", - "bodyChanged": true, - "titleChanged": true, - "ambiguity": null, - "notes": "Replaced the figurative “slice” with “production integration”." - }, - { - "number": 21, - "url": "https://github.com/hashintel/brunch-lite/pull/21", - "state": "OPEN", - "headRefName": "ln/fe-1434-suspension-spike", - "linkedIssue": "FE-1434", - "sourceUpdatedAt": "2026-08-19T14:00:32Z", - "sourceTitle": "FE-1434: Spike: does Flue turn suspension carry batched client-tool round-trips?", - "sourceTitleSha256": "c47a125d640ac02cb20d9907f82c7d3612121c3ba6b574f4fa7560dc2d4e30d7", - "sourceBodySha256": "037a322196d9f97ed586af85f637e9ea51eb4f9a2e19911b5285b5ea7dddfac4", - "proposedTitle": "FE-1434: Test whether Flue resumes batched client-tool results", - "oldOuter": "The planned integration depends on whether suspended turns can resume with several client-tool results together. This spike answers that question with recorded evidence and identifies the remaining obligations.", - "proposedOuter": "The planned integration depends on whether suspended turns can resume with several client-tool results together. This spike answers that question with recorded evidence and identifies the remaining obligations.", - "extractionMethod": "single canonical wrapper extracted", - "sourceInnerRecord": "## Outcome\n\nProves that Flue 2.0.3 can terminate with a batched client-tool interaction pending and resume through one later non-user `signal` dispatch with per-session state and every tool-call ID intact.\n\nThe durable verdict selects a batch-as-pending-slot variant. It records 3-result and 100-result evidence, two dispatches and two model turns in both cases, rejection of native `tool-result` admission, non-user capture-store refusal, and the remaining retry/mismatch obligations for FE-1438.\n\nThe disposable probe was removed after capture; the verdict and normalized JSON evidence retain its source commit and exact command.\n\n## Verification\n\n- `bun run lint`\n- `bun run fmt`\n- `bun run typecheck`\n- `bun test` (138 tests)\n- `bun run build`", - "sourceInnerSha256": "75374819449c6180ad66bdbce8100715a814735ed9812ee909cbe7de36831b0a", - "innerRecord": "## Outcome\n\nProves that Flue 2.0.3 can terminate with a batched client-tool interaction pending and resume through one later non-user `signal` dispatch with per-session state and every tool-call ID intact.\n\nThe durable verdict selects a batch-as-pending-slot variant. It records 3-result and 100-result evidence, two dispatches and two model turns in both cases, rejection of native `tool-result` admission, non-user capture-store refusal, and the remaining retry/mismatch obligations for FE-1438.\n\nThe disposable probe was removed after capture; the verdict and normalized JSON evidence retain its source commit and exact command.\n\n## Verification\n\n- `bun run lint`\n- `bun run fmt`\n- `bun run typecheck`\n- `bun test` (138 tests)\n- `bun run build`", - "innerSha256": "75374819449c6180ad66bdbce8100715a814735ed9812ee909cbe7de36831b0a", - "normalizationNotes": "None; the canonical body and inner record are preserved byte-for-byte.", - "proposedBody": "The planned integration depends on whether suspended turns can resume with several client-tool results together. This spike answers that question with recorded evidence and identifies the remaining obligations.\n\n
🏗️ Agent notes\n\n## Outcome\n\nProves that Flue 2.0.3 can terminate with a batched client-tool interaction pending and resume through one later non-user `signal` dispatch with per-session state and every tool-call ID intact.\n\nThe durable verdict selects a batch-as-pending-slot variant. It records 3-result and 100-result evidence, two dispatches and two model turns in both cases, rejection of native `tool-result` admission, non-user capture-store refusal, and the remaining retry/mismatch obligations for FE-1438.\n\nThe disposable probe was removed after capture; the verdict and normalized JSON evidence retain its source commit and exact command.\n\n## Verification\n\n- `bun run lint`\n- `bun run fmt`\n- `bun run typecheck`\n- `bun test` (138 tests)\n- `bun run build`\n\n
\n", - "proposedBodySha256": "037a322196d9f97ed586af85f637e9ea51eb4f9a2e19911b5285b5ea7dddfac4", - "bodyChanged": false, - "titleChanged": true, - "ambiguity": null, - "notes": "The existing outer meets the house style; only the title changes." - }, - { - "number": 20, - "url": "https://github.com/hashintel/brunch-lite/pull/20", - "state": "OPEN", - "headRefName": "ln/fe-1433-petrinaut-integration-spec", - "linkedIssue": "FE-1433", - "sourceUpdatedAt": "2026-08-19T14:00:30Z", - "sourceTitle": "FE-1433: The elicitor serves demo.petrinaut.org's chat panel from a remote brunch-agent server", - "sourceTitleSha256": "f994067680a1d677c1289baa403f1b2db4452f64845198006fd1c337648d7e81", - "sourceBodySha256": "c3efa637340c6fb14916825e3303b27da6733d7a9528c5bc10cdf065722c9b64", - "proposedTitle": "FE-1433: Deliver the remote Petrinaut elicitor integration", - "oldOuter": "The demo needed a settled integration direction before implementation could proceed. This branch records the decision to serve the existing chat panel from a remote brunch-agent server and defines the delivery work that follows.", - "proposedOuter": "The demo needed a settled integration direction before implementation could proceed. This branch records the decision to serve the existing chat panel from a remote brunch-agent server and defines the delivery work that follows.", - "extractionMethod": "single canonical wrapper extracted", - "sourceInnerRecord": "FE-1433: Record the in-Petrinaut staging decision and its integration spec\n\nThe 2026-08-18 integration meeting overturned the demo-shell recommendation:\nthe September demo stages inside demo.petrinaut.org, with the elicitor as a\nremote server behind the chat panel's host-pluggable aiAssistant transport.\nADR-0004 records the decision (staging, the @hashintel/brunch-agent rename and\nmonorepo import, the apps-are-the-only-meeting-point boundary discipline, the\nui-shell principal, the N3 amendment retiring the demo shell) and supersedes\nrecommendation-demo-vehicle.md.\n\nThe integration spec (FE-1433) specifies the build: one primary test seam (the\nAI SDK UI-message-stream wire contract), the external-tool round-trip protocol\nsharing the ask protocol's suspension floor while differing in cardinality,\nbinding, and provenance (tool outputs are machine entries, never anchorable\nevidence), the transport-aisdk package, the opaque owner key on the storage\nport, and the two gating spikes (Flue suspension carrying batched client-tool\nround-trips; the Pi-to-AI-SDK stream adapter, whose transcript becomes the\nwire seam's golden fixtures).\n\nINDEX updated: recommendation-demo-vehicle marked superseded; both new\ndocuments indexed; docs gate green.\n\nCo-Authored-By: Claude Fable 5 \n\nFE-1433: File the integration delivery graph — sixth cross-map evaluation\n\nThe ds-write-tickets pass over the integration spec published nine execution\ntickets under FE-1433 (FE-1434–FE-1442) with native blocking relations, and\nthis change deposits the coordination record: the sixth evaluation paragraph\n(the sub-graph joins the strategy without displacing the spine; the spikes\njoin the now-band beside FE-1393/FE-1422), the delivery graph itself (mirrored\nin FE-1433's execution record), the FE-1423 re-scope (gates survive the demo\nshell's retirement and now guard the remote elicitor server; auth → FE-1439,\ndeployment → FE-1441), and the N3 watch-item repair (ADR-0004 amended N3, so\nthe living-prototype charter would shape apps/dev or the deployed server, not\nan apps/demo).\n\nThe one fog-sensitive edge is recorded as such: FE-1438 and FE-1395 share the\npending-slot batching ground, held apart by a coordination edge rather than a\nblocker — whichever lands first writes the §7.3/§7.4 amendment.\n\nCo-Authored-By: Claude Fable 5 ", - "sourceInnerSha256": "9ca3adac2b7d307ec1440ef7d27bd38111d069641c9c688cd9a931f3e2af7ce9", - "innerRecord": "FE-1433: Record the in-Petrinaut staging decision and its integration spec\n\nThe 2026-08-18 integration meeting overturned the demo-shell recommendation:\nthe September demo stages inside demo.petrinaut.org, with the elicitor as a\nremote server behind the chat panel's host-pluggable aiAssistant transport.\nADR-0004 records the decision (staging, the @hashintel/brunch-agent rename and\nmonorepo import, the apps-are-the-only-meeting-point boundary discipline, the\nui-shell principal, the N3 amendment retiring the demo shell) and supersedes\nrecommendation-demo-vehicle.md.\n\nThe integration spec (FE-1433) specifies the build: one primary test seam (the\nAI SDK UI-message-stream wire contract), the external-tool round-trip protocol\nsharing the ask protocol's suspension floor while differing in cardinality,\nbinding, and provenance (tool outputs are machine entries, never anchorable\nevidence), the transport-aisdk package, the opaque owner key on the storage\nport, and the two gating spikes (Flue suspension carrying batched client-tool\nround-trips; the Pi-to-AI-SDK stream adapter, whose transcript becomes the\nwire seam's golden fixtures).\n\nINDEX updated: recommendation-demo-vehicle marked superseded; both new\ndocuments indexed; docs gate green.\n\nCo-Authored-By: Claude Fable 5 \n\nFE-1433: File the integration delivery graph — sixth cross-map evaluation\n\nThe ds-write-tickets pass over the integration spec published nine execution\ntickets under FE-1433 (FE-1434–FE-1442) with native blocking relations, and\nthis change deposits the coordination record: the sixth evaluation paragraph\n(the sub-graph joins the strategy without displacing the spine; the spikes\njoin the now-band beside FE-1393/FE-1422), the delivery graph itself (mirrored\nin FE-1433's execution record), the FE-1423 re-scope (gates survive the demo\nshell's retirement and now guard the remote elicitor server; auth → FE-1439,\ndeployment → FE-1441), and the N3 watch-item repair (ADR-0004 amended N3, so\nthe living-prototype charter would shape apps/dev or the deployed server, not\nan apps/demo).\n\nThe one fog-sensitive edge is recorded as such: FE-1438 and FE-1395 share the\npending-slot batching ground, held apart by a coordination edge rather than a\nblocker — whichever lands first writes the §7.3/§7.4 amendment.\n\nCo-Authored-By: Claude Fable 5 ", - "innerSha256": "9ca3adac2b7d307ec1440ef7d27bd38111d069641c9c688cd9a931f3e2af7ce9", - "normalizationNotes": "None; the canonical body and inner record are preserved byte-for-byte.", - "proposedBody": "The demo needed a settled integration direction before implementation could proceed. This branch records the decision to serve the existing chat panel from a remote brunch-agent server and defines the delivery work that follows.\n\n
🏗️ Agent notes\n\nFE-1433: Record the in-Petrinaut staging decision and its integration spec\n\nThe 2026-08-18 integration meeting overturned the demo-shell recommendation:\nthe September demo stages inside demo.petrinaut.org, with the elicitor as a\nremote server behind the chat panel's host-pluggable aiAssistant transport.\nADR-0004 records the decision (staging, the @hashintel/brunch-agent rename and\nmonorepo import, the apps-are-the-only-meeting-point boundary discipline, the\nui-shell principal, the N3 amendment retiring the demo shell) and supersedes\nrecommendation-demo-vehicle.md.\n\nThe integration spec (FE-1433) specifies the build: one primary test seam (the\nAI SDK UI-message-stream wire contract), the external-tool round-trip protocol\nsharing the ask protocol's suspension floor while differing in cardinality,\nbinding, and provenance (tool outputs are machine entries, never anchorable\nevidence), the transport-aisdk package, the opaque owner key on the storage\nport, and the two gating spikes (Flue suspension carrying batched client-tool\nround-trips; the Pi-to-AI-SDK stream adapter, whose transcript becomes the\nwire seam's golden fixtures).\n\nINDEX updated: recommendation-demo-vehicle marked superseded; both new\ndocuments indexed; docs gate green.\n\nCo-Authored-By: Claude Fable 5 \n\nFE-1433: File the integration delivery graph — sixth cross-map evaluation\n\nThe ds-write-tickets pass over the integration spec published nine execution\ntickets under FE-1433 (FE-1434–FE-1442) with native blocking relations, and\nthis change deposits the coordination record: the sixth evaluation paragraph\n(the sub-graph joins the strategy without displacing the spine; the spikes\njoin the now-band beside FE-1393/FE-1422), the delivery graph itself (mirrored\nin FE-1433's execution record), the FE-1423 re-scope (gates survive the demo\nshell's retirement and now guard the remote elicitor server; auth → FE-1439,\ndeployment → FE-1441), and the N3 watch-item repair (ADR-0004 amended N3, so\nthe living-prototype charter would shape apps/dev or the deployed server, not\nan apps/demo).\n\nThe one fog-sensitive edge is recorded as such: FE-1438 and FE-1395 share the\npending-slot batching ground, held apart by a coordination edge rather than a\nblocker — whichever lands first writes the §7.3/§7.4 amendment.\n\nCo-Authored-By: Claude Fable 5 \n\n
\n", - "proposedBodySha256": "c3efa637340c6fb14916825e3303b27da6733d7a9528c5bc10cdf065722c9b64", - "bodyChanged": false, - "titleChanged": true, - "ambiguity": null, - "notes": "The existing outer meets the house style; only the title changes." - }, - { - "number": 19, - "url": "https://github.com/hashintel/brunch-lite/pull/19", - "state": "OPEN", - "headRefName": "ln/fe-1432-review-remediation", - "linkedIssue": "FE-1432", - "sourceUpdatedAt": "2026-08-19T14:00:29Z", - "sourceTitle": "FE-1432: The stack's open review threads are adjudicated: fixed, owned, or refused on the record", - "sourceTitleSha256": "8baa3f7952c774fe17d2c91f5700063fa8e11047b193813df55b11766fee8eab", - "sourceBodySha256": "ae7d1b251d8e1f3547dc5b55930a4e1372bd97f9af952c7e8d3e2319d46c0172", - "proposedTitle": "FE-1432: Resolve the stack's open review threads", - "oldOuter": "The stack had review findings without a clear final disposition. This branch resolves them by fixing the actionable cases and recording ownership or a supported refusal for the rest.", - "proposedOuter": "The stack had review findings without a clear final disposition. This branch resolves them by fixing the actionable cases and recording ownership or a supported refusal for the rest.", - "extractionMethod": "single canonical wrapper extracted", - "sourceInnerRecord": "FE-1432 executes the 2026-08-18 cross-stack review-remediation queue and closes its capture channel.\n\nWhat this establishes:\n\n- Capture-store command admission and persisted parsing share the same closure rules: one terminal event per issue, pairwise-disjoint open conflicts, and active references for every open conflict.\n- Baseline interviewer and expert truncation survives in checkpoint metadata; partial output stops before downstream model consumption, and expert resume regenerates the incomplete reply.\n- Valibot declarations and imports agree in both directions, while the walking-skeleton oracle reports absent context as `false` instead of throwing.\n- New dependency resolutions observe a seven-day release-age quarantine.\n- All 15 residual review threads were replied to and resolved: 7 fixes, 2 findings owned by FE-1385/FE-1393, and 6 evidence-backed refusals.\n- The three tooling-side graduation proposals remain owned by FE-1401's `ds-induct`/lens-registry lane.\n\nCommits:\n\n- `380411f` — preserve capture-store closure and prove local-store setup\n- `f71aa9e` — retain baseline completion metadata\n- `bb46941` — make capability and walking-skeleton gates honest\n- `2814090` — quarantine fresh dependency releases\n- `74b535c` — settle the remediation ledger\n\nVerification: OxLint, Oxfmt, TypeScript, 138 tests, workspace build, and an independent adversarial review of the capture lifecycle. A live review-thread refresh found zero unresolved FE-1432 threads; the sole repository-wide unresolved thread is on child PR #20 and belongs to FE-1433.", - "sourceInnerSha256": "19f5f98e93e5519ab3b1bc2ece5499a459022157087eec5015c58a9c56d17d6b", - "innerRecord": "FE-1432 executes the 2026-08-18 cross-stack review-remediation queue and closes its capture channel.\n\nWhat this establishes:\n\n- Capture-store command admission and persisted parsing share the same closure rules: one terminal event per issue, pairwise-disjoint open conflicts, and active references for every open conflict.\n- Baseline interviewer and expert truncation survives in checkpoint metadata; partial output stops before downstream model consumption, and expert resume regenerates the incomplete reply.\n- Valibot declarations and imports agree in both directions, while the walking-skeleton oracle reports absent context as `false` instead of throwing.\n- New dependency resolutions observe a seven-day release-age quarantine.\n- All 15 residual review threads were replied to and resolved: 7 fixes, 2 findings owned by FE-1385/FE-1393, and 6 evidence-backed refusals.\n- The three tooling-side graduation proposals remain owned by FE-1401's `ds-induct`/lens-registry lane.\n\nCommits:\n\n- `380411f` — preserve capture-store closure and prove local-store setup\n- `f71aa9e` — retain baseline completion metadata\n- `bb46941` — make capability and walking-skeleton gates honest\n- `2814090` — quarantine fresh dependency releases\n- `74b535c` — settle the remediation ledger\n\nVerification: OxLint, Oxfmt, TypeScript, 138 tests, workspace build, and an independent adversarial review of the capture lifecycle. A live review-thread refresh found zero unresolved FE-1432 threads; the sole repository-wide unresolved thread is on child PR #20 and belongs to FE-1433.", - "innerSha256": "19f5f98e93e5519ab3b1bc2ece5499a459022157087eec5015c58a9c56d17d6b", - "normalizationNotes": "None; the canonical body and inner record are preserved byte-for-byte.", - "proposedBody": "The stack had review findings without a clear final disposition. This branch resolves them by fixing the actionable cases and recording ownership or a supported refusal for the rest.\n\n
🏗️ Agent notes\n\nFE-1432 executes the 2026-08-18 cross-stack review-remediation queue and closes its capture channel.\n\nWhat this establishes:\n\n- Capture-store command admission and persisted parsing share the same closure rules: one terminal event per issue, pairwise-disjoint open conflicts, and active references for every open conflict.\n- Baseline interviewer and expert truncation survives in checkpoint metadata; partial output stops before downstream model consumption, and expert resume regenerates the incomplete reply.\n- Valibot declarations and imports agree in both directions, while the walking-skeleton oracle reports absent context as `false` instead of throwing.\n- New dependency resolutions observe a seven-day release-age quarantine.\n- All 15 residual review threads were replied to and resolved: 7 fixes, 2 findings owned by FE-1385/FE-1393, and 6 evidence-backed refusals.\n- The three tooling-side graduation proposals remain owned by FE-1401's `ds-induct`/lens-registry lane.\n\nCommits:\n\n- `380411f` — preserve capture-store closure and prove local-store setup\n- `f71aa9e` — retain baseline completion metadata\n- `bb46941` — make capability and walking-skeleton gates honest\n- `2814090` — quarantine fresh dependency releases\n- `74b535c` — settle the remediation ledger\n\nVerification: OxLint, Oxfmt, TypeScript, 138 tests, workspace build, and an independent adversarial review of the capture lifecycle. A live review-thread refresh found zero unresolved FE-1432 threads; the sole repository-wide unresolved thread is on child PR #20 and belongs to FE-1433.\n\n
\n", - "proposedBodySha256": "ae7d1b251d8e1f3547dc5b55930a4e1372bd97f9af952c7e8d3e2319d46c0172", - "bodyChanged": false, - "titleChanged": true, - "ambiguity": null, - "notes": "The existing outer meets the house style; only the title changes." - }, - { - "number": 18, - "url": "https://github.com/hashintel/brunch-lite/pull/18", - "state": "OPEN", - "headRefName": "ln/fe-1392-settlement-sweep", - "linkedIssue": "FE-1392", - "sourceUpdatedAt": "2026-08-19T14:00:27Z", - "sourceTitle": "FE-1392: Settlement trigger and sweep — the first captured statement", - "sourceTitleSha256": "e2af4a86d0444c15a7135f477591b390b36fff479e0915de853efc36b4e36ec9", - "sourceBodySha256": "74deff20a1a55625cb86f64d8a867194c23f04df9057556314683a9420c14062", - "proposedTitle": "FE-1392: Capture settled conversation statements safely", - "oldOuter": "This branch connects completed conversation work to the first captured statement. It makes settlement occur at the appropriate lifecycle point and keeps the resulting evidence current.", - "proposedOuter": "This branch connects completed conversation work to the first captured statement. It makes settlement occur at the appropriate lifecycle point and keeps the resulting evidence current.", - "extractionMethod": "single canonical wrapper extracted", - "sourceInnerRecord": "Trigger settlement from the finish hook, extract against the plugin-declared grade floor, refresh archived history adjacent to atomic application, and reopen refused ranges without weakening session-qualified provenance.", - "sourceInnerSha256": "df3111e4b7761fa8032c600be382986205f9317ed03564010aa4b5e8172b2986", - "innerRecord": "Trigger settlement from the finish hook, extract against the plugin-declared grade floor, refresh archived history adjacent to atomic application, and reopen refused ranges without weakening session-qualified provenance.", - "innerSha256": "df3111e4b7761fa8032c600be382986205f9317ed03564010aa4b5e8172b2986", - "normalizationNotes": "None; the canonical body and inner record are preserved byte-for-byte.", - "proposedBody": "This branch connects completed conversation work to the first captured statement. It makes settlement occur at the appropriate lifecycle point and keeps the resulting evidence current.\n\n
🏗️ Agent notes\n\nTrigger settlement from the finish hook, extract against the plugin-declared grade floor, refresh archived history adjacent to atomic application, and reopen refused ranges without weakening session-qualified provenance.\n\n
\n", - "proposedBodySha256": "74deff20a1a55625cb86f64d8a867194c23f04df9057556314683a9420c14062", - "bodyChanged": false, - "titleChanged": true, - "ambiguity": null, - "notes": "The existing outer meets the house style; only the title changes." - }, - { - "number": 17, - "url": "https://github.com/hashintel/brunch-lite/pull/17", - "state": "OPEN", - "headRefName": "ln/fe-1391-entry-projection", - "linkedIssue": "FE-1391", - "sourceUpdatedAt": "2026-08-19T14:00:26Z", - "sourceTitle": "FE-1391: Durable entry projection, harness-resolved anchoring, and the session-log archive", - "sourceTitleSha256": "8df88b0ce1d3b89975c44cc7efb9d680d2cbda6518cf08c0af135ab15e663dae", - "sourceBodySha256": "08e75c781979efa46ebd3537f207e71dc89e95cd002bf534696690d6eea06a84", - "proposedTitle": "FE-1391: Resolve evidence quotes to durable conversation entries", - "oldOuter": "Later capture work needed a durable way to locate and anchor prior conversation entries. This branch establishes that path and retains enough history for those anchors to be resolved reliably.", - "proposedOuter": "Later capture work needed a durable way to locate and anchor prior conversation entries. This branch establishes that path and retains enough history for those anchors to be resolved reliably.", - "extractionMethod": "single canonical wrapper extracted", - "sourceInnerRecord": "docs: settle Flue projection and compaction\n\nRecord the installed @flue/runtime and @flue/sdk 2.0.3 contract and implementation evidence for FE-1391 B1/B2. Correct the archive premise to the materialized history surface with host-owned routing and evolving-message merge semantics. Trace append-only compaction and state ownership, then reshape FE-1386 to one behavioral upgrade pin and reconcile the living ledgers.\n\nfeat: anchor captures to archived Flue history\n\nRead the public materialized history through a host-supplied URL resolver and transport, and co-locate a versioned session log with capture state behind a binding-private archive capability. Archive ordinals preserve the session-plus-range pointer while Flue identities remain provenance and evolving messages retain distinct versions.\n\nReplace caller-supplied spans with quote-only evidence commands resolved atomically against the archive. Pin mounted in-process routing, affordance classification, non-user refusal, legacy provisioning, parse-on-read, and independent pointer retrieval. Keep the real-compaction compatibility pin with FE-1386 and move refresh-before-sweep orchestration to FE-1392.", - "sourceInnerSha256": "aa0dc9d30893dc7c516127e3e33517a0cee1493d257d73d835e9ec3ad0840d23", - "innerRecord": "docs: settle Flue projection and compaction\n\nRecord the installed @flue/runtime and @flue/sdk 2.0.3 contract and implementation evidence for FE-1391 B1/B2. Correct the archive premise to the materialized history surface with host-owned routing and evolving-message merge semantics. Trace append-only compaction and state ownership, then reshape FE-1386 to one behavioral upgrade pin and reconcile the living ledgers.\n\nfeat: anchor captures to archived Flue history\n\nRead the public materialized history through a host-supplied URL resolver and transport, and co-locate a versioned session log with capture state behind a binding-private archive capability. Archive ordinals preserve the session-plus-range pointer while Flue identities remain provenance and evolving messages retain distinct versions.\n\nReplace caller-supplied spans with quote-only evidence commands resolved atomically against the archive. Pin mounted in-process routing, affordance classification, non-user refusal, legacy provisioning, parse-on-read, and independent pointer retrieval. Keep the real-compaction compatibility pin with FE-1386 and move refresh-before-sweep orchestration to FE-1392.", - "innerSha256": "aa0dc9d30893dc7c516127e3e33517a0cee1493d257d73d835e9ec3ad0840d23", - "normalizationNotes": "None; the canonical body and inner record are preserved byte-for-byte.", - "proposedBody": "Later capture work needed a durable way to locate and anchor prior conversation entries. This branch establishes that path and retains enough history for those anchors to be resolved reliably.\n\n
🏗️ Agent notes\n\ndocs: settle Flue projection and compaction\n\nRecord the installed @flue/runtime and @flue/sdk 2.0.3 contract and implementation evidence for FE-1391 B1/B2. Correct the archive premise to the materialized history surface with host-owned routing and evolving-message merge semantics. Trace append-only compaction and state ownership, then reshape FE-1386 to one behavioral upgrade pin and reconcile the living ledgers.\n\nfeat: anchor captures to archived Flue history\n\nRead the public materialized history through a host-supplied URL resolver and transport, and co-locate a versioned session log with capture state behind a binding-private archive capability. Archive ordinals preserve the session-plus-range pointer while Flue identities remain provenance and evolving messages retain distinct versions.\n\nReplace caller-supplied spans with quote-only evidence commands resolved atomically against the archive. Pin mounted in-process routing, affordance classification, non-user refusal, legacy provisioning, parse-on-read, and independent pointer retrieval. Keep the real-compaction compatibility pin with FE-1386 and move refresh-before-sweep orchestration to FE-1392.\n\n
\n", - "proposedBodySha256": "08e75c781979efa46ebd3537f207e71dc89e95cd002bf534696690d6eea06a84", - "bodyChanged": false, - "titleChanged": true, - "ambiguity": null, - "notes": "The existing outer meets the house style; only the title changes." - }, - { - "number": 16, - "url": "https://github.com/hashintel/brunch-lite/pull/16", - "state": "OPEN", - "headRefName": "ln/fe-1422-ask-protocol", - "linkedIssue": "FE-1422", - "sourceUpdatedAt": "2026-08-19T14:00:24Z", - "sourceTitle": "FE-1422: The ask protocol is substrate-portable: mechanism moves from the Flue binding into core", - "sourceTitleSha256": "57533df404cf90eb95676d0223056011c73aa2ab89bacfeb6ebc12e5e91d1703", - "sourceBodySha256": "1fc9f55dcaded9b23be43b01e2fe6961b26a0bf5556f802ddfc7d1066846c05d", - "proposedTitle": "FE-1422: Move the portable ask protocol into core", - "oldOuter": "The question-and-reply behavior was tied too closely to one runtime binding. This branch moves the portable part into the core so other application surfaces can use the same protocol.", - "proposedOuter": "The question-and-reply behavior was tied too closely to the Flue runtime binding. This branch moves the portable part into core so other applications can use the same protocol.", - "extractionMethod": "single canonical wrapper extracted", - "sourceInnerRecord": "Move affordance minting, the one-live-affordance decision and refusal, reply-binding signal construction, and render-invariant instruction fragments into a pure core module. The Flue binding now translates hooks, state updates, data writes, and turn termination through that protocol instead of owning its decisions.\n\nCharacterization tests pin every extracted value and branch, while the unchanged walking-skeleton integration test continues to prove the real substrate path, duplicate-ask rejection, durable output, reply binding, and absence of instruction wakes.", - "sourceInnerSha256": "59fdea11ba00ea9f408a7936f5f48176856261837a2957afbd506aa653c88008", - "innerRecord": "Move affordance minting, the one-live-affordance decision and refusal, reply-binding signal construction, and render-invariant instruction fragments into a pure core module. The Flue binding now translates hooks, state updates, data writes, and turn termination through that protocol instead of owning its decisions.\n\nCharacterization tests pin every extracted value and branch, while the unchanged walking-skeleton integration test continues to prove the real substrate path, duplicate-ask rejection, durable output, reply binding, and absence of instruction wakes.", - "innerSha256": "59fdea11ba00ea9f408a7936f5f48176856261837a2957afbd506aa653c88008", - "normalizationNotes": "The inner record is unchanged; the outer was revised to remove an avoidable banned-word use.", - "proposedBody": "The question-and-reply behavior was tied too closely to the Flue runtime binding. This branch moves the portable part into core so other applications can use the same protocol.\n\n
🏗️ Agent notes\n\nMove affordance minting, the one-live-affordance decision and refusal, reply-binding signal construction, and render-invariant instruction fragments into a pure core module. The Flue binding now translates hooks, state updates, data writes, and turn termination through that protocol instead of owning its decisions.\n\nCharacterization tests pin every extracted value and branch, while the unchanged walking-skeleton integration test continues to prove the real substrate path, duplicate-ask rejection, durable output, reply binding, and absence of instruction wakes.\n\n
", - "proposedBodySha256": "b8d8aeb75c6e9c3d29795bf1328907df38da7a1c5b213991077340cd533fef60", - "bodyChanged": true, - "titleChanged": true, - "ambiguity": null, - "notes": "Replaced the figurative noun “surfaces” with the concrete reader “applications” and named the Flue runtime binding." - }, - { - "number": 15, - "url": "https://github.com/hashintel/brunch-lite/pull/15", - "state": "OPEN", - "headRefName": "ln/fe-1405-payload-interiors", - "linkedIssue": "FE-1405", - "sourceUpdatedAt": "2026-08-19T14:00:22Z", - "sourceTitle": "FE-1405: Draft the CPS payload interiors: annotated shapes for the ten kinds, worked from baseline utterances", - "sourceTitleSha256": "9b957cd8c760d786142e94469da03e59648c0835a5a54a7ec59204f86372f2dd", - "sourceBodySha256": "105a9f8da172e7bd653130a30dcbe55ae828672de2469449a8c3e8e2d99901fa", - "proposedTitle": "FE-1405: Draft and test the CPS payload schemas", - "oldOuter": "The CPS plugin needed concrete payload shapes grounded in the baseline evidence. This branch drafts those shapes and records the open pressures that later work must resolve.", - "proposedOuter": "The CPS plugin needed concrete payload shapes grounded in the baseline evidence. This branch drafts those shapes and records the open pressures that later work must resolve.", - "extractionMethod": "single canonical wrapper extracted", - "sourceInnerRecord": "This branch settles the FE-1405 payload-interiors arc and closes it.\n\n**What it establishes.** The session found that Layer A's definition sentence (\"active captures read through the payload type system\") describes an evidence ledger, not a model of the domain. Two artifacts fix that:\n\n- **ADR-0003 (three-register IR)**: typed assertions (active captures) are folded by a pure, plugin-declared fold into the elicited model — the IR proper — which projections consume without rereading the transcript. All interpretation happens at write time as contestable captures; promotion never refusal; an acceptance oracle (a second projection consumes the model alone) keeps the read path semantics-free. \"No second store\" survives: the model is a derivation, never a persistence surface.\n- **Provisional plugin-contract spec** (`docs/planning/process-model-elicitation/plugin-contract-spec.md`, published as FE-1431): a plugin is two schemas and two small tables — model schema, proposal catalog (typed proposals with interiors from a standard library), fold table (overrides only; rules mostly derive), demand table. Harness machinery is pure functions classified by which declaration each reads. The provisional marker comes off after a full worked pass across three plugin targets (the FE-1397 precedent); the open strains (grade sources, support closure, temporal patterns, sweep-time concentration, the absence-locator envelope pressure) are first-class sections with owning tickets, not folded into the design.\n\nThe capture envelope is untouched — the one confirmed pressure (absence captures carry no locator) is recorded at the FE-1383/FE-1357 seam in CONVERGENCE, not forked around.\n\n**Arc close** (second commit): SDCPN inbox arrival settled to `docs/reference/` with a skeptical digest; INDEX trued; CONVERGENCE re-evaluated with a dated third sequencing pass (the convergence collision now waits only on the build spine reaching FE-1392); registry audit re-parented two orphans (FE-1399/FE-1400 → FE-1383); CONTEXT.md's IR entry rewritten per ADR-0003 and a Grade entry added.\n\nGates: oxlint clean, oxfmt clean, typecheck clean, 100/100 tests, build green.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)", - "sourceInnerSha256": "c9864945e962b46747bac5d78ecf91947d3ed95f4ae9cd3f7e7e22fecf363083", - "innerRecord": "This branch settles the FE-1405 payload-interiors arc and closes it.\n\n**What it establishes.** The session found that Layer A's definition sentence (\"active captures read through the payload type system\") describes an evidence ledger, not a model of the domain. Two artifacts fix that:\n\n- **ADR-0003 (three-register IR)**: typed assertions (active captures) are folded by a pure, plugin-declared fold into the elicited model — the IR proper — which projections consume without rereading the transcript. All interpretation happens at write time as contestable captures; promotion never refusal; an acceptance oracle (a second projection consumes the model alone) keeps the read path semantics-free. \"No second store\" survives: the model is a derivation, never a persistence surface.\n- **Provisional plugin-contract spec** (`docs/planning/process-model-elicitation/plugin-contract-spec.md`, published as FE-1431): a plugin is two schemas and two small tables — model schema, proposal catalog (typed proposals with interiors from a standard library), fold table (overrides only; rules mostly derive), demand table. Harness machinery is pure functions classified by which declaration each reads. The provisional marker comes off after a full worked pass across three plugin targets (the FE-1397 precedent); the open strains (grade sources, support closure, temporal patterns, sweep-time concentration, the absence-locator envelope pressure) are first-class sections with owning tickets, not folded into the design.\n\nThe capture envelope is untouched — the one confirmed pressure (absence captures carry no locator) is recorded at the FE-1383/FE-1357 seam in CONVERGENCE, not forked around.\n\n**Arc close** (second commit): SDCPN inbox arrival settled to `docs/reference/` with a skeptical digest; INDEX trued; CONVERGENCE re-evaluated with a dated third sequencing pass (the convergence collision now waits only on the build spine reaching FE-1392); registry audit re-parented two orphans (FE-1399/FE-1400 → FE-1383); CONTEXT.md's IR entry rewritten per ADR-0003 and a Grade entry added.\n\nGates: oxlint clean, oxfmt clean, typecheck clean, 100/100 tests, build green.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)", - "innerSha256": "c9864945e962b46747bac5d78ecf91947d3ed95f4ae9cd3f7e7e22fecf363083", - "normalizationNotes": "None; the canonical body and inner record are preserved byte-for-byte.", - "proposedBody": "The CPS plugin needed concrete payload shapes grounded in the baseline evidence. This branch drafts those shapes and records the open pressures that later work must resolve.\n\n
🏗️ Agent notes\n\nThis branch settles the FE-1405 payload-interiors arc and closes it.\n\n**What it establishes.** The session found that Layer A's definition sentence (\"active captures read through the payload type system\") describes an evidence ledger, not a model of the domain. Two artifacts fix that:\n\n- **ADR-0003 (three-register IR)**: typed assertions (active captures) are folded by a pure, plugin-declared fold into the elicited model — the IR proper — which projections consume without rereading the transcript. All interpretation happens at write time as contestable captures; promotion never refusal; an acceptance oracle (a second projection consumes the model alone) keeps the read path semantics-free. \"No second store\" survives: the model is a derivation, never a persistence surface.\n- **Provisional plugin-contract spec** (`docs/planning/process-model-elicitation/plugin-contract-spec.md`, published as FE-1431): a plugin is two schemas and two small tables — model schema, proposal catalog (typed proposals with interiors from a standard library), fold table (overrides only; rules mostly derive), demand table. Harness machinery is pure functions classified by which declaration each reads. The provisional marker comes off after a full worked pass across three plugin targets (the FE-1397 precedent); the open strains (grade sources, support closure, temporal patterns, sweep-time concentration, the absence-locator envelope pressure) are first-class sections with owning tickets, not folded into the design.\n\nThe capture envelope is untouched — the one confirmed pressure (absence captures carry no locator) is recorded at the FE-1383/FE-1357 seam in CONVERGENCE, not forked around.\n\n**Arc close** (second commit): SDCPN inbox arrival settled to `docs/reference/` with a skeptical digest; INDEX trued; CONVERGENCE re-evaluated with a dated third sequencing pass (the convergence collision now waits only on the build spine reaching FE-1392); registry audit re-parented two orphans (FE-1399/FE-1400 → FE-1383); CONTEXT.md's IR entry rewritten per ADR-0003 and a Grade entry added.\n\nGates: oxlint clean, oxfmt clean, typecheck clean, 100/100 tests, build green.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\n
\n", - "proposedBodySha256": "105a9f8da172e7bd653130a30dcbe55ae828672de2469449a8c3e8e2d99901fa", - "bodyChanged": false, - "titleChanged": true, - "ambiguity": null, - "notes": "The existing outer meets the house style; only the title changes." - }, - { - "number": 14, - "url": "https://github.com/hashintel/brunch-lite/pull/14", - "state": "OPEN", - "headRefName": "ln/fe-1424-docs-housekeeping", - "linkedIssue": "FE-1424", - "sourceUpdatedAt": "2026-08-19T14:00:20Z", - "sourceTitle": "FE-1424: The documentation protocol runs end to end: inbox settled, planning reshaped, index gated, arc-close triggerable", - "sourceTitleSha256": "23fbf383a72d5bb528910b8067ebd01c3aef2aad088e7404b1c19f5a196b894e", - "sourceBodySha256": "c3cec4fdb4fe1de796714e121acf420b6d2da0d14d743c2e9b147d705f2ac8a1", - "proposedTitle": "FE-1424: Complete the documentation protocol", - "oldOuter": "The documentation protocol described a complete lifecycle but had not yet been exercised as one. This branch runs that lifecycle, gives each document a clear home, and adds a check that keeps the index honest.", - "proposedOuter": "The documentation protocol described a complete lifecycle but had not yet been exercised as one. This branch runs that lifecycle, gives each document a clear home, and adds a check that keeps the index honest.", - "extractionMethod": "single canonical wrapper extracted", - "sourceInnerRecord": "## What this establishes\n\nCloses FE-1424. The documentation protocol (`docs/agents/documentation.md`) specified zones and a settle sweep that were never executed; this branch runs it, amends it where practice had outgrown it, and makes the parts that can be mechanical, mechanical. Ratified by Lu 2026-08-17.\n\n**Commit 1 — settle + reshape.** The seven inbox documents settle into `docs/reference/` (the protocol's specified-but-never-created destination). `docs/planning/` splits by kind: cross-effort living documents (`CONVERGENCE`, `topology`, the Flue cheatsheet) into `_shared/`; dated arc records into `legibility-sweep/`. The planning top level now holds only directories, so living-vs-record is structural, not judged. All links re-pathed in both directions (14 files, verified by walking every relative link); Linear descriptions carrying old paths repaired (FE-1357/59/66/70, FE-1419, FE-1422/23).\n\n**Commit 2 — arc-close.** Three protocols each carried an arc-close step in prose with nothing firing them together; `docs/agents/arc-close.md` is now the single triggerable checklist (inbox sweep → INDEX pass → CONVERGENCE re-evaluation → registry audit → tense repair), with a content-free `/arc-close` skill wrapper. `documentation.md` gains the `_shared/` zone, the living-vs-record rule, the flattened drafts convention (`docs/planning//drafts/`), and the ephemera-reference convention (issue IDs glossed at first mention; load-bearing only in the tracking layer; tense repair at arc close).\n\n**Commit 3 — the gate.** `test/docs-index.test.ts`: every file under `docs/` covered by an INDEX row, every row resolves, planning top level directories-only, every `docs/agents/` protocol named in `AGENTS.md`. Enumerated live, vacuous-pass guarded, red-proved in both directions per rule. Freshness deliberately stays procedural — mechanizing it would build a dead gate.\n\n## Not done here\n\nOld Linear *comments* citing pre-move paths are left as historical record (descriptions were repaired); git log resolves the moves. Two pre-existing broken links found and left: the never-committed prototype HTML in `elicitation-kernel/issues/11`, and links inside the Amp transcript's embedded skill text (verbatim record, unfixable without corrupting it).\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)", - "sourceInnerSha256": "901600f53a27bda06c361821ab502ea6e0e30a5c279c9503fd5f0ea6ca13c050", - "innerRecord": "## What this establishes\n\nCloses FE-1424. The documentation protocol (`docs/agents/documentation.md`) specified zones and a settle sweep that were never executed; this branch runs it, amends it where practice had outgrown it, and makes the parts that can be mechanical, mechanical. Ratified by Lu 2026-08-17.\n\n**Commit 1 — settle + reshape.** The seven inbox documents settle into `docs/reference/` (the protocol's specified-but-never-created destination). `docs/planning/` splits by kind: cross-effort living documents (`CONVERGENCE`, `topology`, the Flue cheatsheet) into `_shared/`; dated arc records into `legibility-sweep/`. The planning top level now holds only directories, so living-vs-record is structural, not judged. All links re-pathed in both directions (14 files, verified by walking every relative link); Linear descriptions carrying old paths repaired (FE-1357/59/66/70, FE-1419, FE-1422/23).\n\n**Commit 2 — arc-close.** Three protocols each carried an arc-close step in prose with nothing firing them together; `docs/agents/arc-close.md` is now the single triggerable checklist (inbox sweep → INDEX pass → CONVERGENCE re-evaluation → registry audit → tense repair), with a content-free `/arc-close` skill wrapper. `documentation.md` gains the `_shared/` zone, the living-vs-record rule, the flattened drafts convention (`docs/planning//drafts/`), and the ephemera-reference convention (issue IDs glossed at first mention; load-bearing only in the tracking layer; tense repair at arc close).\n\n**Commit 3 — the gate.** `test/docs-index.test.ts`: every file under `docs/` covered by an INDEX row, every row resolves, planning top level directories-only, every `docs/agents/` protocol named in `AGENTS.md`. Enumerated live, vacuous-pass guarded, red-proved in both directions per rule. Freshness deliberately stays procedural — mechanizing it would build a dead gate.\n\n## Not done here\n\nOld Linear *comments* citing pre-move paths are left as historical record (descriptions were repaired); git log resolves the moves. Two pre-existing broken links found and left: the never-committed prototype HTML in `elicitation-kernel/issues/11`, and links inside the Amp transcript's embedded skill text (verbatim record, unfixable without corrupting it).\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)", - "innerSha256": "901600f53a27bda06c361821ab502ea6e0e30a5c279c9503fd5f0ea6ca13c050", - "normalizationNotes": "None; the canonical body and inner record are preserved byte-for-byte.", - "proposedBody": "The documentation protocol described a complete lifecycle but had not yet been exercised as one. This branch runs that lifecycle, gives each document a clear home, and adds a check that keeps the index honest.\n\n
🏗️ Agent notes\n\n## What this establishes\n\nCloses FE-1424. The documentation protocol (`docs/agents/documentation.md`) specified zones and a settle sweep that were never executed; this branch runs it, amends it where practice had outgrown it, and makes the parts that can be mechanical, mechanical. Ratified by Lu 2026-08-17.\n\n**Commit 1 — settle + reshape.** The seven inbox documents settle into `docs/reference/` (the protocol's specified-but-never-created destination). `docs/planning/` splits by kind: cross-effort living documents (`CONVERGENCE`, `topology`, the Flue cheatsheet) into `_shared/`; dated arc records into `legibility-sweep/`. The planning top level now holds only directories, so living-vs-record is structural, not judged. All links re-pathed in both directions (14 files, verified by walking every relative link); Linear descriptions carrying old paths repaired (FE-1357/59/66/70, FE-1419, FE-1422/23).\n\n**Commit 2 — arc-close.** Three protocols each carried an arc-close step in prose with nothing firing them together; `docs/agents/arc-close.md` is now the single triggerable checklist (inbox sweep → INDEX pass → CONVERGENCE re-evaluation → registry audit → tense repair), with a content-free `/arc-close` skill wrapper. `documentation.md` gains the `_shared/` zone, the living-vs-record rule, the flattened drafts convention (`docs/planning//drafts/`), and the ephemera-reference convention (issue IDs glossed at first mention; load-bearing only in the tracking layer; tense repair at arc close).\n\n**Commit 3 — the gate.** `test/docs-index.test.ts`: every file under `docs/` covered by an INDEX row, every row resolves, planning top level directories-only, every `docs/agents/` protocol named in `AGENTS.md`. Enumerated live, vacuous-pass guarded, red-proved in both directions per rule. Freshness deliberately stays procedural — mechanizing it would build a dead gate.\n\n## Not done here\n\nOld Linear *comments* citing pre-move paths are left as historical record (descriptions were repaired); git log resolves the moves. Two pre-existing broken links found and left: the never-committed prototype HTML in `elicitation-kernel/issues/11`, and links inside the Amp transcript's embedded skill text (verbatim record, unfixable without corrupting it).\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\n
\n", - "proposedBodySha256": "c3cec4fdb4fe1de796714e121acf420b6d2da0d14d743c2e9b147d705f2ac8a1", - "bodyChanged": false, - "titleChanged": true, - "ambiguity": null, - "notes": "The existing outer meets the house style; only the title changes." - }, - { - "number": 13, - "url": "https://github.com/hashintel/brunch-lite/pull/13", - "state": "OPEN", - "headRefName": "ln/fe-1419-contract-closure", - "linkedIssue": "FE-1419", - "sourceUpdatedAt": "2026-08-19T14:00:18Z", - "sourceTitle": "FE-1419: Close the seams where the capture store and verification gates claim more than they enforce", - "sourceTitleSha256": "fb7a8ac27ea24f41a79d749ca20733e2c25c4b1cee170719bc02ce0d241ad260", - "sourceBodySha256": "f9ab772a065145e23ed733c85c63888b8319ec31e8b04db7b281917372d796d4", - "proposedTitle": "FE-1419: Align capture-store rules and verification claims", - "oldOuter": "This branch closes places where the capture store and verification checks promised stronger guarantees than they actually enforced. It aligns the implementation and the checks with the properties the stack relies on.", - "proposedOuter": "This branch closes places where the capture store and verification checks promised stronger guarantees than they enforced. It aligns the implementation and the checks with the properties the stack relies on.", - "extractionMethod": "single canonical wrapper extracted", - "sourceInnerRecord": "Executes the FE-1419 refactor queue (`docs/planning/refactor-queue-2026-08-14.md`) — nine commits that give each contract one owner and make negative results precise, so green gates and successful commands stop claiming more than their implementations support.\n\nVerification layer: known-gap closure becomes an explicit ledger whose entries the proof commit deletes (no more citation-token inference); CI gates match exact declared commands; substrate access in tests is a reviewed inventory asserted as set equality; resolver probes classify only module-not-found as \"no\" and carry positive controls; asset serving accepts the producer's real name space behind property checks and per-segment encoding (closing a latent backslash-traversal path a probe found), translating exactly four absence errnos and propagating everything else.\n\nCapture store: evidence-range ordering, the issue contract, and set-equality resolution accounting each live in one definition enforced at command time and parse time alike; all event write paths deep-copy (the aliasing hole is closed); conflicts open only over two-plus distinct active captures and pin them until a user-cited resolution frees them — jointly, every open conflict is resolvable and stays resolvable, closing the invariant-2 bypass. Commit 9 locks the closure property totally: every command any test accepts round-trips through the persisted parser (an injected fault fails 19 of 21 tests), and refused commands leave the store file byte-identical.\n\nEvery commit was red-proved in both directions and independently reviewed before landing. Known residue, deliberately out of scope for a test-only commit: a `-0` capture value survives the command surface but returns as `0` through JSON — recorded on FE-1419. Fixes four refusal messages to state what was checked rather than what a reader might hope was verified.\n\nAlso carries the FE-1401 remediation-sweep docs that landed during execution: the two-map CONVERGENCE record with seam section, issue glosses, and ordering strategies; the registry rule in issue-tracker.md; the Flue canonical-patterns audit; and the deep-read records.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)", - "sourceInnerSha256": "03aaf220ef749b0fcb9a0b8a846cc9670fd135f285e682acaf84c04dc77759a0", - "innerRecord": "Executes the FE-1419 refactor queue (`docs/planning/refactor-queue-2026-08-14.md`) — nine commits that give each contract one owner and make negative results precise, so green gates and successful commands stop claiming more than their implementations support.\n\nVerification layer: known-gap closure becomes an explicit ledger whose entries the proof commit deletes (no more citation-token inference); CI gates match exact declared commands; substrate access in tests is a reviewed inventory asserted as set equality; resolver probes classify only module-not-found as \"no\" and carry positive controls; asset serving accepts the producer's real name space behind property checks and per-segment encoding (closing a latent backslash-traversal path a probe found), translating exactly four absence errnos and propagating everything else.\n\nCapture store: evidence-range ordering, the issue contract, and set-equality resolution accounting each live in one definition enforced at command time and parse time alike; all event write paths deep-copy (the aliasing hole is closed); conflicts open only over two-plus distinct active captures and pin them until a user-cited resolution frees them — jointly, every open conflict is resolvable and stays resolvable, closing the invariant-2 bypass. Commit 9 locks the closure property totally: every command any test accepts round-trips through the persisted parser (an injected fault fails 19 of 21 tests), and refused commands leave the store file byte-identical.\n\nEvery commit was red-proved in both directions and independently reviewed before landing. Known residue, deliberately out of scope for a test-only commit: a `-0` capture value survives the command surface but returns as `0` through JSON — recorded on FE-1419. Fixes four refusal messages to state what was checked rather than what a reader might hope was verified.\n\nAlso carries the FE-1401 remediation-sweep docs that landed during execution: the two-map CONVERGENCE record with seam section, issue glosses, and ordering strategies; the registry rule in issue-tracker.md; the Flue canonical-patterns audit; and the deep-read records.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)", - "innerSha256": "03aaf220ef749b0fcb9a0b8a846cc9670fd135f285e682acaf84c04dc77759a0", - "normalizationNotes": "The inner record is unchanged; the outer was revised to remove an avoidable banned-word use.", - "proposedBody": "This branch closes places where the capture store and verification checks promised stronger guarantees than they enforced. It aligns the implementation and the checks with the properties the stack relies on.\n\n
🏗️ Agent notes\n\nExecutes the FE-1419 refactor queue (`docs/planning/refactor-queue-2026-08-14.md`) — nine commits that give each contract one owner and make negative results precise, so green gates and successful commands stop claiming more than their implementations support.\n\nVerification layer: known-gap closure becomes an explicit ledger whose entries the proof commit deletes (no more citation-token inference); CI gates match exact declared commands; substrate access in tests is a reviewed inventory asserted as set equality; resolver probes classify only module-not-found as \"no\" and carry positive controls; asset serving accepts the producer's real name space behind property checks and per-segment encoding (closing a latent backslash-traversal path a probe found), translating exactly four absence errnos and propagating everything else.\n\nCapture store: evidence-range ordering, the issue contract, and set-equality resolution accounting each live in one definition enforced at command time and parse time alike; all event write paths deep-copy (the aliasing hole is closed); conflicts open only over two-plus distinct active captures and pin them until a user-cited resolution frees them — jointly, every open conflict is resolvable and stays resolvable, closing the invariant-2 bypass. Commit 9 locks the closure property totally: every command any test accepts round-trips through the persisted parser (an injected fault fails 19 of 21 tests), and refused commands leave the store file byte-identical.\n\nEvery commit was red-proved in both directions and independently reviewed before landing. Known residue, deliberately out of scope for a test-only commit: a `-0` capture value survives the command surface but returns as `0` through JSON — recorded on FE-1419. Fixes four refusal messages to state what was checked rather than what a reader might hope was verified.\n\nAlso carries the FE-1401 remediation-sweep docs that landed during execution: the two-map CONVERGENCE record with seam section, issue glosses, and ordering strategies; the registry rule in issue-tracker.md; the Flue canonical-patterns audit; and the deep-read records.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\n
", - "proposedBodySha256": "7c6d072833c15450fddbb3e102cf9b39e7aeb2af621c1090a990f5c6894e0b14", - "bodyChanged": true, - "titleChanged": true, - "ambiguity": null, - "notes": "Removed “actually”; the sentence now names what the checks failed to enforce." - }, - { - "number": 12, - "url": "https://github.com/hashintel/brunch-lite/pull/12", - "state": "OPEN", - "headRefName": "ln/fe-1401-legibility-sweep", - "linkedIssue": "FE-1401", - "sourceUpdatedAt": "2026-08-19T14:00:17Z", - "sourceTitle": "FE-1401: Resolve the follow-ups from the stack legibility session", - "sourceTitleSha256": "0451f66a36602e806df9c2112c1121521b94245613ec8efffb621bb83695c9d0", - "sourceBodySha256": "cca722394c59d6fc2431b1d6619f0991782d6d1969121d2d9193b4bbe84af0c3", - "proposedTitle": "FE-1401: Resolve the stack legibility follow-ups", - "oldOuter": "The legibility review produced findings that needed durable documentation and follow-up ownership. This branch records those findings, repairs the affected planning material, and makes the remaining work visible.", - "proposedOuter": "The legibility review produced findings that needed durable documentation and follow-up ownership. This branch records those findings, repairs the affected planning material, and makes the remaining work visible.", - "extractionMethod": "single canonical wrapper extracted", - "sourceInnerRecord": "FE-1401: Land the legibility session's renderings, audit, and penciled directions\n\nThree documents promoted from drafts/ by Lu, with their post-move repairs\n(self-descriptions, relative links) and editorial additions:\n\n- ir-design-plain.md — plain-prose rendering of the IR design (same content\n commitments; original authoritative). The rendering pass doubled as review:\n seven strain findings on FE-1401, the load-bearing one being the loss\n report's unresolved unit of loss (capture vs. capture-facet).\n- notes/research-patterns-audit.md — ~30 research imports in 7 families,\n each with plain explanation, provenance, evidence grade, and where it\n landed; strain appendix (8 findings, incl. the betting-questions\n misattribution and the v0-vs-IDEA quantile divergence).\n- notes/penciled-directions-2026-08-14.md — 8 penciled directions with\n firming actions and marked editorial reflections (schema as forcing\n function, plugin manifest sketch, mode oscillation, activation probes,\n incorporation rubric, super-map question, write-time check tiers,\n per-domain machinery inventory).\n\nINDEX.md gains rows for all three plus two pre-existing gaps: ir-design.md\nand ir-worked-examples.md were never indexed.\n\nSession record: FE-1401 and its accrual comments; new tickets FE-1402–1407.\n\nCo-Authored-By: Claude Fable 5 \n\nprep refactor queue, and flue analysis\n\nSigned-off-by: Lu Nelson ", - "sourceInnerSha256": "deb834741c2f9b93ca8d47f6032007240f2aeafb09d79b6e6593f4d11f61226a", - "innerRecord": "FE-1401: Land the legibility session's renderings, audit, and penciled directions\n\nThree documents promoted from drafts/ by Lu, with their post-move repairs\n(self-descriptions, relative links) and editorial additions:\n\n- ir-design-plain.md — plain-prose rendering of the IR design (same content\n commitments; original authoritative). The rendering pass doubled as review:\n seven strain findings on FE-1401, the load-bearing one being the loss\n report's unresolved unit of loss (capture vs. capture-facet).\n- notes/research-patterns-audit.md — ~30 research imports in 7 families,\n each with plain explanation, provenance, evidence grade, and where it\n landed; strain appendix (8 findings, incl. the betting-questions\n misattribution and the v0-vs-IDEA quantile divergence).\n- notes/penciled-directions-2026-08-14.md — 8 penciled directions with\n firming actions and marked editorial reflections (schema as forcing\n function, plugin manifest sketch, mode oscillation, activation probes,\n incorporation rubric, super-map question, write-time check tiers,\n per-domain machinery inventory).\n\nINDEX.md gains rows for all three plus two pre-existing gaps: ir-design.md\nand ir-worked-examples.md were never indexed.\n\nSession record: FE-1401 and its accrual comments; new tickets FE-1402–1407.\n\nCo-Authored-By: Claude Fable 5 \n\nprep refactor queue, and flue analysis\n\nSigned-off-by: Lu Nelson ", - "innerSha256": "deb834741c2f9b93ca8d47f6032007240f2aeafb09d79b6e6593f4d11f61226a", - "normalizationNotes": "None; the canonical body and inner record are preserved byte-for-byte.", - "proposedBody": "The legibility review produced findings that needed durable documentation and follow-up ownership. This branch records those findings, repairs the affected planning material, and makes the remaining work visible.\n\n
🏗️ Agent notes\n\nFE-1401: Land the legibility session's renderings, audit, and penciled directions\n\nThree documents promoted from drafts/ by Lu, with their post-move repairs\n(self-descriptions, relative links) and editorial additions:\n\n- ir-design-plain.md — plain-prose rendering of the IR design (same content\n commitments; original authoritative). The rendering pass doubled as review:\n seven strain findings on FE-1401, the load-bearing one being the loss\n report's unresolved unit of loss (capture vs. capture-facet).\n- notes/research-patterns-audit.md — ~30 research imports in 7 families,\n each with plain explanation, provenance, evidence grade, and where it\n landed; strain appendix (8 findings, incl. the betting-questions\n misattribution and the v0-vs-IDEA quantile divergence).\n- notes/penciled-directions-2026-08-14.md — 8 penciled directions with\n firming actions and marked editorial reflections (schema as forcing\n function, plugin manifest sketch, mode oscillation, activation probes,\n incorporation rubric, super-map question, write-time check tiers,\n per-domain machinery inventory).\n\nINDEX.md gains rows for all three plus two pre-existing gaps: ir-design.md\nand ir-worked-examples.md were never indexed.\n\nSession record: FE-1401 and its accrual comments; new tickets FE-1402–1407.\n\nCo-Authored-By: Claude Fable 5 \n\nprep refactor queue, and flue analysis\n\nSigned-off-by: Lu Nelson \n\n
\n", - "proposedBodySha256": "cca722394c59d6fc2431b1d6619f0991782d6d1969121d2d9193b4bbe84af0c3", - "bodyChanged": false, - "titleChanged": true, - "ambiguity": null, - "notes": "The existing outer meets the house style; only the title changes." - }, - { - "number": 11, - "url": "https://github.com/hashintel/brunch-lite/pull/11", - "state": "OPEN", - "headRefName": "ln/fe-1390-capture-store", - "linkedIssue": "FE-1390", - "sourceUpdatedAt": "2026-08-19T14:00:15Z", - "sourceTitle": "FE-1390: Capture envelope, storage port, and the local capture store", - "sourceTitleSha256": "710989fa7a8870a91ba1e36447c52327068378fbb13bc879314f367363dc3f6c", - "sourceBodySha256": "a58c82980be648e3b01142e3c0d0087a3c49b893c9547055bde27b98bf0bc5e0", - "proposedTitle": "FE-1390: Implement capture history and local persistence", - "oldOuter": "This branch gives captured evidence a durable local home with an explicit write path. It establishes reliable history for later model work while leaving consumption work to subsequent branches.", - "proposedOuter": "This branch gives captured evidence a durable local home with an explicit write path. It establishes reliable history for later model work while leaving consumption work to subsequent branches.", - "extractionMethod": "single canonical wrapper extracted", - "sourceInnerRecord": "Adds the capture store — the durable, session-independent truth of a target-document — as an append-only snapshot of captures, issues, and events, with every status derived at read time instead of stored. Writes go through one pure command function that returns either a whole new snapshot or a typed refusal, so a sweep applies completely or not at all, and the Flue binding implements the storage port over it with a JSON file, tmp-and-rename, and per-path serialized writes. Capture identity is content-keyed with epistemic status excluded, which makes re-sweeps idempotent and forces revised readings through explicit supersession. The session-log-archive half of the storage port (spec §9.6) and write-time verification of evidence pointers are not in this branch, and nothing consumes the store yet.\n\nDeep-read record: `docs/planning/process-model-elicitation/notes/deep-read-fe-1390.md` (FE-1401 remediation sweep); the plain rendering with strain report is `docs/planning/process-model-elicitation/capture-store-plain.md`; contract-closure work is queued as FE-1419.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)", - "sourceInnerSha256": "dd6b285545fcc139792afefef315c9c22acc0e2f1b1d08a9a80b9198b98a46ba", - "innerRecord": "Adds the capture store — the durable, session-independent truth of a target-document — as an append-only snapshot of captures, issues, and events, with every status derived at read time instead of stored. Writes go through one pure command function that returns either a whole new snapshot or a typed refusal, so a sweep applies completely or not at all, and the Flue binding implements the storage port over it with a JSON file, tmp-and-rename, and per-path serialized writes. Capture identity is content-keyed with epistemic status excluded, which makes re-sweeps idempotent and forces revised readings through explicit supersession. The session-log-archive half of the storage port (spec §9.6) and write-time verification of evidence pointers are not in this branch, and nothing consumes the store yet.\n\nDeep-read record: `docs/planning/process-model-elicitation/notes/deep-read-fe-1390.md` (FE-1401 remediation sweep); the plain rendering with strain report is `docs/planning/process-model-elicitation/capture-store-plain.md`; contract-closure work is queued as FE-1419.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)", - "innerSha256": "dd6b285545fcc139792afefef315c9c22acc0e2f1b1d08a9a80b9198b98a46ba", - "normalizationNotes": "None; the canonical body and inner record are preserved byte-for-byte.", - "proposedBody": "This branch gives captured evidence a durable local home with an explicit write path. It establishes reliable history for later model work while leaving consumption work to subsequent branches.\n\n
🏗️ Agent notes\n\nAdds the capture store — the durable, session-independent truth of a target-document — as an append-only snapshot of captures, issues, and events, with every status derived at read time instead of stored. Writes go through one pure command function that returns either a whole new snapshot or a typed refusal, so a sweep applies completely or not at all, and the Flue binding implements the storage port over it with a JSON file, tmp-and-rename, and per-path serialized writes. Capture identity is content-keyed with epistemic status excluded, which makes re-sweeps idempotent and forces revised readings through explicit supersession. The session-log-archive half of the storage port (spec §9.6) and write-time verification of evidence pointers are not in this branch, and nothing consumes the store yet.\n\nDeep-read record: `docs/planning/process-model-elicitation/notes/deep-read-fe-1390.md` (FE-1401 remediation sweep); the plain rendering with strain report is `docs/planning/process-model-elicitation/capture-store-plain.md`; contract-closure work is queued as FE-1419.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\n
\n", - "proposedBodySha256": "a58c82980be648e3b01142e3c0d0087a3c49b893c9547055bde27b98bf0bc5e0", - "bodyChanged": false, - "titleChanged": true, - "ambiguity": null, - "notes": "The existing outer meets the house style; only the title changes." - }, - { - "number": 10, - "url": "https://github.com/hashintel/brunch-lite/pull/10", - "state": "OPEN", - "headRefName": "ln/fe-1389-walking-skeleton", - "linkedIssue": "FE-1389", - "sourceUpdatedAt": "2026-08-19T14:00:14Z", - "sourceTitle": "FE-1389: Walking skeleton — the harness asks a free-text question and binds the reply", - "sourceTitleSha256": "9aa634521196bc66ad8a36254eb995aee3b484177468d0bb0e007fb358ffb8b7", - "sourceBodySha256": "f5249295ba3564783719e2d9e870e7659b074482d3c86d20803c118308d26a69", - "proposedTitle": "FE-1389: Implement the first suspended free-text question", - "oldOuter": "This branch proves the first end-to-end question-and-reply path through the real application harness. It establishes that a question can pause a turn, persist, and bind the later answer correctly.", - "proposedOuter": "This branch proves the first end-to-end question-and-reply path through the real application harness. It establishes that a question can pause a turn, persist, and bind the later answer correctly.", - "extractionMethod": "single canonical wrapper extracted", - "sourceInnerRecord": "This turns the ask seam into a working turn-suspension protocol: the tool mints a free-text affordance, parks it in the pending-affordance slot, returns it on the tool output part where its identity is durable, and terminates the turn — then the next user dispatch clears the slot and gets bound to that affordance by the harness itself, announced to the model as a signal rather than left to the model's memory. Because the pending question is never interpolated into the instructions, the instruction string cannot change between turns, which is what removes the wasted \"instructions updated\" wake turn the ticket-10 prototype hit once per question. The dev app is the proof: a chat UI plus an integration test that boots the real Flue runtime and the real Hono app in one process, drives them over a fetch shim with a scripted faux provider, and asserts in one shot that the reply binding reached the model, the affordance survived durably, no advisory wake appeared, and a second ask in the same batch was refused.\n\nDeep-read record: `docs/planning/process-model-elicitation/notes/deep-read-fe-1389.md` (FE-1401 remediation sweep); hardening follow-ups filed as FE-1420.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)", - "sourceInnerSha256": "f5792d146c557204d975fa7c7b4cacc298cced709faec5fe5cd951c46b0e7ea4", - "innerRecord": "This turns the ask seam into a working turn-suspension protocol: the tool mints a free-text affordance, parks it in the pending-affordance slot, returns it on the tool output part where its identity is durable, and terminates the turn — then the next user dispatch clears the slot and gets bound to that affordance by the harness itself, announced to the model as a signal rather than left to the model's memory. Because the pending question is never interpolated into the instructions, the instruction string cannot change between turns, which is what removes the wasted \"instructions updated\" wake turn the ticket-10 prototype hit once per question. The dev app is the proof: a chat UI plus an integration test that boots the real Flue runtime and the real Hono app in one process, drives them over a fetch shim with a scripted faux provider, and asserts in one shot that the reply binding reached the model, the affordance survived durably, no advisory wake appeared, and a second ask in the same batch was refused.\n\nDeep-read record: `docs/planning/process-model-elicitation/notes/deep-read-fe-1389.md` (FE-1401 remediation sweep); hardening follow-ups filed as FE-1420.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)", - "innerSha256": "f5792d146c557204d975fa7c7b4cacc298cced709faec5fe5cd951c46b0e7ea4", - "normalizationNotes": "None; the canonical body and inner record are preserved byte-for-byte.", - "proposedBody": "This branch proves the first end-to-end question-and-reply path through the real application harness. It establishes that a question can pause a turn, persist, and bind the later answer correctly.\n\n
🏗️ Agent notes\n\nThis turns the ask seam into a working turn-suspension protocol: the tool mints a free-text affordance, parks it in the pending-affordance slot, returns it on the tool output part where its identity is durable, and terminates the turn — then the next user dispatch clears the slot and gets bound to that affordance by the harness itself, announced to the model as a signal rather than left to the model's memory. Because the pending question is never interpolated into the instructions, the instruction string cannot change between turns, which is what removes the wasted \"instructions updated\" wake turn the ticket-10 prototype hit once per question. The dev app is the proof: a chat UI plus an integration test that boots the real Flue runtime and the real Hono app in one process, drives them over a fetch shim with a scripted faux provider, and asserts in one shot that the reply binding reached the model, the affordance survived durably, no advisory wake appeared, and a second ask in the same batch was refused.\n\nDeep-read record: `docs/planning/process-model-elicitation/notes/deep-read-fe-1389.md` (FE-1401 remediation sweep); hardening follow-ups filed as FE-1420.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\n
\n", - "proposedBodySha256": "f5249295ba3564783719e2d9e870e7659b074482d3c86d20803c118308d26a69", - "bodyChanged": false, - "titleChanged": true, - "ambiguity": null, - "notes": "The existing outer meets the house style; only the title changes." - }, - { - "number": 9, - "url": "https://github.com/hashintel/brunch-lite/pull/9", - "state": "OPEN", - "headRefName": "ln/fe-1400-close-silent-gaps", - "linkedIssue": "FE-1400", - "sourceUpdatedAt": "2026-08-19T14:00:12Z", - "sourceTitle": "FE-1400: Close the review-found gaps where the gates, dev app, and baseline runner still fail silently", - "sourceTitleSha256": "438c3f68d2d61c5975585c864f38610d5e0899e9f6d1aea28d3091b82f63bb71", - "sourceBodySha256": "3e7d50eae5e60d3152d16e1ecefb3de27934ea2f73e2e9f26d995921a67cc869", - "proposedTitle": "FE-1400: Strengthen verification, dev storage, and the baseline runner", - "oldOuter": "This branch resolves the remaining review findings where checks or local tools could report success without proving the intended behavior. The result makes those failure paths visible and testable.", - "proposedOuter": "This branch resolves the remaining review findings where checks or local tools could report success without proving the intended behavior. The result makes those failure paths visible and testable.", - "extractionMethod": "single canonical wrapper extracted", - "sourceInnerRecord": "Executes the FE-1400 bundle: the ten verified findings from the FE-1397 tie-off review, plus the cleanup tail, as small always-green commits. Each strengthened gate was red-proved — the violation it guards against was temporarily introduced and the gate watched go red — before restoring green.\n\n**Docs truth:** the AGENTS.md triage one-liner now matches the triage doc's state/label mapping.\n\n**Test integrity:** build-artifact witnesses are now strings unique to the guarded modules (`createAgentRouter`/`sqlite` both matched bootstrap code regardless of what bundled); the first-statement check reuses the shared directive pattern instead of a literal compare; the workspace walker's source/test partition is total (nested `src/test/` files no longer escape every invariant); the CI-gate check parses the workflow so commented-out or `if: false` gates read as absent; known-gaps predicates require an explicit `closes-gap: ` citation in a real test instead of `existsSync` on a guessed path — a stub can't close a gap, a differently-named real closure can. FWIW the citation-token design earned its keep immediately: the first draft accepted any prose mention of the gap id, and a doc comment in this branch's own new test closed a gap.\n\n**Dev app:** the conversation store's default path anchors to the module (`db-path.ts`) instead of the launch directory, with tests for cwd-independence and the set-but-empty env override (which would have opened an anonymous temp database).\n\n**Baseline runner:** the hand-rolled HTTP client is gone in favor of `@anthropic-ai/sdk` (network-error retries, retry-after, cache-token usage in the totals); truncation survives continuation stitching and is reported with the `--continue-final` escape hatch; fence matching tolerates the info-string forms real runs produce, and a delivered run that yields no extractable artifact — or an ambiguous multi-block one — says so loudly. The post-review pass also added an explicit SDK timeout (the doubled retry budget tripped the SDK's 10-minute non-streaming guard), no-clobber guards for fresh/`--resume` runs, and seam-exact stitching.\n\n**Cleanup tail:** assets serve via a `/assets/*` wildcard and hono's MIME table (nested paths now work); CI builds once (root build inside `bun test`'s artifact suite, exercising every package's build script) and caches bun installs; the root build filter is `@brunch/*`; the runner's parallel `expertMessages` array and dead checkpoint field are gone; the boundary suite derives its topology pin from the spec's §12.2 block and its physical-resolution probes from the tree.\n\nTwo review findings deliberately not taken: swapping the hand-rolled asset handler for hono's `serveStatic` middleware (the strict dotfile/extension refusal semantics are pinned by tests and the ticket asked for framework routing + MIME table, both done), and de-obfuscating the composed `MODEL_KEY_NAME` pattern (an exclusion list for pattern-defining files has its own silent-failure mode; the composition predates this branch and is documented in place).\n\nCloses FE-1400.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)", - "sourceInnerSha256": "417c1c5f7a1e36797056a57c4926ece639a5b1194698472254dc2bb5269d8de9", - "innerRecord": "Executes the FE-1400 bundle: the ten verified findings from the FE-1397 tie-off review, plus the cleanup tail, as small always-green commits. Each strengthened gate was red-proved — the violation it guards against was temporarily introduced and the gate watched go red — before restoring green.\n\n**Docs truth:** the AGENTS.md triage one-liner now matches the triage doc's state/label mapping.\n\n**Test integrity:** build-artifact witnesses are now strings unique to the guarded modules (`createAgentRouter`/`sqlite` both matched bootstrap code regardless of what bundled); the first-statement check reuses the shared directive pattern instead of a literal compare; the workspace walker's source/test partition is total (nested `src/test/` files no longer escape every invariant); the CI-gate check parses the workflow so commented-out or `if: false` gates read as absent; known-gaps predicates require an explicit `closes-gap: ` citation in a real test instead of `existsSync` on a guessed path — a stub can't close a gap, a differently-named real closure can. FWIW the citation-token design earned its keep immediately: the first draft accepted any prose mention of the gap id, and a doc comment in this branch's own new test closed a gap.\n\n**Dev app:** the conversation store's default path anchors to the module (`db-path.ts`) instead of the launch directory, with tests for cwd-independence and the set-but-empty env override (which would have opened an anonymous temp database).\n\n**Baseline runner:** the hand-rolled HTTP client is gone in favor of `@anthropic-ai/sdk` (network-error retries, retry-after, cache-token usage in the totals); truncation survives continuation stitching and is reported with the `--continue-final` escape hatch; fence matching tolerates the info-string forms real runs produce, and a delivered run that yields no extractable artifact — or an ambiguous multi-block one — says so loudly. The post-review pass also added an explicit SDK timeout (the doubled retry budget tripped the SDK's 10-minute non-streaming guard), no-clobber guards for fresh/`--resume` runs, and seam-exact stitching.\n\n**Cleanup tail:** assets serve via a `/assets/*` wildcard and hono's MIME table (nested paths now work); CI builds once (root build inside `bun test`'s artifact suite, exercising every package's build script) and caches bun installs; the root build filter is `@brunch/*`; the runner's parallel `expertMessages` array and dead checkpoint field are gone; the boundary suite derives its topology pin from the spec's §12.2 block and its physical-resolution probes from the tree.\n\nTwo review findings deliberately not taken: swapping the hand-rolled asset handler for hono's `serveStatic` middleware (the strict dotfile/extension refusal semantics are pinned by tests and the ticket asked for framework routing + MIME table, both done), and de-obfuscating the composed `MODEL_KEY_NAME` pattern (an exclusion list for pattern-defining files has its own silent-failure mode; the composition predates this branch and is documented in place).\n\nCloses FE-1400.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)", - "innerSha256": "417c1c5f7a1e36797056a57c4926ece639a5b1194698472254dc2bb5269d8de9", - "normalizationNotes": "None; the canonical body and inner record are preserved byte-for-byte.", - "proposedBody": "This branch resolves the remaining review findings where checks or local tools could report success without proving the intended behavior. The result makes those failure paths visible and testable.\n\n
🏗️ Agent notes\n\nExecutes the FE-1400 bundle: the ten verified findings from the FE-1397 tie-off review, plus the cleanup tail, as small always-green commits. Each strengthened gate was red-proved — the violation it guards against was temporarily introduced and the gate watched go red — before restoring green.\n\n**Docs truth:** the AGENTS.md triage one-liner now matches the triage doc's state/label mapping.\n\n**Test integrity:** build-artifact witnesses are now strings unique to the guarded modules (`createAgentRouter`/`sqlite` both matched bootstrap code regardless of what bundled); the first-statement check reuses the shared directive pattern instead of a literal compare; the workspace walker's source/test partition is total (nested `src/test/` files no longer escape every invariant); the CI-gate check parses the workflow so commented-out or `if: false` gates read as absent; known-gaps predicates require an explicit `closes-gap: ` citation in a real test instead of `existsSync` on a guessed path — a stub can't close a gap, a differently-named real closure can. FWIW the citation-token design earned its keep immediately: the first draft accepted any prose mention of the gap id, and a doc comment in this branch's own new test closed a gap.\n\n**Dev app:** the conversation store's default path anchors to the module (`db-path.ts`) instead of the launch directory, with tests for cwd-independence and the set-but-empty env override (which would have opened an anonymous temp database).\n\n**Baseline runner:** the hand-rolled HTTP client is gone in favor of `@anthropic-ai/sdk` (network-error retries, retry-after, cache-token usage in the totals); truncation survives continuation stitching and is reported with the `--continue-final` escape hatch; fence matching tolerates the info-string forms real runs produce, and a delivered run that yields no extractable artifact — or an ambiguous multi-block one — says so loudly. The post-review pass also added an explicit SDK timeout (the doubled retry budget tripped the SDK's 10-minute non-streaming guard), no-clobber guards for fresh/`--resume` runs, and seam-exact stitching.\n\n**Cleanup tail:** assets serve via a `/assets/*` wildcard and hono's MIME table (nested paths now work); CI builds once (root build inside `bun test`'s artifact suite, exercising every package's build script) and caches bun installs; the root build filter is `@brunch/*`; the runner's parallel `expertMessages` array and dead checkpoint field are gone; the boundary suite derives its topology pin from the spec's §12.2 block and its physical-resolution probes from the tree.\n\nTwo review findings deliberately not taken: swapping the hand-rolled asset handler for hono's `serveStatic` middleware (the strict dotfile/extension refusal semantics are pinned by tests and the ticket asked for framework routing + MIME table, both done), and de-obfuscating the composed `MODEL_KEY_NAME` pattern (an exclusion list for pattern-defining files has its own silent-failure mode; the composition predates this branch and is documented in place).\n\nCloses FE-1400.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\n
\n", - "proposedBodySha256": "3e7d50eae5e60d3152d16e1ecefb3de27934ea2f73e2e9f26d995921a67cc869", - "bodyChanged": false, - "titleChanged": true, - "ambiguity": null, - "notes": "The existing outer meets the house style; only the title changes." - }, - { - "number": 8, - "url": "https://github.com/hashintel/brunch-lite/pull/8", - "state": "OPEN", - "headRefName": "ln/fe-1397-worked-examples", - "linkedIssue": "FE-1397", - "sourceUpdatedAt": "2026-08-19T14:00:10Z", - "sourceTitle": "FE-1397: Validate the generic IR definition against worked payload designs (Gherkin, CPS, +1)", - "sourceTitleSha256": "bb81d98a6ecb4ad55e7dddaf963f52da31c86a14e7cb652ee63e46396ee2867e", - "sourceBodySha256": "e8ed6d96887037c79160976cbc45078edc4c452c456ef50783cead160602a7f0", - "proposedTitle": "FE-1397: Validate the generic IR against worked plugin payloads", - "oldOuter": "The generic representation still needed evidence that it held across more than one domain shape. This branch tests it against worked payload designs and records which parts of the definition remain sound.", - "proposedOuter": "The generic representation still needed evidence that it held across more than one domain shape. This branch tests it against worked payload designs and records which parts of the definition remain sound.", - "extractionMethod": "single canonical wrapper extracted", - "sourceInnerRecord": "Discharges the ratification condition on the generic IR definition (`ir-design.md`, Layer A): speculative payload type systems drafted for Gherkin (thin) and BPMN/process-mining (mid, the kernel spec's named third target), checked property-by-property alongside CPS (Layer B) and the assurance plugin from spec canon.\n\n**New:** `docs/planning/process-model-elicitation/ir-worked-examples.md` — the two drafted payload designs, per-property verdict table, sublimation findings, and the handoff list for plugin-spec authoring.\n\n**Amended:** `ir-design.md` —\n- All five MUST properties survive; property 2 generalized from statement granularity to **evidence granularity** (log-derived `external-lookup` captures have no user statement), property 3 restated operatively (its bite is proportional to domain–format distance; the enforceable content is IR-legitimacy of unconsumed kinds + the honest loss report).\n- Symbolic name references promoted MAY → SHOULD (all four designs use them); the objective-kind pattern generalized to completion-anchor kinds; motif annotations demoted to a named escape hatch (zero uptake); **source-regime** promoted from Layer B to a Layer-A MAY pattern for process-shaped domains.\n- Status: conditionally ratified → **ratified on worked examples**, still provisional until the September harness run.\n\nStacked on `ln/fe-1399-fail-loudly` because `ir-design.md` is not on main yet.\n\nCloses FE-1397.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)", - "sourceInnerSha256": "bf06af3e6f96d849b4c1be53c8a5d9a6d3d750d4f8fdd6ddd6cac5444ec17d8a", - "innerRecord": "Discharges the ratification condition on the generic IR definition (`ir-design.md`, Layer A): speculative payload type systems drafted for Gherkin (thin) and BPMN/process-mining (mid, the kernel spec's named third target), checked property-by-property alongside CPS (Layer B) and the assurance plugin from spec canon.\n\n**New:** `docs/planning/process-model-elicitation/ir-worked-examples.md` — the two drafted payload designs, per-property verdict table, sublimation findings, and the handoff list for plugin-spec authoring.\n\n**Amended:** `ir-design.md` —\n- All five MUST properties survive; property 2 generalized from statement granularity to **evidence granularity** (log-derived `external-lookup` captures have no user statement), property 3 restated operatively (its bite is proportional to domain–format distance; the enforceable content is IR-legitimacy of unconsumed kinds + the honest loss report).\n- Symbolic name references promoted MAY → SHOULD (all four designs use them); the objective-kind pattern generalized to completion-anchor kinds; motif annotations demoted to a named escape hatch (zero uptake); **source-regime** promoted from Layer B to a Layer-A MAY pattern for process-shaped domains.\n- Status: conditionally ratified → **ratified on worked examples**, still provisional until the September harness run.\n\nStacked on `ln/fe-1399-fail-loudly` because `ir-design.md` is not on main yet.\n\nCloses FE-1397.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)", - "innerSha256": "bf06af3e6f96d849b4c1be53c8a5d9a6d3d750d4f8fdd6ddd6cac5444ec17d8a", - "normalizationNotes": "None; the canonical body and inner record are preserved byte-for-byte.", - "proposedBody": "The generic representation still needed evidence that it held across more than one domain shape. This branch tests it against worked payload designs and records which parts of the definition remain sound.\n\n
🏗️ Agent notes\n\nDischarges the ratification condition on the generic IR definition (`ir-design.md`, Layer A): speculative payload type systems drafted for Gherkin (thin) and BPMN/process-mining (mid, the kernel spec's named third target), checked property-by-property alongside CPS (Layer B) and the assurance plugin from spec canon.\n\n**New:** `docs/planning/process-model-elicitation/ir-worked-examples.md` — the two drafted payload designs, per-property verdict table, sublimation findings, and the handoff list for plugin-spec authoring.\n\n**Amended:** `ir-design.md` —\n- All five MUST properties survive; property 2 generalized from statement granularity to **evidence granularity** (log-derived `external-lookup` captures have no user statement), property 3 restated operatively (its bite is proportional to domain–format distance; the enforceable content is IR-legitimacy of unconsumed kinds + the honest loss report).\n- Symbolic name references promoted MAY → SHOULD (all four designs use them); the objective-kind pattern generalized to completion-anchor kinds; motif annotations demoted to a named escape hatch (zero uptake); **source-regime** promoted from Layer B to a Layer-A MAY pattern for process-shaped domains.\n- Status: conditionally ratified → **ratified on worked examples**, still provisional until the September harness run.\n\nStacked on `ln/fe-1399-fail-loudly` because `ir-design.md` is not on main yet.\n\nCloses FE-1397.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\n
\n", - "proposedBodySha256": "e8ed6d96887037c79160976cbc45078edc4c452c456ef50783cead160602a7f0", - "bodyChanged": false, - "titleChanged": true, - "ambiguity": null, - "notes": "The existing outer meets the house style; only the title changes." - }, - { - "number": 7, - "url": "https://github.com/hashintel/brunch-lite/pull/7", - "state": "OPEN", - "headRefName": "ln/fe-1399-fail-loudly", - "linkedIssue": "FE-1399", - "sourceUpdatedAt": "2026-08-19T14:00:08Z", - "sourceTitle": "FE-1399: Make the CI gates and dev app fail loudly where review found they fail silently", - "sourceTitleSha256": "09929e03f4a76113a051454c63fd0fdad8466e536a83e0e3f13e4a817672c64c", - "sourceBodySha256": "23f9bc92607a7d5be6b55ee742f4669bf684245ce7a5c126d92f5fd65807c119", - "proposedTitle": "FE-1399: Fix verified silent failures in CI and the dev app", - "oldOuter": "Review found checks that could pass while failing to protect the behavior they claimed to cover. This branch makes those checks and the development app report those failures instead of quietly accepting them.", - "proposedOuter": "Review found checks that could pass while failing to protect the behavior they claimed to cover. This branch makes those checks and the development app report those failures instead of quietly accepting them.", - "extractionMethod": "single canonical wrapper extracted", - "sourceInnerRecord": "FE-1399: Make the CI gates and dev app fail loudly where review found they fail silently\n\nThe five verified findings from the FE-1361 review sweep:\n\n- Agent-module detection is statement-anchored (shared in test/workspace.ts,\n unit-tested), so a comment mentioning 'use agent' no longer turns a file\n into an agent module — while a misplaced or comment-trailed directive is\n still detected and failed loudly.\n- workspacePackages() derives its groups from the root manifest's workspaces\n globs; an unknown group, unreadable glob shape, or missing directory now\n throws instead of passing every boundary invariant vacuously.\n- toolName() takes the Operation union instead of string, so a misspelled\n operation is a compile error; a @ts-expect-error test pins the narrowing.\n- The dev app's mount path derives from GherkinElicitor.agentName, and a new\n boundary test forbids a pinned identity being duplicated as a string\n literal outside its agent module.\n- The production /assets/:file route serves everything the client build can\n emit — bytes, not UTF-8; case-folded extensions; content-type map failing\n open to octet-stream — with the handler extracted to assets.ts and driven\n as a real Hono route over binary fixtures.\n\nReview fixes folded in: trailing-comment directives still detected,\nidentity scan matches quoted literals only (no comment cry-wolf),\nuppercase extensions serve.\n\nCo-Authored-By: Claude Fable 5 \n\nsymlink CLAUDE.md from AGENTS.md", - "sourceInnerSha256": "4fb763857dc7d92796d5c5100d3a1569bbe3e42455521a70c011b9fecfeafb2e", - "innerRecord": "FE-1399: Make the CI gates and dev app fail loudly where review found they fail silently\n\nThe five verified findings from the FE-1361 review sweep:\n\n- Agent-module detection is statement-anchored (shared in test/workspace.ts,\n unit-tested), so a comment mentioning 'use agent' no longer turns a file\n into an agent module — while a misplaced or comment-trailed directive is\n still detected and failed loudly.\n- workspacePackages() derives its groups from the root manifest's workspaces\n globs; an unknown group, unreadable glob shape, or missing directory now\n throws instead of passing every boundary invariant vacuously.\n- toolName() takes the Operation union instead of string, so a misspelled\n operation is a compile error; a @ts-expect-error test pins the narrowing.\n- The dev app's mount path derives from GherkinElicitor.agentName, and a new\n boundary test forbids a pinned identity being duplicated as a string\n literal outside its agent module.\n- The production /assets/:file route serves everything the client build can\n emit — bytes, not UTF-8; case-folded extensions; content-type map failing\n open to octet-stream — with the handler extracted to assets.ts and driven\n as a real Hono route over binary fixtures.\n\nReview fixes folded in: trailing-comment directives still detected,\nidentity scan matches quoted literals only (no comment cry-wolf),\nuppercase extensions serve.\n\nCo-Authored-By: Claude Fable 5 \n\nsymlink CLAUDE.md from AGENTS.md", - "innerSha256": "4fb763857dc7d92796d5c5100d3a1569bbe3e42455521a70c011b9fecfeafb2e", - "normalizationNotes": "None; the canonical body and inner record are preserved byte-for-byte.", - "proposedBody": "Review found checks that could pass while failing to protect the behavior they claimed to cover. This branch makes those checks and the development app report those failures instead of quietly accepting them.\n\n
🏗️ Agent notes\n\nFE-1399: Make the CI gates and dev app fail loudly where review found they fail silently\n\nThe five verified findings from the FE-1361 review sweep:\n\n- Agent-module detection is statement-anchored (shared in test/workspace.ts,\n unit-tested), so a comment mentioning 'use agent' no longer turns a file\n into an agent module — while a misplaced or comment-trailed directive is\n still detected and failed loudly.\n- workspacePackages() derives its groups from the root manifest's workspaces\n globs; an unknown group, unreadable glob shape, or missing directory now\n throws instead of passing every boundary invariant vacuously.\n- toolName() takes the Operation union instead of string, so a misspelled\n operation is a compile error; a @ts-expect-error test pins the narrowing.\n- The dev app's mount path derives from GherkinElicitor.agentName, and a new\n boundary test forbids a pinned identity being duplicated as a string\n literal outside its agent module.\n- The production /assets/:file route serves everything the client build can\n emit — bytes, not UTF-8; case-folded extensions; content-type map failing\n open to octet-stream — with the handler extracted to assets.ts and driven\n as a real Hono route over binary fixtures.\n\nReview fixes folded in: trailing-comment directives still detected,\nidentity scan matches quoted literals only (no comment cry-wolf),\nuppercase extensions serve.\n\nCo-Authored-By: Claude Fable 5 \n\nsymlink CLAUDE.md from AGENTS.md\n\n
\n", - "proposedBodySha256": "23f9bc92607a7d5be6b55ee742f4669bf684245ce7a5c126d92f5fd65807c119", - "bodyChanged": false, - "titleChanged": true, - "ambiguity": null, - "notes": "The existing outer meets the house style; only the title changes." - }, - { - "number": 6, - "url": "https://github.com/hashintel/brunch-lite/pull/6", - "state": "OPEN", - "headRefName": "ln/fe-1361-baseline-control", - "linkedIssue": "FE-1361", - "sourceUpdatedAt": "2026-08-19T14:00:06Z", - "sourceTitle": "FE-1361: Baseline control — what does one-shot AI elicitation already achieve?", - "sourceTitleSha256": "49a7e617a4276a193a7ac78578d95fb09586d2fd660232472afb83eaa2c81739", - "sourceBodySha256": "a504f8f2a3ac48ec0599e2f1bf0a8d9968e94dbbd04eb6d8b23d200966e8bbb6", - "proposedTitle": "FE-1361: Measure the one-shot AI elicitation baseline", - "oldOuter": "This branch records what a lightly guided model can already do in the reference case. The findings set a factual baseline for deciding which product machinery is actually needed.", - "proposedOuter": "This branch records what a lightly guided model can already do in the reference case. The findings set a factual baseline for deciding which product machinery is needed.", - "extractionMethod": "single canonical wrapper extracted", - "sourceInnerRecord": "Resolves [FE-1361](https://linear.app/hash/issue/FE-1361): the baseline-control experiment — what does one-shot / lightly-prompted AI elicitation already achieve?\n\nTwo conditions ran against the same simulated master scheduler (Production Scheduling testbed case): bare `claude-opus-5`, and the same model armed with the seven-category v0 prompt. Everything lives under `docs/planning/process-model-elicitation/baseline/` — protocol, situation pack, v0 prompt, runner, both full transcripts, and the scored read-out (`readout.md`).\n\nHeadline findings (details and scores in the read-out, gist on the ticket):\n\n- The bare baseline interviews far better than the positioning assumed — objectives-first, unwritten-rules probing, refusal to invent values, and an unprompted assumptions register. The differentiation story must rest on machinery, and now has evidence to rest on.\n- Neither condition can end the engagement: one novel deliverable-deferral failure, one sophisticated budget-exhaustion. Completion has to be an adjudicated contract.\n- The v0 prompt buys interaction shape, quantile elicitation, live category accounting, conflict-point depth, and penalty-weight co-construction; its residual gaps are the evidence-derived plugin requirements.\n\nAlso on this branch, from the review pass: the typecheck gate now covers `docs/planning/**` scripts (which surfaced and fixed a real narrowing error in the runner), and five off-by-one relative links left by the docs migration are repaired. The review's remaining repo-wide findings are filed as [FE-1399](https://linear.app/hash/issue/FE-1399).\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)", - "sourceInnerSha256": "fb6a119b6a835d5db47ffc75c42aa42213d0e7dc5b224fbccffeb462fb126408", - "innerRecord": "Resolves [FE-1361](https://linear.app/hash/issue/FE-1361): the baseline-control experiment — what does one-shot / lightly-prompted AI elicitation already achieve?\n\nTwo conditions ran against the same simulated master scheduler (Production Scheduling testbed case): bare `claude-opus-5`, and the same model armed with the seven-category v0 prompt. Everything lives under `docs/planning/process-model-elicitation/baseline/` — protocol, situation pack, v0 prompt, runner, both full transcripts, and the scored read-out (`readout.md`).\n\nHeadline findings (details and scores in the read-out, gist on the ticket):\n\n- The bare baseline interviews far better than the positioning assumed — objectives-first, unwritten-rules probing, refusal to invent values, and an unprompted assumptions register. The differentiation story must rest on machinery, and now has evidence to rest on.\n- Neither condition can end the engagement: one novel deliverable-deferral failure, one sophisticated budget-exhaustion. Completion has to be an adjudicated contract.\n- The v0 prompt buys interaction shape, quantile elicitation, live category accounting, conflict-point depth, and penalty-weight co-construction; its residual gaps are the evidence-derived plugin requirements.\n\nAlso on this branch, from the review pass: the typecheck gate now covers `docs/planning/**` scripts (which surfaced and fixed a real narrowing error in the runner), and five off-by-one relative links left by the docs migration are repaired. The review's remaining repo-wide findings are filed as [FE-1399](https://linear.app/hash/issue/FE-1399).\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)", - "innerSha256": "fb6a119b6a835d5db47ffc75c42aa42213d0e7dc5b224fbccffeb462fb126408", - "normalizationNotes": "The inner record is unchanged; the outer was revised to remove an avoidable banned-word use.", - "proposedBody": "This branch records what a lightly guided model can already do in the reference case. The findings set a factual baseline for deciding which product machinery is needed.\n\n
🏗️ Agent notes\n\nResolves [FE-1361](https://linear.app/hash/issue/FE-1361): the baseline-control experiment — what does one-shot / lightly-prompted AI elicitation already achieve?\n\nTwo conditions ran against the same simulated master scheduler (Production Scheduling testbed case): bare `claude-opus-5`, and the same model armed with the seven-category v0 prompt. Everything lives under `docs/planning/process-model-elicitation/baseline/` — protocol, situation pack, v0 prompt, runner, both full transcripts, and the scored read-out (`readout.md`).\n\nHeadline findings (details and scores in the read-out, gist on the ticket):\n\n- The bare baseline interviews far better than the positioning assumed — objectives-first, unwritten-rules probing, refusal to invent values, and an unprompted assumptions register. The differentiation story must rest on machinery, and now has evidence to rest on.\n- Neither condition can end the engagement: one novel deliverable-deferral failure, one sophisticated budget-exhaustion. Completion has to be an adjudicated contract.\n- The v0 prompt buys interaction shape, quantile elicitation, live category accounting, conflict-point depth, and penalty-weight co-construction; its residual gaps are the evidence-derived plugin requirements.\n\nAlso on this branch, from the review pass: the typecheck gate now covers `docs/planning/**` scripts (which surfaced and fixed a real narrowing error in the runner), and five off-by-one relative links left by the docs migration are repaired. The review's remaining repo-wide findings are filed as [FE-1399](https://linear.app/hash/issue/FE-1399).\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\n
", - "proposedBodySha256": "d83c7fcbbec74341c2a25153d3146f0bd579d0c4f60794b634a3aaf73c472bcf", - "bodyChanged": true, - "titleChanged": true, - "ambiguity": null, - "notes": "Removed “actually”; the factual baseline claim is unchanged." - }, - { - "number": 5, - "url": "https://github.com/hashintel/brunch-lite/pull/5", - "state": "OPEN", - "headRefName": "ln/fe-1364-intermediate-representation", - "linkedIssue": "FE-1364", - "sourceUpdatedAt": "2026-08-19T14:00:05Z", - "sourceTitle": "FE-1364: Define the intermediate representation for process-model elicitation", - "sourceTitleSha256": "229ac27a91eab0e70254f9a283f3ef6a9f8d375bbf17fffdb8f7d04d2ac59603", - "sourceBodySha256": "e6f28eca8b261bb837c955a6a243db8b74fbb2b31d4033a230b131ffef570cbc", - "proposedTitle": "FE-1364: Define the process-model elicitation representation", - "oldOuter": "This branch defines the shared representation used to turn elicited evidence into a model. The definition gives future plugins and projections a common basis without introducing another source of stored truth.", - "proposedOuter": "This branch defines the shared representation used to turn elicited evidence into a model. The definition gives future plugins and projections a common basis without introducing another source of stored truth.", - "extractionMethod": "single canonical wrapper extracted", - "sourceInnerRecord": "Resolution of the wayfinder ticket: the IR is the set of active captures\nread through the plugin's declared payload type system — no second store;\nthe net is one projection. Layer A (architecture-level definition) is\nconditionally ratified pending worked-examples validation (FE-1397);\nLayer B is the CPS plugin's ten-kind payload design for the truck-fleet\nSeptember case. Adds the IR glossary entry to CONTEXT.md.\n\nCo-Authored-By: Claude Fable 5 ", - "sourceInnerSha256": "5855717a5f072802db5ab6383077122e4599d0435709ea2222b1da1006ba5d89", - "innerRecord": "Resolution of the wayfinder ticket: the IR is the set of active captures\nread through the plugin's declared payload type system — no second store;\nthe net is one projection. Layer A (architecture-level definition) is\nconditionally ratified pending worked-examples validation (FE-1397);\nLayer B is the CPS plugin's ten-kind payload design for the truck-fleet\nSeptember case. Adds the IR glossary entry to CONTEXT.md.\n\nCo-Authored-By: Claude Fable 5 ", - "innerSha256": "5855717a5f072802db5ab6383077122e4599d0435709ea2222b1da1006ba5d89", - "normalizationNotes": "None; the canonical body and inner record are preserved byte-for-byte.", - "proposedBody": "This branch defines the shared representation used to turn elicited evidence into a model. The definition gives future plugins and projections a common basis without introducing another source of stored truth.\n\n
🏗️ Agent notes\n\nResolution of the wayfinder ticket: the IR is the set of active captures\nread through the plugin's declared payload type system — no second store;\nthe net is one projection. Layer A (architecture-level definition) is\nconditionally ratified pending worked-examples validation (FE-1397);\nLayer B is the CPS plugin's ten-kind payload design for the truck-fleet\nSeptember case. Adds the IR glossary entry to CONTEXT.md.\n\nCo-Authored-By: Claude Fable 5 \n\n
\n", - "proposedBodySha256": "e6f28eca8b261bb837c955a6a243db8b74fbb2b31d4033a230b131ffef570cbc", - "bodyChanged": false, - "titleChanged": true, - "ambiguity": null, - "notes": "The existing outer meets the house style; only the title changes." - }, - { - "number": 4, - "url": "https://github.com/hashintel/brunch-lite/pull/4", - "state": "OPEN", - "headRefName": "ln/fe-1388-scaffold-workspace", - "linkedIssue": "FE-1388", - "sourceUpdatedAt": "2026-08-19T14:00:04Z", - "sourceTitle": "FE-1388: Scaffold the Bun workspace and prove the CI smoke", - "sourceTitleSha256": "d1e15580b5ebf90f1be401019c3b33876f59f9aabe23a0424ee5b0688b15a628", - "sourceBodySha256": "b14b6372d8340e427843903ecd013bdf356c3adc8d00a4cec6f664672b67817d", - "proposedTitle": "FE-1388: Create the Bun workspace and enforce dependency boundaries", - "oldOuter": "The repository needed a runnable workspace before feature work could be trusted. This branch establishes the initial package layout and checks that catch boundary and build failures early.", - "proposedOuter": "The repository needed a runnable workspace before feature work could be trusted. This branch establishes the initial package layout and checks that catch boundary and build failures early.", - "extractionMethod": "single canonical wrapper extracted", - "sourceInnerRecord": "FE-1388: Scaffold the Bun workspace and prove the CI smoke\n\nThe repo held planning documents and no code. This lands the package\ntopology of spec §12.2 with its dependency direction enforced from the\nfirst commit rather than retrofitted later, so every later slice arrives\ninto a structure that already refuses the wrong import.\n\nPackages: core (plugin SDK as its export surface, with a core/testing\nsubpath), binding-flue, plugin-gherkin, and a dev app owning the agent\nmodule, the mount, and the conversation store.\n\nThe direction is enforced twice over. Bun's isolated linker gives each\npackage only what it declares, so plugin-gherkin cannot resolve Flue or\nthe binding even if someone writes the import; test/boundaries.test.ts\nasserts that property still holds, alongside the declared-dependency and\nsource-import checks, so a hoisted node_modules could not quietly restore\nevery forbidden path.\n\nTwo recorded Flue constraints needed opposite treatment. A computed\nagentName already fails the build loudly. A 'use agent' directive that is\nnot the file's first statement does NOT: the build stays green and the\nmodule simply stops being an agent, which nothing would notice until a\nconversation failed to start. That one is the load-bearing check in the\nboundary suite.\n\nNothing here ratifies the SDK export surface. Spec §13's two-targets rule\nkeeps the plugin contract unfrozen until the hard target has stressed it,\nso the plugin descriptor carries identity only and the ask tool fixes its\nname and schema seam while its behaviour waits for FE-1389.\n\nThe product name stays provisional in one constant, and the tool prefix\nderives from it — the name-fog costs one edit rather than a rename.\n\nCo-Authored-By: Claude Opus 5 (1M context) \n\nFE-1388: Close the review findings — make the gates able to fail\n\nReview found that three of the guards this ticket added could not\nactually go red, which is the same failure mode as a green build that\ncompiled nothing.\n\n- The CI lint step was decorative. oxlint reports its default rules at\n warning severity and exits 0 for them, so `oxlint .` passed whatever\n landed; it now runs with --deny-warnings, and a test asserts that.\n- The boundary suite was never typechecked: the root tsconfig included\n every package's src and test but not the repo-root test/ directory, so\n a type error in the file that enforces the architecture shipped green.\n- sourceFiles() scanned src/ only and returned [] for anything else, so a\n package laid out differently would pass every file-level invariant\n vacuously. The scan now covers the whole package directory, and a test\n asserts each package was actually scanned.\n\nAlso honest now: the dev app's / route no longer serves a page whose only\nscript 404s after a build. @flue/vite builds the server environment\nalone, so there is no client bundle to serve; the built server says so\nand vite build earns its CI place by compiling the agent module.\n\nSmaller: REPO_ROOT resolves through fileURLToPath rather than a\npercent-encoded URL.pathname; a workspace directory missing a manifest\nreports that rather than crashing the suite with ENOENT; bunfig.toml pins\nthe isolated linker that makes the dependency direction physical; and the\nfour per-package tsconfigs were referenced by nothing, so they are gone\nrather than left to drift.\n\nCo-Authored-By: Claude Opus 5 (1M context) ", - "sourceInnerSha256": "a64a1d8ff07ec6eb8ddff05773cfae355166251c53888725f2272d729eccd4ab", - "innerRecord": "FE-1388: Scaffold the Bun workspace and prove the CI smoke\n\nThe repo held planning documents and no code. This lands the package\ntopology of spec §12.2 with its dependency direction enforced from the\nfirst commit rather than retrofitted later, so every later slice arrives\ninto a structure that already refuses the wrong import.\n\nPackages: core (plugin SDK as its export surface, with a core/testing\nsubpath), binding-flue, plugin-gherkin, and a dev app owning the agent\nmodule, the mount, and the conversation store.\n\nThe direction is enforced twice over. Bun's isolated linker gives each\npackage only what it declares, so plugin-gherkin cannot resolve Flue or\nthe binding even if someone writes the import; test/boundaries.test.ts\nasserts that property still holds, alongside the declared-dependency and\nsource-import checks, so a hoisted node_modules could not quietly restore\nevery forbidden path.\n\nTwo recorded Flue constraints needed opposite treatment. A computed\nagentName already fails the build loudly. A 'use agent' directive that is\nnot the file's first statement does NOT: the build stays green and the\nmodule simply stops being an agent, which nothing would notice until a\nconversation failed to start. That one is the load-bearing check in the\nboundary suite.\n\nNothing here ratifies the SDK export surface. Spec §13's two-targets rule\nkeeps the plugin contract unfrozen until the hard target has stressed it,\nso the plugin descriptor carries identity only and the ask tool fixes its\nname and schema seam while its behaviour waits for FE-1389.\n\nThe product name stays provisional in one constant, and the tool prefix\nderives from it — the name-fog costs one edit rather than a rename.\n\nCo-Authored-By: Claude Opus 5 (1M context) \n\nFE-1388: Close the review findings — make the gates able to fail\n\nReview found that three of the guards this ticket added could not\nactually go red, which is the same failure mode as a green build that\ncompiled nothing.\n\n- The CI lint step was decorative. oxlint reports its default rules at\n warning severity and exits 0 for them, so `oxlint .` passed whatever\n landed; it now runs with --deny-warnings, and a test asserts that.\n- The boundary suite was never typechecked: the root tsconfig included\n every package's src and test but not the repo-root test/ directory, so\n a type error in the file that enforces the architecture shipped green.\n- sourceFiles() scanned src/ only and returned [] for anything else, so a\n package laid out differently would pass every file-level invariant\n vacuously. The scan now covers the whole package directory, and a test\n asserts each package was actually scanned.\n\nAlso honest now: the dev app's / route no longer serves a page whose only\nscript 404s after a build. @flue/vite builds the server environment\nalone, so there is no client bundle to serve; the built server says so\nand vite build earns its CI place by compiling the agent module.\n\nSmaller: REPO_ROOT resolves through fileURLToPath rather than a\npercent-encoded URL.pathname; a workspace directory missing a manifest\nreports that rather than crashing the suite with ENOENT; bunfig.toml pins\nthe isolated linker that makes the dependency direction physical; and the\nfour per-package tsconfigs were referenced by nothing, so they are gone\nrather than left to drift.\n\nCo-Authored-By: Claude Opus 5 (1M context) ", - "innerSha256": "a64a1d8ff07ec6eb8ddff05773cfae355166251c53888725f2272d729eccd4ab", - "normalizationNotes": "None; the canonical body and inner record are preserved byte-for-byte.", - "proposedBody": "The repository needed a runnable workspace before feature work could be trusted. This branch establishes the initial package layout and checks that catch boundary and build failures early.\n\n
🏗️ Agent notes\n\nFE-1388: Scaffold the Bun workspace and prove the CI smoke\n\nThe repo held planning documents and no code. This lands the package\ntopology of spec §12.2 with its dependency direction enforced from the\nfirst commit rather than retrofitted later, so every later slice arrives\ninto a structure that already refuses the wrong import.\n\nPackages: core (plugin SDK as its export surface, with a core/testing\nsubpath), binding-flue, plugin-gherkin, and a dev app owning the agent\nmodule, the mount, and the conversation store.\n\nThe direction is enforced twice over. Bun's isolated linker gives each\npackage only what it declares, so plugin-gherkin cannot resolve Flue or\nthe binding even if someone writes the import; test/boundaries.test.ts\nasserts that property still holds, alongside the declared-dependency and\nsource-import checks, so a hoisted node_modules could not quietly restore\nevery forbidden path.\n\nTwo recorded Flue constraints needed opposite treatment. A computed\nagentName already fails the build loudly. A 'use agent' directive that is\nnot the file's first statement does NOT: the build stays green and the\nmodule simply stops being an agent, which nothing would notice until a\nconversation failed to start. That one is the load-bearing check in the\nboundary suite.\n\nNothing here ratifies the SDK export surface. Spec §13's two-targets rule\nkeeps the plugin contract unfrozen until the hard target has stressed it,\nso the plugin descriptor carries identity only and the ask tool fixes its\nname and schema seam while its behaviour waits for FE-1389.\n\nThe product name stays provisional in one constant, and the tool prefix\nderives from it — the name-fog costs one edit rather than a rename.\n\nCo-Authored-By: Claude Opus 5 (1M context) \n\nFE-1388: Close the review findings — make the gates able to fail\n\nReview found that three of the guards this ticket added could not\nactually go red, which is the same failure mode as a green build that\ncompiled nothing.\n\n- The CI lint step was decorative. oxlint reports its default rules at\n warning severity and exits 0 for them, so `oxlint .` passed whatever\n landed; it now runs with --deny-warnings, and a test asserts that.\n- The boundary suite was never typechecked: the root tsconfig included\n every package's src and test but not the repo-root test/ directory, so\n a type error in the file that enforces the architecture shipped green.\n- sourceFiles() scanned src/ only and returned [] for anything else, so a\n package laid out differently would pass every file-level invariant\n vacuously. The scan now covers the whole package directory, and a test\n asserts each package was actually scanned.\n\nAlso honest now: the dev app's / route no longer serves a page whose only\nscript 404s after a build. @flue/vite builds the server environment\nalone, so there is no client bundle to serve; the built server says so\nand vite build earns its CI place by compiling the agent module.\n\nSmaller: REPO_ROOT resolves through fileURLToPath rather than a\npercent-encoded URL.pathname; a workspace directory missing a manifest\nreports that rather than crashing the suite with ENOENT; bunfig.toml pins\nthe isolated linker that makes the dependency direction physical; and the\nfour per-package tsconfigs were referenced by nothing, so they are gone\nrather than left to drift.\n\nCo-Authored-By: Claude Opus 5 (1M context) \n\n
\n", - "proposedBodySha256": "b14b6372d8340e427843903ecd013bdf356c3adc8d00a4cec6f664672b67817d", - "bodyChanged": false, - "titleChanged": true, - "ambiguity": null, - "notes": "The existing outer meets the house style; only the title changes." - }, - { - "number": 3, - "url": "https://github.com/hashintel/brunch-lite/pull/3", - "state": "OPEN", - "headRefName": "ln/fe-1363-reference-use-case", - "linkedIssue": "FE-1363", - "sourceUpdatedAt": "2026-08-19T14:00:02Z", - "sourceTitle": "FE-1363: Choose the reference use case; settle the SDCPN-showcase criterion", - "sourceTitleSha256": "479fc988f738df20272d535d4cdec59affba9e2c782f716ba8b2c1077966ece0", - "sourceBodySha256": "c11ea0f55f34cf925c4c4dd02834521dc97c1f1b2575c18b5b48e087b027fb00", - "proposedTitle": "FE-1363: Choose the demo use case and modelling criteria", - "oldOuter": "This branch defines the reference case used to judge the demonstration work. It makes the selection criteria clear enough for later planning and review.", - "proposedOuter": "This branch defines the reference case used to judge the demonstration work. It makes the selection criteria clear enough for later planning and review.", - "extractionMethod": "single canonical wrapper extracted", - "sourceInnerRecord": "Glossary: revision story, situation pack, answer key — including the\npack/IR information-wall invariant.\n\nCo-Authored-By: Claude Fable 5 ", - "sourceInnerSha256": "61d4a1544d743147646d62c9ae8c4bfb0d70cfa15585c6348c8025e2ef892f27", - "innerRecord": "Glossary: revision story, situation pack, answer key — including the\npack/IR information-wall invariant.\n\nCo-Authored-By: Claude Fable 5 ", - "innerSha256": "61d4a1544d743147646d62c9ae8c4bfb0d70cfa15585c6348c8025e2ef892f27", - "normalizationNotes": "None; the canonical body and inner record are preserved byte-for-byte.", - "proposedBody": "This branch defines the reference case used to judge the demonstration work. It makes the selection criteria clear enough for later planning and review.\n\n
🏗️ Agent notes\n\nGlossary: revision story, situation pack, answer key — including the\npack/IR information-wall invariant.\n\nCo-Authored-By: Claude Fable 5 \n\n
\n", - "proposedBodySha256": "c11ea0f55f34cf925c4c4dd02834521dc97c1f1b2575c18b5b48e087b027fb00", - "bodyChanged": false, - "titleChanged": true, - "ambiguity": null, - "notes": "The existing outer meets the house style; only the title changes." - }, - { - "number": 2, - "url": "https://github.com/hashintel/brunch-lite/pull/2", - "state": "OPEN", - "headRefName": "ln/fe-1362-demo-vehicle", - "linkedIssue": "FE-1362", - "sourceUpdatedAt": "2026-08-19T14:00:01Z", - "sourceTitle": "FE-1362: Decide the September demo vehicle", - "sourceTitleSha256": "213035e657b4ee4135ccdf4f4781e3b7a94d4a42d404ced75c5f821df9c4b371", - "sourceBodySha256": "def66364b5c84aaf3357de5f03701eea169d84972dceb6f35284fa16e281f6ce", - "proposedTitle": "FE-1362: Decide the September demo architecture", - "oldOuter": "This branch records the decision that guided the September demonstration and organizes the supporting planning material. It gives later work one documented starting point instead of leaving the demo shape implicit.", - "proposedOuter": "This branch records the decision that guided the September demonstration and organizes the supporting planning material. It gives later work one documented starting point instead of leaving the demo shape implicit.", - "extractionMethod": "single canonical wrapper extracted", - "sourceInnerRecord": "Recommendation doc (demo shell + artifact boundary) with evidence for the\n18 Aug integration discussion; planning tree migrated .scratch/ -> docs/planning/\nwith INDEX; agent docs gained issue-writing (contract/execution-record, voice\nand authority) and git-workflow (Graphite stacks, branch per Linear issue);\ndocumentation protocol gained the untracked drafts/ convention; CONTEXT.md\ngained Demo shell and Artifact boundary; inbox ingest of SDCPN/SAILS/voice/\ntranscript arrivals.\n\nCo-Authored-By: Claude Fable 5 ", - "sourceInnerSha256": "9ea766c6ed1b481260e6cdfa69d077f14aa6f9044c104f1625f69be708cf525d", - "innerRecord": "Recommendation doc (demo shell + artifact boundary) with evidence for the\n18 Aug integration discussion; planning tree migrated .scratch/ -> docs/planning/\nwith INDEX; agent docs gained issue-writing (contract/execution-record, voice\nand authority) and git-workflow (Graphite stacks, branch per Linear issue);\ndocumentation protocol gained the untracked drafts/ convention; CONTEXT.md\ngained Demo shell and Artifact boundary; inbox ingest of SDCPN/SAILS/voice/\ntranscript arrivals.\n\nCo-Authored-By: Claude Fable 5 ", - "innerSha256": "9ea766c6ed1b481260e6cdfa69d077f14aa6f9044c104f1625f69be708cf525d", - "normalizationNotes": "None; the canonical body and inner record are preserved byte-for-byte.", - "proposedBody": "This branch records the decision that guided the September demonstration and organizes the supporting planning material. It gives later work one documented starting point instead of leaving the demo shape implicit.\n\n
🏗️ Agent notes\n\nRecommendation doc (demo shell + artifact boundary) with evidence for the\n18 Aug integration discussion; planning tree migrated .scratch/ -> docs/planning/\nwith INDEX; agent docs gained issue-writing (contract/execution-record, voice\nand authority) and git-workflow (Graphite stacks, branch per Linear issue);\ndocumentation protocol gained the untracked drafts/ convention; CONTEXT.md\ngained Demo shell and Artifact boundary; inbox ingest of SDCPN/SAILS/voice/\ntranscript arrivals.\n\nCo-Authored-By: Claude Fable 5 \n\n
\n", - "proposedBodySha256": "def66364b5c84aaf3357de5f03701eea169d84972dceb6f35284fa16e281f6ce", - "bodyChanged": false, - "titleChanged": true, - "ambiguity": null, - "notes": "The existing outer meets the house style; only the title changes." - }, - { - "number": 1, - "url": "https://github.com/hashintel/brunch-lite/pull/1", - "state": "MERGED", - "headRefName": "docs/product-description", - "linkedIssue": "FE-1374", - "sourceUpdatedAt": "2026-08-19T13:59:58Z", - "sourceTitle": "FE-1374: Assemble the spec", - "sourceTitleSha256": "2349400d04eb80fe6d4f1253ea64eecc37cc01df5cc71141a92dbc1839396dbc", - "sourceBodySha256": "b034992e70ad0e9a8ab26ba321aaa87f91365d563c4bb4a021dd8c5cee3aed51", - "proposedTitle": "FE-1374: Assemble the elicitation harness specification", - "oldOuter": "", - "proposedOuter": "This branch assembles the elicitation harness specification and its companion product descriptions. It also records the second review round and the resulting changes to conversation history, absence states, evidence navigation, and reusable strategies.", - "extractionMethod": "whole source body treated as authoritative inner record and wrapped once", - "sourceInnerRecord": "Follow-through on the assembled elicitation-kernel spec (FE-1374; branch predates the one-branch-per-issue convention, so the range holds the spec's companion artifacts and its second review round):\n\n- **STE product description** (`product-description.md`) — what the product does in ASD-STE100 Simplified Technical English, twelve sections, with a technical-name table mapping product words back to spec vocabulary.\n- **Plain-prose product description** (`product-description-plain.md`) — the same ground in Google/GOV.UK plain-language style.\n- **Spec review round 2** folded into `spec.md` (+ glossary touch-ups in `CONTEXT.md`): session-log archive (archive-on-read, §9.6), `declined`/`deferred` absence states, pointer navigation for evidence spans, and the harness-shipped generic strategy quiver (§11.5).\n\nPaths are `.scratch/elicitation-kernel/…` as of this range; the stack above migrates `.scratch/` → `docs/planning/`.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n", - "sourceInnerSha256": "b034992e70ad0e9a8ab26ba321aaa87f91365d563c4bb4a021dd8c5cee3aed51", - "innerRecord": "Follow-through on the assembled elicitation-kernel spec (FE-1374; branch predates the one-branch-per-issue convention, so the range holds the spec's companion artifacts and its second review round):\n\n- **STE product description** (`product-description.md`) — what the product does in ASD-STE100 Simplified Technical English, twelve sections, with a technical-name table mapping product words back to spec vocabulary.\n- **Plain-prose product description** (`product-description-plain.md`) — the same ground in Google/GOV.UK plain-language style.\n- **Spec review round 2** folded into `spec.md` (+ glossary touch-ups in `CONTEXT.md`): session-log archive (archive-on-read, §9.6), `declined`/`deferred` absence states, pointer navigation for evidence spans, and the harness-shipped generic strategy quiver (§11.5).\n\nPaths are `.scratch/elicitation-kernel/…` as of this range; the stack above migrates `.scratch/` → `docs/planning/`.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n", - "innerSha256": "b034992e70ad0e9a8ab26ba321aaa87f91365d563c4bb4a021dd8c5cee3aed51", - "normalizationNotes": "Added one canonical Agent-notes wrapper around the unchanged source body.", - "proposedBody": "This branch assembles the elicitation harness specification and its companion product descriptions. It also records the second review round and the resulting changes to conversation history, absence states, evidence navigation, and reusable strategies.\n\n
🏗️ Agent notes\n\nFollow-through on the assembled elicitation-kernel spec (FE-1374; branch predates the one-branch-per-issue convention, so the range holds the spec's companion artifacts and its second review round):\n\n- **STE product description** (`product-description.md`) — what the product does in ASD-STE100 Simplified Technical English, twelve sections, with a technical-name table mapping product words back to spec vocabulary.\n- **Plain-prose product description** (`product-description-plain.md`) — the same ground in Google/GOV.UK plain-language style.\n- **Spec review round 2** folded into `spec.md` (+ glossary touch-ups in `CONTEXT.md`): session-log archive (archive-on-read, §9.6), `declined`/`deferred` absence states, pointer navigation for evidence spans, and the harness-shipped generic strategy quiver (§11.5).\n\nPaths are `.scratch/elicitation-kernel/…` as of this range; the stack above migrates `.scratch/` → `docs/planning/`.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\n\n
", - "proposedBodySha256": "0979c93127052da2249f61275c1bdc3d9b77744bb39473f37b45268c9ba56e27", - "bodyChanged": true, - "titleChanged": true, - "ambiguity": null, - "notes": "The source had no Agent-notes wrapper, as specified for this migration." - } - ] -} diff --git a/libs/@hashintel/brunch-agent/docs/archive/migrations/issue-pr-legibility-2026-08-20/data/github-source.json b/libs/@hashintel/brunch-agent/docs/archive/migrations/issue-pr-legibility-2026-08-20/data/github-source.json deleted file mode 100644 index 4eef66bf14a..00000000000 --- a/libs/@hashintel/brunch-agent/docs/archive/migrations/issue-pr-legibility-2026-08-20/data/github-source.json +++ /dev/null @@ -1,477 +0,0 @@ -[ - { - "author": { - "id": "MDQ6VXNlcjEyNDI4NjQ=", - "is_bot": false, - "login": "lunelson", - "name": "Lu Nelson" - }, - "baseRefName": "ln/fe-1451-fold-protocol", - "body": "## Stack Context\n\nFE-1433 sequences the Petrinaut integration from the proven Flue suspension and AI SDK seams. This branch is the ask suspend/return slice, stacked on FE-1436's durable transport; its panel-side counterpart is the FE-1448 host interactive-tool API in hashintel/hash (PR hashintel/hash#9249).\n\n## What?\n\n- `transport-aisdk` holds a `brunch_ask` open on the wire: awaiting client tool with a stable `toolCallId`, the harness's minted affordance output withheld\n- the tool-result follow-up POST — previously refused wholesale — is admitted exactly when it carries the pending ask's correlated `{ answer }` submission\n- `@brunch/core` gains the pure protocol: `pendingAskAffordanceId`, `decideAskReplyAdmission`, and the `AskSubmission` submitted-output contract\n- the application's new `askReply` seam admits against durable Flue history before any dispatch, then resumes the conversation as a fresh user dispatch that the binding binds as the user-affordance reply\n- opt-in inspection gains `ask-await`, `ask-reply-admitted`, `ask-reply-refused`\n\n## Why?\n\nFE-1449 requires provenance to settle at the wire boundary: a human answer travels tool-output-shaped but is not a machine tool result. Only the currently pending ask's correlated submission may become user evidence — stale/duplicate submissions get `409 ask_not_pending`, forged ids `409 ask_mismatch`, malformed submissions `400 invalid_ask_submission`, and machine-only follow-ups (mutation outputs, diagnostics) keep FE-1436's `422`. Concurrent duplicates collapse at the substrate via the `{conversationId}:ask:{toolCallId}` idempotency key.\n\nRemaining for full FE-1449 acceptance: petrinaut-website registers the ask component through the FE-1448 `aiAssistant` API (hash-side branch); the implementation record names it.\n\n## Verification\n\n- `bun run smoke` — lint, fmt, typecheck, 163 tests\n- `bun run build`\n- wire contract tests for the translated ask part and each refusal class\n- end-to-end run over the committed application route: the actual elicitor asks, the correlated answer resumes the same Flue conversation, a replayed duplicate is refused before dispatch\n", - "closedAt": null, - "createdAt": "2026-08-19T13:59:43Z", - "headRefName": "ln/fe-1449-structured-ask", - "mergedAt": null, - "number": 25, - "state": "OPEN", - "title": "FE-1449: A structured brunch question suspends and resumes visibly in Petrinaut", - "updatedAt": "2026-08-20T08:46:31Z", - "url": "https://github.com/hashintel/brunch-lite/pull/25" - }, - { - "author": { - "id": "MDQ6VXNlcjEyNDI4NjQ=", - "is_bot": false, - "login": "lunelson", - "name": "Lu Nelson" - }, - "baseRefName": "ln/fe-1435-aisdk-panel-spike", - "body": "The spikes proved the required seams separately, but the elicitor still needed to answer real chat turns in the target panel. This branch connects that path and preserves the evidence needed to verify completed, failed, and cancelled turns.\n\n
🏗️ Agent notes\n\n## Stack Context\n\nFE-1433 sequences a durable Petrinaut integration from the proven Flue suspension and AI SDK panel seams. This branch is the first production-intent transport slice, stacked on FE-1435.\n\n## What?\n\n- adds a substrate-neutral harness reply protocol and `transport-aisdk` encoder\n- mounts the actual elicitor behind Petrinaut's `/api/chat` contract\n- commits the local Petrinaut launcher, opt-in JSONL inspector, and wire fixtures\n- validates external chat requests with Valibot and keeps transport diagnostics outside user evidence\n- records one truthful terminal sequence for completed, failed, and aborted turns\n\n## Why?\n\nLater structured-ask and editor-tool slices need a durable, inspectable server path rather than the spike's disposable replay harness. The committed application-route golden now crosses the real application, Flue projector, and transport while normalizing only dynamic identifiers and delta segmentation.\n\n## Verification\n\n- `bun run lint`\n- `bun run fmt`\n- `bun run typecheck`\n- `bun test` — 152 passed, 0 failed\n- `bun run build`\n- independent standards/spec review plus focused remediation closure review\n\n
\n", - "closedAt": null, - "createdAt": "2026-08-19T12:43:42Z", - "headRefName": "ln/fe-1436-transport-aisdk", - "mergedAt": null, - "number": 24, - "state": "OPEN", - "title": "FE-1436: The elicitor answers conversation turns in Petrinaut's real chat panel", - "updatedAt": "2026-08-19T14:00:38Z", - "url": "https://github.com/hashintel/brunch-lite/pull/24" - }, - { - "author": { - "id": "MDQ6VXNlcjEyNDI4NjQ=", - "is_bot": false, - "login": "lunelson", - "name": "Lu Nelson" - }, - "baseRefName": "ln/fe-1436-transport-aisdk", - "body": "Agent-written issues, comments, and PR descriptions had become hard to scan. This branch sets a compact title format and a plain-language summary above collapsed agent working detail, then applies the rules to the issue that introduced them.\n\n
🏗️ Agent notes\n\nAgent-written issues, comments, and PR descriptions had become hard to scan. This PR gives issues compact, active-verb task titles and a human-owned plain-language summary, with agent working detail in a collapsed `🏗️ Agent notes` section. It applies the same rules to [FE-1451](https://linear.app/hash/issue/FE-1451/keep-issues-comments-and-prs-easy-to-scan), which serves as a live example.\n\n
🏗️ Agent notes\n\n## What changed\n\n- `docs/agents/issue-writing.md` defines compact task-shaped titles, the collapsed section, visible comments limited to one decision or change, and vocabulary rules by audience.\n- The human driving the work owns the visible summary. An agent fetches and preserves that text before writing on the person's behalf; `🏗️ Agent notes` remains agent-maintained.\n- Issues quote and link user, stakeholder, or teammate feedback when the issue's audience can read the source.\n- The org technical-writing rules apply to all prose. The earlier draft applied the word list only to visible summaries.\n- The coined name \"the fold\" was removed rather than added to the glossary. The house word list asks writers to use \"collapsed section,\" and formalizing the coined term would have preserved the conflict.\n- `docs/agents/issue-tracker.md` records the safe raw-description edit process and the shorter project-update format.\n- `AGENTS.md`, `docs/INDEX.md`, and `docs/planning/_shared/CONVERGENCE.md` point to the current rules and state.\n\n## Verification\n\n- `bun run smoke`: 138 tests passed.\n- `bun run build`: passed.\n- Focused document-index and formatting checks passed after the final wording changes.\n- Linear round-trip: FE-1451 retains its human-owned summary and collapsed `🏗️ Agent notes` after update and read-back.\n\n## Arc close\n\n- Inbox sweep: clean.\n- INDEX pass: the CONVERGENCE digest matches the seventh evaluation.\n- CONVERGENCE re-evaluation: FE-1451 changes reporting and does not change the delivery order.\n- Registry audit: FE-1451 is a child of FE-1401; this branch introduces no orphan.\n- Tense repair: the living record says the rules are implemented on this branch.\n- Plain-language review: the required forked reviewer could not start because the subagent service failed twice. A manual strain read found and repaired the coined term, the partial application of the house word list, and the unsupported claim that an audit already checks the label bytes.\n\n
\n\n
\n", - "closedAt": null, - "createdAt": "2026-08-19T12:19:45Z", - "headRefName": "ln/fe-1451-fold-protocol", - "mergedAt": null, - "number": 23, - "state": "OPEN", - "title": "FE-1451: Keep issues, comments, and PRs easy to scan", - "updatedAt": "2026-08-19T14:00:37Z", - "url": "https://github.com/hashintel/brunch-lite/pull/23" - }, - { - "author": { - "id": "MDQ6VXNlcjEyNDI4NjQ=", - "is_bot": false, - "login": "lunelson", - "name": "Lu Nelson" - }, - "baseRefName": "ln/fe-1434-suspension-spike", - "body": "The integration needed proof that the real chat panel accepts the stream produced by the harness. This spike exercises that path and records the resulting wire evidence before the production slice builds on it.\n\n
🏗️ Agent notes\n\n## Outcome\n\nProves that harness-level parts translated to AI SDK v6 UI-message-stream frames can drive Petrinaut's real, unmodified chat panel at the pinned dependency versions.\n\nThe live panel rendered reasoning, text, and a provider-executed server tool; executed two client tools against the editor; posted both outputs together in one automatic follow-up; and added diagnostics context through its existing transport decorator. The complete two POST bodies and two SSE streams are frozen as fixtures with semantic golden tests.\n\nThe external `hashintel/hash` checkout remained clean at `1046b5c881cd00cf205b4895348b022934d66b4a`; no prototype source was retained.\n\n## Verification\n\n- `bun run lint`\n- `bun run fmt`\n- `bun run typecheck`\n- `bun test` (142 tests)\n- `bun run build`\n\n
\n", - "closedAt": null, - "createdAt": "2026-08-19T09:55:29Z", - "headRefName": "ln/fe-1435-aisdk-panel-spike", - "mergedAt": null, - "number": 22, - "state": "OPEN", - "title": "FE-1435: Spike: does a harness-driven stream drive Petrinaut's real chat panel?", - "updatedAt": "2026-08-19T14:00:35Z", - "url": "https://github.com/hashintel/brunch-lite/pull/22" - }, - { - "author": { - "id": "MDQ6VXNlcjEyNDI4NjQ=", - "is_bot": false, - "login": "lunelson", - "name": "Lu Nelson" - }, - "baseRefName": "ln/fe-1433-petrinaut-integration-spec", - "body": "The planned integration depends on whether suspended turns can resume with several client-tool results together. This spike answers that question with recorded evidence and identifies the remaining obligations.\n\n
🏗️ Agent notes\n\n## Outcome\n\nProves that Flue 2.0.3 can terminate with a batched client-tool interaction pending and resume through one later non-user `signal` dispatch with per-session state and every tool-call ID intact.\n\nThe durable verdict selects a batch-as-pending-slot variant. It records 3-result and 100-result evidence, two dispatches and two model turns in both cases, rejection of native `tool-result` admission, non-user capture-store refusal, and the remaining retry/mismatch obligations for FE-1438.\n\nThe disposable probe was removed after capture; the verdict and normalized JSON evidence retain its source commit and exact command.\n\n## Verification\n\n- `bun run lint`\n- `bun run fmt`\n- `bun run typecheck`\n- `bun test` (138 tests)\n- `bun run build`\n\n
\n", - "closedAt": null, - "createdAt": "2026-08-19T09:55:16Z", - "headRefName": "ln/fe-1434-suspension-spike", - "mergedAt": null, - "number": 21, - "state": "OPEN", - "title": "FE-1434: Spike: does Flue turn suspension carry batched client-tool round-trips?", - "updatedAt": "2026-08-19T14:00:32Z", - "url": "https://github.com/hashintel/brunch-lite/pull/21" - }, - { - "author": { - "id": "MDQ6VXNlcjEyNDI4NjQ=", - "is_bot": false, - "login": "lunelson", - "name": "Lu Nelson" - }, - "baseRefName": "ln/fe-1432-review-remediation", - "body": "The demo needed a settled integration direction before implementation could proceed. This branch records the decision to serve the existing chat panel from a remote brunch-agent server and defines the delivery work that follows.\n\n
🏗️ Agent notes\n\nFE-1433: Record the in-Petrinaut staging decision and its integration spec\n\nThe 2026-08-18 integration meeting overturned the demo-shell recommendation:\nthe September demo stages inside demo.petrinaut.org, with the elicitor as a\nremote server behind the chat panel's host-pluggable aiAssistant transport.\nADR-0004 records the decision (staging, the @hashintel/brunch-agent rename and\nmonorepo import, the apps-are-the-only-meeting-point boundary discipline, the\nui-shell principal, the N3 amendment retiring the demo shell) and supersedes\nrecommendation-demo-vehicle.md.\n\nThe integration spec (FE-1433) specifies the build: one primary test seam (the\nAI SDK UI-message-stream wire contract), the external-tool round-trip protocol\nsharing the ask protocol's suspension floor while differing in cardinality,\nbinding, and provenance (tool outputs are machine entries, never anchorable\nevidence), the transport-aisdk package, the opaque owner key on the storage\nport, and the two gating spikes (Flue suspension carrying batched client-tool\nround-trips; the Pi-to-AI-SDK stream adapter, whose transcript becomes the\nwire seam's golden fixtures).\n\nINDEX updated: recommendation-demo-vehicle marked superseded; both new\ndocuments indexed; docs gate green.\n\nCo-Authored-By: Claude Fable 5 \n\nFE-1433: File the integration delivery graph — sixth cross-map evaluation\n\nThe ds-write-tickets pass over the integration spec published nine execution\ntickets under FE-1433 (FE-1434–FE-1442) with native blocking relations, and\nthis change deposits the coordination record: the sixth evaluation paragraph\n(the sub-graph joins the strategy without displacing the spine; the spikes\njoin the now-band beside FE-1393/FE-1422), the delivery graph itself (mirrored\nin FE-1433's execution record), the FE-1423 re-scope (gates survive the demo\nshell's retirement and now guard the remote elicitor server; auth → FE-1439,\ndeployment → FE-1441), and the N3 watch-item repair (ADR-0004 amended N3, so\nthe living-prototype charter would shape apps/dev or the deployed server, not\nan apps/demo).\n\nThe one fog-sensitive edge is recorded as such: FE-1438 and FE-1395 share the\npending-slot batching ground, held apart by a coordination edge rather than a\nblocker — whichever lands first writes the §7.3/§7.4 amendment.\n\nCo-Authored-By: Claude Fable 5 \n\n
\n", - "closedAt": null, - "createdAt": "2026-08-18T16:36:43Z", - "headRefName": "ln/fe-1433-petrinaut-integration-spec", - "mergedAt": null, - "number": 20, - "state": "OPEN", - "title": "FE-1433: The elicitor serves demo.petrinaut.org's chat panel from a remote brunch-agent server", - "updatedAt": "2026-08-19T14:00:30Z", - "url": "https://github.com/hashintel/brunch-lite/pull/20" - }, - { - "author": { - "id": "MDQ6VXNlcjEyNDI4NjQ=", - "is_bot": false, - "login": "lunelson", - "name": "Lu Nelson" - }, - "baseRefName": "ln/fe-1392-settlement-sweep", - "body": "The stack had review findings without a clear final disposition. This branch resolves them by fixing the actionable cases and recording ownership or a supported refusal for the rest.\n\n
🏗️ Agent notes\n\nFE-1432 executes the 2026-08-18 cross-stack review-remediation queue and closes its capture channel.\n\nWhat this establishes:\n\n- Capture-store command admission and persisted parsing share the same closure rules: one terminal event per issue, pairwise-disjoint open conflicts, and active references for every open conflict.\n- Baseline interviewer and expert truncation survives in checkpoint metadata; partial output stops before downstream model consumption, and expert resume regenerates the incomplete reply.\n- Valibot declarations and imports agree in both directions, while the walking-skeleton oracle reports absent context as `false` instead of throwing.\n- New dependency resolutions observe a seven-day release-age quarantine.\n- All 15 residual review threads were replied to and resolved: 7 fixes, 2 findings owned by FE-1385/FE-1393, and 6 evidence-backed refusals.\n- The three tooling-side graduation proposals remain owned by FE-1401's `ds-induct`/lens-registry lane.\n\nCommits:\n\n- `380411f` — preserve capture-store closure and prove local-store setup\n- `f71aa9e` — retain baseline completion metadata\n- `bb46941` — make capability and walking-skeleton gates honest\n- `2814090` — quarantine fresh dependency releases\n- `74b535c` — settle the remediation ledger\n\nVerification: OxLint, Oxfmt, TypeScript, 138 tests, workspace build, and an independent adversarial review of the capture lifecycle. A live review-thread refresh found zero unresolved FE-1432 threads; the sole repository-wide unresolved thread is on child PR #20 and belongs to FE-1433.\n\n
\n", - "closedAt": null, - "createdAt": "2026-08-18T16:36:42Z", - "headRefName": "ln/fe-1432-review-remediation", - "mergedAt": null, - "number": 19, - "state": "OPEN", - "title": "FE-1432: The stack's open review threads are adjudicated: fixed, owned, or refused on the record", - "updatedAt": "2026-08-19T14:00:29Z", - "url": "https://github.com/hashintel/brunch-lite/pull/19" - }, - { - "author": { - "id": "MDQ6VXNlcjEyNDI4NjQ=", - "is_bot": false, - "login": "lunelson", - "name": "Lu Nelson" - }, - "baseRefName": "ln/fe-1391-entry-projection", - "body": "This branch connects completed conversation work to the first captured statement. It makes settlement occur at the appropriate lifecycle point and keeps the resulting evidence current.\n\n
🏗️ Agent notes\n\nTrigger settlement from the finish hook, extract against the plugin-declared grade floor, refresh archived history adjacent to atomic application, and reopen refused ranges without weakening session-qualified provenance.\n\n
\n", - "closedAt": null, - "createdAt": "2026-08-18T16:36:40Z", - "headRefName": "ln/fe-1392-settlement-sweep", - "mergedAt": null, - "number": 18, - "state": "OPEN", - "title": "FE-1392: Settlement trigger and sweep — the first captured statement", - "updatedAt": "2026-08-19T14:00:27Z", - "url": "https://github.com/hashintel/brunch-lite/pull/18" - }, - { - "author": { - "id": "MDQ6VXNlcjEyNDI4NjQ=", - "is_bot": false, - "login": "lunelson", - "name": "Lu Nelson" - }, - "baseRefName": "ln/fe-1422-ask-protocol", - "body": "Later capture work needed a durable way to locate and anchor prior conversation entries. This branch establishes that path and retains enough history for those anchors to be resolved reliably.\n\n
🏗️ Agent notes\n\ndocs: settle Flue projection and compaction\n\nRecord the installed @flue/runtime and @flue/sdk 2.0.3 contract and implementation evidence for FE-1391 B1/B2. Correct the archive premise to the materialized history surface with host-owned routing and evolving-message merge semantics. Trace append-only compaction and state ownership, then reshape FE-1386 to one behavioral upgrade pin and reconcile the living ledgers.\n\nfeat: anchor captures to archived Flue history\n\nRead the public materialized history through a host-supplied URL resolver and transport, and co-locate a versioned session log with capture state behind a binding-private archive capability. Archive ordinals preserve the session-plus-range pointer while Flue identities remain provenance and evolving messages retain distinct versions.\n\nReplace caller-supplied spans with quote-only evidence commands resolved atomically against the archive. Pin mounted in-process routing, affordance classification, non-user refusal, legacy provisioning, parse-on-read, and independent pointer retrieval. Keep the real-compaction compatibility pin with FE-1386 and move refresh-before-sweep orchestration to FE-1392.\n\n
\n", - "closedAt": null, - "createdAt": "2026-08-18T16:36:38Z", - "headRefName": "ln/fe-1391-entry-projection", - "mergedAt": null, - "number": 17, - "state": "OPEN", - "title": "FE-1391: Durable entry projection, harness-resolved anchoring, and the session-log archive", - "updatedAt": "2026-08-19T14:00:26Z", - "url": "https://github.com/hashintel/brunch-lite/pull/17" - }, - { - "author": { - "id": "MDQ6VXNlcjEyNDI4NjQ=", - "is_bot": false, - "login": "lunelson", - "name": "Lu Nelson" - }, - "baseRefName": "ln/fe-1405-payload-interiors", - "body": "The question-and-reply behavior was tied too closely to one runtime binding. This branch moves the portable part into the core so other application surfaces can use the same protocol.\n\n
🏗️ Agent notes\n\nMove affordance minting, the one-live-affordance decision and refusal, reply-binding signal construction, and render-invariant instruction fragments into a pure core module. The Flue binding now translates hooks, state updates, data writes, and turn termination through that protocol instead of owning its decisions.\n\nCharacterization tests pin every extracted value and branch, while the unchanged walking-skeleton integration test continues to prove the real substrate path, duplicate-ask rejection, durable output, reply binding, and absence of instruction wakes.\n\n
\n", - "closedAt": null, - "createdAt": "2026-08-18T16:36:35Z", - "headRefName": "ln/fe-1422-ask-protocol", - "mergedAt": null, - "number": 16, - "state": "OPEN", - "title": "FE-1422: The ask protocol is substrate-portable: mechanism moves from the Flue binding into core", - "updatedAt": "2026-08-19T14:00:24Z", - "url": "https://github.com/hashintel/brunch-lite/pull/16" - }, - { - "author": { - "id": "MDQ6VXNlcjEyNDI4NjQ=", - "is_bot": false, - "login": "lunelson", - "name": "Lu Nelson" - }, - "baseRefName": "ln/fe-1424-docs-housekeeping", - "body": "The CPS plugin needed concrete payload shapes grounded in the baseline evidence. This branch drafts those shapes and records the open pressures that later work must resolve.\n\n
🏗️ Agent notes\n\nThis branch settles the FE-1405 payload-interiors arc and closes it.\n\n**What it establishes.** The session found that Layer A's definition sentence (\"active captures read through the payload type system\") describes an evidence ledger, not a model of the domain. Two artifacts fix that:\n\n- **ADR-0003 (three-register IR)**: typed assertions (active captures) are folded by a pure, plugin-declared fold into the elicited model — the IR proper — which projections consume without rereading the transcript. All interpretation happens at write time as contestable captures; promotion never refusal; an acceptance oracle (a second projection consumes the model alone) keeps the read path semantics-free. \"No second store\" survives: the model is a derivation, never a persistence surface.\n- **Provisional plugin-contract spec** (`docs/planning/process-model-elicitation/plugin-contract-spec.md`, published as FE-1431): a plugin is two schemas and two small tables — model schema, proposal catalog (typed proposals with interiors from a standard library), fold table (overrides only; rules mostly derive), demand table. Harness machinery is pure functions classified by which declaration each reads. The provisional marker comes off after a full worked pass across three plugin targets (the FE-1397 precedent); the open strains (grade sources, support closure, temporal patterns, sweep-time concentration, the absence-locator envelope pressure) are first-class sections with owning tickets, not folded into the design.\n\nThe capture envelope is untouched — the one confirmed pressure (absence captures carry no locator) is recorded at the FE-1383/FE-1357 seam in CONVERGENCE, not forked around.\n\n**Arc close** (second commit): SDCPN inbox arrival settled to `docs/reference/` with a skeptical digest; INDEX trued; CONVERGENCE re-evaluated with a dated third sequencing pass (the convergence collision now waits only on the build spine reaching FE-1392); registry audit re-parented two orphans (FE-1399/FE-1400 → FE-1383); CONTEXT.md's IR entry rewritten per ADR-0003 and a Grade entry added.\n\nGates: oxlint clean, oxfmt clean, typecheck clean, 100/100 tests, build green.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\n
\n", - "closedAt": null, - "createdAt": "2026-08-18T11:41:51Z", - "headRefName": "ln/fe-1405-payload-interiors", - "mergedAt": null, - "number": 15, - "state": "OPEN", - "title": "FE-1405: Draft the CPS payload interiors: annotated shapes for the ten kinds, worked from baseline utterances", - "updatedAt": "2026-08-19T14:00:22Z", - "url": "https://github.com/hashintel/brunch-lite/pull/15" - }, - { - "author": { - "id": "MDQ6VXNlcjEyNDI4NjQ=", - "is_bot": false, - "login": "lunelson", - "name": "Lu Nelson" - }, - "baseRefName": "ln/fe-1419-contract-closure", - "body": "The documentation protocol described a complete lifecycle but had not yet been exercised as one. This branch runs that lifecycle, gives each document a clear home, and adds a check that keeps the index honest.\n\n
🏗️ Agent notes\n\n## What this establishes\n\nCloses FE-1424. The documentation protocol (`docs/agents/documentation.md`) specified zones and a settle sweep that were never executed; this branch runs it, amends it where practice had outgrown it, and makes the parts that can be mechanical, mechanical. Ratified by Lu 2026-08-17.\n\n**Commit 1 — settle + reshape.** The seven inbox documents settle into `docs/reference/` (the protocol's specified-but-never-created destination). `docs/planning/` splits by kind: cross-effort living documents (`CONVERGENCE`, `topology`, the Flue cheatsheet) into `_shared/`; dated arc records into `legibility-sweep/`. The planning top level now holds only directories, so living-vs-record is structural, not judged. All links re-pathed in both directions (14 files, verified by walking every relative link); Linear descriptions carrying old paths repaired (FE-1357/59/66/70, FE-1419, FE-1422/23).\n\n**Commit 2 — arc-close.** Three protocols each carried an arc-close step in prose with nothing firing them together; `docs/agents/arc-close.md` is now the single triggerable checklist (inbox sweep → INDEX pass → CONVERGENCE re-evaluation → registry audit → tense repair), with a content-free `/arc-close` skill wrapper. `documentation.md` gains the `_shared/` zone, the living-vs-record rule, the flattened drafts convention (`docs/planning//drafts/`), and the ephemera-reference convention (issue IDs glossed at first mention; load-bearing only in the tracking layer; tense repair at arc close).\n\n**Commit 3 — the gate.** `test/docs-index.test.ts`: every file under `docs/` covered by an INDEX row, every row resolves, planning top level directories-only, every `docs/agents/` protocol named in `AGENTS.md`. Enumerated live, vacuous-pass guarded, red-proved in both directions per rule. Freshness deliberately stays procedural — mechanizing it would build a dead gate.\n\n## Not done here\n\nOld Linear *comments* citing pre-move paths are left as historical record (descriptions were repaired); git log resolves the moves. Two pre-existing broken links found and left: the never-committed prototype HTML in `elicitation-kernel/issues/11`, and links inside the Amp transcript's embedded skill text (verbatim record, unfixable without corrupting it).\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\n
\n", - "closedAt": null, - "createdAt": "2026-08-17T13:12:17Z", - "headRefName": "ln/fe-1424-docs-housekeeping", - "mergedAt": null, - "number": 14, - "state": "OPEN", - "title": "FE-1424: The documentation protocol runs end to end: inbox settled, planning reshaped, index gated, arc-close triggerable", - "updatedAt": "2026-08-19T14:00:20Z", - "url": "https://github.com/hashintel/brunch-lite/pull/14" - }, - { - "author": { - "id": "MDQ6VXNlcjEyNDI4NjQ=", - "is_bot": false, - "login": "lunelson", - "name": "Lu Nelson" - }, - "baseRefName": "ln/fe-1401-legibility-sweep", - "body": "This branch closes places where the capture store and verification checks promised stronger guarantees than they actually enforced. It aligns the implementation and the checks with the properties the stack relies on.\n\n
🏗️ Agent notes\n\nExecutes the FE-1419 refactor queue (`docs/planning/refactor-queue-2026-08-14.md`) — nine commits that give each contract one owner and make negative results precise, so green gates and successful commands stop claiming more than their implementations support.\n\nVerification layer: known-gap closure becomes an explicit ledger whose entries the proof commit deletes (no more citation-token inference); CI gates match exact declared commands; substrate access in tests is a reviewed inventory asserted as set equality; resolver probes classify only module-not-found as \"no\" and carry positive controls; asset serving accepts the producer's real name space behind property checks and per-segment encoding (closing a latent backslash-traversal path a probe found), translating exactly four absence errnos and propagating everything else.\n\nCapture store: evidence-range ordering, the issue contract, and set-equality resolution accounting each live in one definition enforced at command time and parse time alike; all event write paths deep-copy (the aliasing hole is closed); conflicts open only over two-plus distinct active captures and pin them until a user-cited resolution frees them — jointly, every open conflict is resolvable and stays resolvable, closing the invariant-2 bypass. Commit 9 locks the closure property totally: every command any test accepts round-trips through the persisted parser (an injected fault fails 19 of 21 tests), and refused commands leave the store file byte-identical.\n\nEvery commit was red-proved in both directions and independently reviewed before landing. Known residue, deliberately out of scope for a test-only commit: a `-0` capture value survives the command surface but returns as `0` through JSON — recorded on FE-1419. Fixes four refusal messages to state what was checked rather than what a reader might hope was verified.\n\nAlso carries the FE-1401 remediation-sweep docs that landed during execution: the two-map CONVERGENCE record with seam section, issue glosses, and ordering strategies; the registry rule in issue-tracker.md; the Flue canonical-patterns audit; and the deep-read records.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\n
\n", - "closedAt": null, - "createdAt": "2026-08-17T11:36:20Z", - "headRefName": "ln/fe-1419-contract-closure", - "mergedAt": null, - "number": 13, - "state": "OPEN", - "title": "FE-1419: Close the seams where the capture store and verification gates claim more than they enforce", - "updatedAt": "2026-08-19T14:00:18Z", - "url": "https://github.com/hashintel/brunch-lite/pull/13" - }, - { - "author": { - "id": "MDQ6VXNlcjEyNDI4NjQ=", - "is_bot": false, - "login": "lunelson", - "name": "Lu Nelson" - }, - "baseRefName": "ln/fe-1390-capture-store", - "body": "The legibility review produced findings that needed durable documentation and follow-up ownership. This branch records those findings, repairs the affected planning material, and makes the remaining work visible.\n\n
🏗️ Agent notes\n\nFE-1401: Land the legibility session's renderings, audit, and penciled directions\n\nThree documents promoted from drafts/ by Lu, with their post-move repairs\n(self-descriptions, relative links) and editorial additions:\n\n- ir-design-plain.md — plain-prose rendering of the IR design (same content\n commitments; original authoritative). The rendering pass doubled as review:\n seven strain findings on FE-1401, the load-bearing one being the loss\n report's unresolved unit of loss (capture vs. capture-facet).\n- notes/research-patterns-audit.md — ~30 research imports in 7 families,\n each with plain explanation, provenance, evidence grade, and where it\n landed; strain appendix (8 findings, incl. the betting-questions\n misattribution and the v0-vs-IDEA quantile divergence).\n- notes/penciled-directions-2026-08-14.md — 8 penciled directions with\n firming actions and marked editorial reflections (schema as forcing\n function, plugin manifest sketch, mode oscillation, activation probes,\n incorporation rubric, super-map question, write-time check tiers,\n per-domain machinery inventory).\n\nINDEX.md gains rows for all three plus two pre-existing gaps: ir-design.md\nand ir-worked-examples.md were never indexed.\n\nSession record: FE-1401 and its accrual comments; new tickets FE-1402–1407.\n\nCo-Authored-By: Claude Fable 5 \n\nprep refactor queue, and flue analysis\n\nSigned-off-by: Lu Nelson \n\n
\n", - "closedAt": null, - "createdAt": "2026-08-14T14:14:56Z", - "headRefName": "ln/fe-1401-legibility-sweep", - "mergedAt": null, - "number": 12, - "state": "OPEN", - "title": "FE-1401: Resolve the follow-ups from the stack legibility session", - "updatedAt": "2026-08-19T14:00:17Z", - "url": "https://github.com/hashintel/brunch-lite/pull/12" - }, - { - "author": { - "id": "MDQ6VXNlcjEyNDI4NjQ=", - "is_bot": false, - "login": "lunelson", - "name": "Lu Nelson" - }, - "baseRefName": "ln/fe-1389-walking-skeleton", - "body": "This branch gives captured evidence a durable local home with an explicit write path. It establishes reliable history for later model work while leaving consumption work to subsequent branches.\n\n
🏗️ Agent notes\n\nAdds the capture store — the durable, session-independent truth of a target-document — as an append-only snapshot of captures, issues, and events, with every status derived at read time instead of stored. Writes go through one pure command function that returns either a whole new snapshot or a typed refusal, so a sweep applies completely or not at all, and the Flue binding implements the storage port over it with a JSON file, tmp-and-rename, and per-path serialized writes. Capture identity is content-keyed with epistemic status excluded, which makes re-sweeps idempotent and forces revised readings through explicit supersession. The session-log-archive half of the storage port (spec §9.6) and write-time verification of evidence pointers are not in this branch, and nothing consumes the store yet.\n\nDeep-read record: `docs/planning/process-model-elicitation/notes/deep-read-fe-1390.md` (FE-1401 remediation sweep); the plain rendering with strain report is `docs/planning/process-model-elicitation/capture-store-plain.md`; contract-closure work is queued as FE-1419.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\n
\n", - "closedAt": null, - "createdAt": "2026-08-13T16:01:17Z", - "headRefName": "ln/fe-1390-capture-store", - "mergedAt": null, - "number": 11, - "state": "OPEN", - "title": "FE-1390: Capture envelope, storage port, and the local capture store", - "updatedAt": "2026-08-19T14:00:15Z", - "url": "https://github.com/hashintel/brunch-lite/pull/11" - }, - { - "author": { - "id": "MDQ6VXNlcjEyNDI4NjQ=", - "is_bot": false, - "login": "lunelson", - "name": "Lu Nelson" - }, - "baseRefName": "ln/fe-1400-close-silent-gaps", - "body": "This branch proves the first end-to-end question-and-reply path through the real application harness. It establishes that a question can pause a turn, persist, and bind the later answer correctly.\n\n
🏗️ Agent notes\n\nThis turns the ask seam into a working turn-suspension protocol: the tool mints a free-text affordance, parks it in the pending-affordance slot, returns it on the tool output part where its identity is durable, and terminates the turn — then the next user dispatch clears the slot and gets bound to that affordance by the harness itself, announced to the model as a signal rather than left to the model's memory. Because the pending question is never interpolated into the instructions, the instruction string cannot change between turns, which is what removes the wasted \"instructions updated\" wake turn the ticket-10 prototype hit once per question. The dev app is the proof: a chat UI plus an integration test that boots the real Flue runtime and the real Hono app in one process, drives them over a fetch shim with a scripted faux provider, and asserts in one shot that the reply binding reached the model, the affordance survived durably, no advisory wake appeared, and a second ask in the same batch was refused.\n\nDeep-read record: `docs/planning/process-model-elicitation/notes/deep-read-fe-1389.md` (FE-1401 remediation sweep); hardening follow-ups filed as FE-1420.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\n
\n", - "closedAt": null, - "createdAt": "2026-08-13T16:01:14Z", - "headRefName": "ln/fe-1389-walking-skeleton", - "mergedAt": null, - "number": 10, - "state": "OPEN", - "title": "FE-1389: Walking skeleton — the harness asks a free-text question and binds the reply", - "updatedAt": "2026-08-19T14:00:14Z", - "url": "https://github.com/hashintel/brunch-lite/pull/10" - }, - { - "author": { - "id": "MDQ6VXNlcjEyNDI4NjQ=", - "is_bot": false, - "login": "lunelson", - "name": "Lu Nelson" - }, - "baseRefName": "ln/fe-1397-worked-examples", - "body": "This branch resolves the remaining review findings where checks or local tools could report success without proving the intended behavior. The result makes those failure paths visible and testable.\n\n
🏗️ Agent notes\n\nExecutes the FE-1400 bundle: the ten verified findings from the FE-1397 tie-off review, plus the cleanup tail, as small always-green commits. Each strengthened gate was red-proved — the violation it guards against was temporarily introduced and the gate watched go red — before restoring green.\n\n**Docs truth:** the AGENTS.md triage one-liner now matches the triage doc's state/label mapping.\n\n**Test integrity:** build-artifact witnesses are now strings unique to the guarded modules (`createAgentRouter`/`sqlite` both matched bootstrap code regardless of what bundled); the first-statement check reuses the shared directive pattern instead of a literal compare; the workspace walker's source/test partition is total (nested `src/test/` files no longer escape every invariant); the CI-gate check parses the workflow so commented-out or `if: false` gates read as absent; known-gaps predicates require an explicit `closes-gap: ` citation in a real test instead of `existsSync` on a guessed path — a stub can't close a gap, a differently-named real closure can. FWIW the citation-token design earned its keep immediately: the first draft accepted any prose mention of the gap id, and a doc comment in this branch's own new test closed a gap.\n\n**Dev app:** the conversation store's default path anchors to the module (`db-path.ts`) instead of the launch directory, with tests for cwd-independence and the set-but-empty env override (which would have opened an anonymous temp database).\n\n**Baseline runner:** the hand-rolled HTTP client is gone in favor of `@anthropic-ai/sdk` (network-error retries, retry-after, cache-token usage in the totals); truncation survives continuation stitching and is reported with the `--continue-final` escape hatch; fence matching tolerates the info-string forms real runs produce, and a delivered run that yields no extractable artifact — or an ambiguous multi-block one — says so loudly. The post-review pass also added an explicit SDK timeout (the doubled retry budget tripped the SDK's 10-minute non-streaming guard), no-clobber guards for fresh/`--resume` runs, and seam-exact stitching.\n\n**Cleanup tail:** assets serve via a `/assets/*` wildcard and hono's MIME table (nested paths now work); CI builds once (root build inside `bun test`'s artifact suite, exercising every package's build script) and caches bun installs; the root build filter is `@brunch/*`; the runner's parallel `expertMessages` array and dead checkpoint field are gone; the boundary suite derives its topology pin from the spec's §12.2 block and its physical-resolution probes from the tree.\n\nTwo review findings deliberately not taken: swapping the hand-rolled asset handler for hono's `serveStatic` middleware (the strict dotfile/extension refusal semantics are pinned by tests and the ticket asked for framework routing + MIME table, both done), and de-obfuscating the composed `MODEL_KEY_NAME` pattern (an exclusion list for pattern-defining files has its own silent-failure mode; the composition predates this branch and is documented in place).\n\nCloses FE-1400.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\n
\n", - "closedAt": null, - "createdAt": "2026-08-13T13:49:12Z", - "headRefName": "ln/fe-1400-close-silent-gaps", - "mergedAt": null, - "number": 9, - "state": "OPEN", - "title": "FE-1400: Close the review-found gaps where the gates, dev app, and baseline runner still fail silently", - "updatedAt": "2026-08-19T14:00:12Z", - "url": "https://github.com/hashintel/brunch-lite/pull/9" - }, - { - "author": { - "id": "MDQ6VXNlcjEyNDI4NjQ=", - "is_bot": false, - "login": "lunelson", - "name": "Lu Nelson" - }, - "baseRefName": "ln/fe-1399-fail-loudly", - "body": "The generic representation still needed evidence that it held across more than one domain shape. This branch tests it against worked payload designs and records which parts of the definition remain sound.\n\n
🏗️ Agent notes\n\nDischarges the ratification condition on the generic IR definition (`ir-design.md`, Layer A): speculative payload type systems drafted for Gherkin (thin) and BPMN/process-mining (mid, the kernel spec's named third target), checked property-by-property alongside CPS (Layer B) and the assurance plugin from spec canon.\n\n**New:** `docs/planning/process-model-elicitation/ir-worked-examples.md` — the two drafted payload designs, per-property verdict table, sublimation findings, and the handoff list for plugin-spec authoring.\n\n**Amended:** `ir-design.md` —\n- All five MUST properties survive; property 2 generalized from statement granularity to **evidence granularity** (log-derived `external-lookup` captures have no user statement), property 3 restated operatively (its bite is proportional to domain–format distance; the enforceable content is IR-legitimacy of unconsumed kinds + the honest loss report).\n- Symbolic name references promoted MAY → SHOULD (all four designs use them); the objective-kind pattern generalized to completion-anchor kinds; motif annotations demoted to a named escape hatch (zero uptake); **source-regime** promoted from Layer B to a Layer-A MAY pattern for process-shaped domains.\n- Status: conditionally ratified → **ratified on worked examples**, still provisional until the September harness run.\n\nStacked on `ln/fe-1399-fail-loudly` because `ir-design.md` is not on main yet.\n\nCloses FE-1397.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\n
\n", - "closedAt": null, - "createdAt": "2026-08-13T12:50:28Z", - "headRefName": "ln/fe-1397-worked-examples", - "mergedAt": null, - "number": 8, - "state": "OPEN", - "title": "FE-1397: Validate the generic IR definition against worked payload designs (Gherkin, CPS, +1)", - "updatedAt": "2026-08-19T14:00:10Z", - "url": "https://github.com/hashintel/brunch-lite/pull/8" - }, - { - "author": { - "id": "MDQ6VXNlcjEyNDI4NjQ=", - "is_bot": false, - "login": "lunelson", - "name": "Lu Nelson" - }, - "baseRefName": "ln/fe-1361-baseline-control", - "body": "Review found checks that could pass while failing to protect the behavior they claimed to cover. This branch makes those checks and the development app report those failures instead of quietly accepting them.\n\n
🏗️ Agent notes\n\nFE-1399: Make the CI gates and dev app fail loudly where review found they fail silently\n\nThe five verified findings from the FE-1361 review sweep:\n\n- Agent-module detection is statement-anchored (shared in test/workspace.ts,\n unit-tested), so a comment mentioning 'use agent' no longer turns a file\n into an agent module — while a misplaced or comment-trailed directive is\n still detected and failed loudly.\n- workspacePackages() derives its groups from the root manifest's workspaces\n globs; an unknown group, unreadable glob shape, or missing directory now\n throws instead of passing every boundary invariant vacuously.\n- toolName() takes the Operation union instead of string, so a misspelled\n operation is a compile error; a @ts-expect-error test pins the narrowing.\n- The dev app's mount path derives from GherkinElicitor.agentName, and a new\n boundary test forbids a pinned identity being duplicated as a string\n literal outside its agent module.\n- The production /assets/:file route serves everything the client build can\n emit — bytes, not UTF-8; case-folded extensions; content-type map failing\n open to octet-stream — with the handler extracted to assets.ts and driven\n as a real Hono route over binary fixtures.\n\nReview fixes folded in: trailing-comment directives still detected,\nidentity scan matches quoted literals only (no comment cry-wolf),\nuppercase extensions serve.\n\nCo-Authored-By: Claude Fable 5 \n\nsymlink CLAUDE.md from AGENTS.md\n\n
\n", - "closedAt": null, - "createdAt": "2026-08-13T12:50:26Z", - "headRefName": "ln/fe-1399-fail-loudly", - "mergedAt": null, - "number": 7, - "state": "OPEN", - "title": "FE-1399: Make the CI gates and dev app fail loudly where review found they fail silently", - "updatedAt": "2026-08-19T14:00:08Z", - "url": "https://github.com/hashintel/brunch-lite/pull/7" - }, - { - "author": { - "id": "MDQ6VXNlcjEyNDI4NjQ=", - "is_bot": false, - "login": "lunelson", - "name": "Lu Nelson" - }, - "baseRefName": "ln/fe-1364-intermediate-representation", - "body": "This branch records what a lightly guided model can already do in the reference case. The findings set a factual baseline for deciding which product machinery is actually needed.\n\n
🏗️ Agent notes\n\nResolves [FE-1361](https://linear.app/hash/issue/FE-1361): the baseline-control experiment — what does one-shot / lightly-prompted AI elicitation already achieve?\n\nTwo conditions ran against the same simulated master scheduler (Production Scheduling testbed case): bare `claude-opus-5`, and the same model armed with the seven-category v0 prompt. Everything lives under `docs/planning/process-model-elicitation/baseline/` — protocol, situation pack, v0 prompt, runner, both full transcripts, and the scored read-out (`readout.md`).\n\nHeadline findings (details and scores in the read-out, gist on the ticket):\n\n- The bare baseline interviews far better than the positioning assumed — objectives-first, unwritten-rules probing, refusal to invent values, and an unprompted assumptions register. The differentiation story must rest on machinery, and now has evidence to rest on.\n- Neither condition can end the engagement: one novel deliverable-deferral failure, one sophisticated budget-exhaustion. Completion has to be an adjudicated contract.\n- The v0 prompt buys interaction shape, quantile elicitation, live category accounting, conflict-point depth, and penalty-weight co-construction; its residual gaps are the evidence-derived plugin requirements.\n\nAlso on this branch, from the review pass: the typecheck gate now covers `docs/planning/**` scripts (which surfaced and fixed a real narrowing error in the runner), and five off-by-one relative links left by the docs migration are repaired. The review's remaining repo-wide findings are filed as [FE-1399](https://linear.app/hash/issue/FE-1399).\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\n
\n", - "closedAt": null, - "createdAt": "2026-08-13T11:43:51Z", - "headRefName": "ln/fe-1361-baseline-control", - "mergedAt": null, - "number": 6, - "state": "OPEN", - "title": "FE-1361: Baseline control — what does one-shot AI elicitation already achieve?", - "updatedAt": "2026-08-19T14:00:06Z", - "url": "https://github.com/hashintel/brunch-lite/pull/6" - }, - { - "author": { - "id": "MDQ6VXNlcjEyNDI4NjQ=", - "is_bot": false, - "login": "lunelson", - "name": "Lu Nelson" - }, - "baseRefName": "ln/fe-1388-scaffold-workspace", - "body": "This branch defines the shared representation used to turn elicited evidence into a model. The definition gives future plugins and projections a common basis without introducing another source of stored truth.\n\n
🏗️ Agent notes\n\nResolution of the wayfinder ticket: the IR is the set of active captures\nread through the plugin's declared payload type system — no second store;\nthe net is one projection. Layer A (architecture-level definition) is\nconditionally ratified pending worked-examples validation (FE-1397);\nLayer B is the CPS plugin's ten-kind payload design for the truck-fleet\nSeptember case. Adds the IR glossary entry to CONTEXT.md.\n\nCo-Authored-By: Claude Fable 5 \n\n
\n", - "closedAt": null, - "createdAt": "2026-08-13T09:15:35Z", - "headRefName": "ln/fe-1364-intermediate-representation", - "mergedAt": null, - "number": 5, - "state": "OPEN", - "title": "FE-1364: Define the intermediate representation for process-model elicitation", - "updatedAt": "2026-08-19T14:00:05Z", - "url": "https://github.com/hashintel/brunch-lite/pull/5" - }, - { - "author": { - "id": "MDQ6VXNlcjEyNDI4NjQ=", - "is_bot": false, - "login": "lunelson", - "name": "Lu Nelson" - }, - "baseRefName": "ln/fe-1363-reference-use-case", - "body": "The repository needed a runnable workspace before feature work could be trusted. This branch establishes the initial package layout and checks that catch boundary and build failures early.\n\n
🏗️ Agent notes\n\nFE-1388: Scaffold the Bun workspace and prove the CI smoke\n\nThe repo held planning documents and no code. This lands the package\ntopology of spec §12.2 with its dependency direction enforced from the\nfirst commit rather than retrofitted later, so every later slice arrives\ninto a structure that already refuses the wrong import.\n\nPackages: core (plugin SDK as its export surface, with a core/testing\nsubpath), binding-flue, plugin-gherkin, and a dev app owning the agent\nmodule, the mount, and the conversation store.\n\nThe direction is enforced twice over. Bun's isolated linker gives each\npackage only what it declares, so plugin-gherkin cannot resolve Flue or\nthe binding even if someone writes the import; test/boundaries.test.ts\nasserts that property still holds, alongside the declared-dependency and\nsource-import checks, so a hoisted node_modules could not quietly restore\nevery forbidden path.\n\nTwo recorded Flue constraints needed opposite treatment. A computed\nagentName already fails the build loudly. A 'use agent' directive that is\nnot the file's first statement does NOT: the build stays green and the\nmodule simply stops being an agent, which nothing would notice until a\nconversation failed to start. That one is the load-bearing check in the\nboundary suite.\n\nNothing here ratifies the SDK export surface. Spec §13's two-targets rule\nkeeps the plugin contract unfrozen until the hard target has stressed it,\nso the plugin descriptor carries identity only and the ask tool fixes its\nname and schema seam while its behaviour waits for FE-1389.\n\nThe product name stays provisional in one constant, and the tool prefix\nderives from it — the name-fog costs one edit rather than a rename.\n\nCo-Authored-By: Claude Opus 5 (1M context) \n\nFE-1388: Close the review findings — make the gates able to fail\n\nReview found that three of the guards this ticket added could not\nactually go red, which is the same failure mode as a green build that\ncompiled nothing.\n\n- The CI lint step was decorative. oxlint reports its default rules at\n warning severity and exits 0 for them, so `oxlint .` passed whatever\n landed; it now runs with --deny-warnings, and a test asserts that.\n- The boundary suite was never typechecked: the root tsconfig included\n every package's src and test but not the repo-root test/ directory, so\n a type error in the file that enforces the architecture shipped green.\n- sourceFiles() scanned src/ only and returned [] for anything else, so a\n package laid out differently would pass every file-level invariant\n vacuously. The scan now covers the whole package directory, and a test\n asserts each package was actually scanned.\n\nAlso honest now: the dev app's / route no longer serves a page whose only\nscript 404s after a build. @flue/vite builds the server environment\nalone, so there is no client bundle to serve; the built server says so\nand vite build earns its CI place by compiling the agent module.\n\nSmaller: REPO_ROOT resolves through fileURLToPath rather than a\npercent-encoded URL.pathname; a workspace directory missing a manifest\nreports that rather than crashing the suite with ENOENT; bunfig.toml pins\nthe isolated linker that makes the dependency direction physical; and the\nfour per-package tsconfigs were referenced by nothing, so they are gone\nrather than left to drift.\n\nCo-Authored-By: Claude Opus 5 (1M context) \n\n
\n", - "closedAt": null, - "createdAt": "2026-08-13T09:15:33Z", - "headRefName": "ln/fe-1388-scaffold-workspace", - "mergedAt": null, - "number": 4, - "state": "OPEN", - "title": "FE-1388: Scaffold the Bun workspace and prove the CI smoke", - "updatedAt": "2026-08-19T14:00:04Z", - "url": "https://github.com/hashintel/brunch-lite/pull/4" - }, - { - "author": { - "id": "MDQ6VXNlcjEyNDI4NjQ=", - "is_bot": false, - "login": "lunelson", - "name": "Lu Nelson" - }, - "baseRefName": "ln/fe-1362-demo-vehicle", - "body": "This branch defines the reference case used to judge the demonstration work. It makes the selection criteria clear enough for later planning and review.\n\n
🏗️ Agent notes\n\nGlossary: revision story, situation pack, answer key — including the\npack/IR information-wall invariant.\n\nCo-Authored-By: Claude Fable 5 \n\n
\n", - "closedAt": null, - "createdAt": "2026-08-12T17:09:26Z", - "headRefName": "ln/fe-1363-reference-use-case", - "mergedAt": null, - "number": 3, - "state": "OPEN", - "title": "FE-1363: Choose the reference use case; settle the SDCPN-showcase criterion", - "updatedAt": "2026-08-19T14:00:02Z", - "url": "https://github.com/hashintel/brunch-lite/pull/3" - }, - { - "author": { - "id": "MDQ6VXNlcjEyNDI4NjQ=", - "is_bot": false, - "login": "lunelson", - "name": "Lu Nelson" - }, - "baseRefName": "main", - "body": "This branch records the decision that guided the September demonstration and organizes the supporting planning material. It gives later work one documented starting point instead of leaving the demo shape implicit.\n\n
🏗️ Agent notes\n\nRecommendation doc (demo shell + artifact boundary) with evidence for the\n18 Aug integration discussion; planning tree migrated .scratch/ -> docs/planning/\nwith INDEX; agent docs gained issue-writing (contract/execution-record, voice\nand authority) and git-workflow (Graphite stacks, branch per Linear issue);\ndocumentation protocol gained the untracked drafts/ convention; CONTEXT.md\ngained Demo shell and Artifact boundary; inbox ingest of SDCPN/SAILS/voice/\ntranscript arrivals.\n\nCo-Authored-By: Claude Fable 5 \n\n
\n", - "closedAt": null, - "createdAt": "2026-08-12T17:09:24Z", - "headRefName": "ln/fe-1362-demo-vehicle", - "mergedAt": null, - "number": 2, - "state": "OPEN", - "title": "FE-1362: Decide the September demo vehicle", - "updatedAt": "2026-08-19T14:00:01Z", - "url": "https://github.com/hashintel/brunch-lite/pull/2" - }, - { - "author": { - "id": "MDQ6VXNlcjEyNDI4NjQ=", - "is_bot": false, - "login": "lunelson", - "name": "Lu Nelson" - }, - "baseRefName": "main", - "body": "Follow-through on the assembled elicitation-kernel spec (FE-1374; branch predates the one-branch-per-issue convention, so the range holds the spec's companion artifacts and its second review round):\n\n- **STE product description** (`product-description.md`) — what the product does in ASD-STE100 Simplified Technical English, twelve sections, with a technical-name table mapping product words back to spec vocabulary.\n- **Plain-prose product description** (`product-description-plain.md`) — the same ground in Google/GOV.UK plain-language style.\n- **Spec review round 2** folded into `spec.md` (+ glossary touch-ups in `CONTEXT.md`): session-log archive (archive-on-read, §9.6), `declined`/`deferred` absence states, pointer navigation for evidence spans, and the harness-shipped generic strategy quiver (§11.5).\n\nPaths are `.scratch/elicitation-kernel/…` as of this range; the stack above migrates `.scratch/` → `docs/planning/`.\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n", - "closedAt": "2026-08-19T08:52:18Z", - "createdAt": "2026-08-10T15:52:03Z", - "headRefName": "docs/product-description", - "mergedAt": "2026-08-19T08:52:18Z", - "number": 1, - "state": "MERGED", - "title": "FE-1374: Assemble the spec", - "updatedAt": "2026-08-19T13:59:58Z", - "url": "https://github.com/hashintel/brunch-lite/pull/1" - } -] diff --git a/libs/@hashintel/brunch-agent/docs/archive/migrations/issue-pr-legibility-2026-08-20/data/linear-canonical-target-hashes.json b/libs/@hashintel/brunch-agent/docs/archive/migrations/issue-pr-legibility-2026-08-20/data/linear-canonical-target-hashes.json deleted file mode 100644 index 0d8f3bed57b..00000000000 --- a/libs/@hashintel/brunch-agent/docs/archive/migrations/issue-pr-legibility-2026-08-20/data/linear-canonical-target-hashes.json +++ /dev/null @@ -1,281 +0,0 @@ -{ - "format": "linear-canonical-fold-v1", - "issues": [ - { - "identifier": "FE-1357", - "bodySha256": "af21a19b276419b7f351b3edb6c48bf3f598dd25934f835e971cc7a908d60f98" - }, - { - "identifier": "FE-1358", - "bodySha256": "08c91e46efd7697eb722cc04373be6680de6591dc6ad4ed9c35f971f6ca55899" - }, - { - "identifier": "FE-1359", - "bodySha256": "5229603b3d41374e36d31d761c83528f7ba5291963ac84c843d60a138d56652b" - }, - { - "identifier": "FE-1360", - "bodySha256": "a6d7709d6fad560785f2c4d8d5d4d93731ff5d6c8e2b16dcf2bc384ac5daae19" - }, - { - "identifier": "FE-1361", - "bodySha256": "5bd4de3670bdb15872c5826c6d053fcf0e4bbc17542313a21640b6732ae941e8" - }, - { - "identifier": "FE-1362", - "bodySha256": "9576d5d00ff1e5cc3e8d8ee8d61c762e297bb63889aaea2469a7a778418d9993" - }, - { - "identifier": "FE-1363", - "bodySha256": "4cc749b9cfdeeec3960bba78110fe36874827ca888085991a97f232009e89d97" - }, - { - "identifier": "FE-1364", - "bodySha256": "454e2287159c78a31d09cbdea9402be4594916e53106026ec42944445d768a24" - }, - { - "identifier": "FE-1366", - "bodySha256": "d07ec0b6e6bfdb01e9dbcc57de35dbfeeae65c80d5c688a0fb4b0f855c05739d" - }, - { - "identifier": "FE-1367", - "bodySha256": "0d564e0427b73c79440b31b09c85a11d83bdada98759c0c0571531e1b7eacdc2" - }, - { - "identifier": "FE-1368", - "bodySha256": "58eac8c7f4da2110cff73432764c20d71576fbedbed167d88a7c0ccaacfe8b70" - }, - { - "identifier": "FE-1369", - "bodySha256": "f9acc9c49a910e07ea12eee26f5156578029354e5b695d3b6f679744a4d30d3d" - }, - { - "identifier": "FE-1370", - "bodySha256": "586900fec359fdea19b40355ce70ada6f4dc6cc1a4b318a5266fd04925e9330b" - }, - { - "identifier": "FE-1371", - "bodySha256": "8e34e41697c46c46a81ea4c5ab48edcb49de55973b2b11732bcf451b8191a17f" - }, - { - "identifier": "FE-1372", - "bodySha256": "4ceed9ff828af36baeb0d9fe3be38f70f48b89310c8f6eb86afb98ba9ea34d1e" - }, - { - "identifier": "FE-1373", - "bodySha256": "3427cd321d38c72b7abb00985cdc8eb1d4de70b2b1cf4be1bad6537edf468d03" - }, - { - "identifier": "FE-1374", - "bodySha256": "cb0436794a3e2c782938939475a1fa2a0b68e3969b1e1ff03eb8fa4306d4abc0" - }, - { - "identifier": "FE-1375", - "bodySha256": "f1e803bca9e75caf52ea4346c7d8fe2c859523a1de616367c56b1dc58da2d82c" - }, - { - "identifier": "FE-1376", - "bodySha256": "3a752cf0d3362718f9364083a2469b501e3d5037dbfd8fde747216b4e53e36ec" - }, - { - "identifier": "FE-1377", - "bodySha256": "fac78c76da54522de3bfa0777b4f048279581c51300c3fc74bcf80548c0d8cba" - }, - { - "identifier": "FE-1378", - "bodySha256": "77648b3f761ba84c2837ad23b418104e13c2182772006d3e175f63b0066cf065" - }, - { - "identifier": "FE-1379", - "bodySha256": "3b61f9e6a64f171a18eb0482da780e41dab67f44bfd7c7568acca05f5b39c85c" - }, - { - "identifier": "FE-1382", - "bodySha256": "dcbee28f52b04684d16de429518666c033f3d5dc2ace992d55b5fe49f5180e7f" - }, - { - "identifier": "FE-1383", - "bodySha256": "3e7b2d47ea437880f995a53d223fb9380873e02ae5adac83ceafd9d38a6f55cb" - }, - { - "identifier": "FE-1384", - "bodySha256": "bf5b66bf1a18287a64f583e0f7df1761c1d3e13278ff349998410d5aa425156c" - }, - { - "identifier": "FE-1385", - "bodySha256": "9a9849fe884a4047d00b0d673f085c3ca401643df2647eb3a9da07e1530991c6" - }, - { - "identifier": "FE-1386", - "bodySha256": "6714044cb3c8c5e5f5a511ec8339ec15f8d639b78814cd2f430d068265c4443f" - }, - { - "identifier": "FE-1387", - "bodySha256": "e02aff72a2640dbe03ff1027ec7ec203c67be06796834148e5ff7b577cbfd66a" - }, - { - "identifier": "FE-1388", - "bodySha256": "09f30e8f38b786bb7d9645dec9b45137931774e2dbe8d033acabf410fac84a7b" - }, - { - "identifier": "FE-1389", - "bodySha256": "61d11a095b9ca88dfcd274a2d50e1d1e337713abfce81dc75cf29cbd1dad3c80" - }, - { - "identifier": "FE-1390", - "bodySha256": "6cf8d9572068580ce883faeed54d1ef0ac20d8c60666d32d810c1ff45b56a0ad" - }, - { - "identifier": "FE-1391", - "bodySha256": "a10cd59d9b58e0903965106cfc42cace3aa01a8366c414a76196c867c46c5683" - }, - { - "identifier": "FE-1392", - "bodySha256": "71902c740537f89d9251bcdab39d0569d59e2445e9198305e578100b9eafe77d" - }, - { - "identifier": "FE-1393", - "bodySha256": "9cc7f0cedf11c039e9e6c55f9904fd35340c2cb329d275cdefa77e022695fffe" - }, - { - "identifier": "FE-1394", - "bodySha256": "3a738a9370e3a1698e0c0d0fd6c966d15e7635f9be498470ac8c8ef37e8297b1" - }, - { - "identifier": "FE-1395", - "bodySha256": "325fb0866d84fc3386d7dadd18e1fc5140e0f02bc64508826dada5a514c79318" - }, - { - "identifier": "FE-1396", - "bodySha256": "60a1aa6eb6aacbb63940637435b2ecd8dcbc8d38ab4e8057976301ba89665b59" - }, - { - "identifier": "FE-1397", - "bodySha256": "728d19cef7d300fa63be05be9ba870290863be42072f08e865cee254f2ec9cbf" - }, - { - "identifier": "FE-1399", - "bodySha256": "93f520f9c2e08c999d5105fdd07a770dbe8a20b6f8bea6c1995067bcd44cf221" - }, - { - "identifier": "FE-1400", - "bodySha256": "096ccde21fd800d9c0cbb751b580ad160b998666313b43702030a78056471204" - }, - { - "identifier": "FE-1401", - "bodySha256": "9e51a20089190e2e34e70e0a4b6faa35b73c8818a11eb8dce1fcd5402c652a20" - }, - { - "identifier": "FE-1402", - "bodySha256": "b2abd827fdba53895fdccc6d227d8981e7615a103564a8b5955108faf44a49fa" - }, - { - "identifier": "FE-1403", - "bodySha256": "d4f1804674241c2b523f122d3a510d4730eabaa564dc2a589e63886e48b94240" - }, - { - "identifier": "FE-1404", - "bodySha256": "0f61b1baed5eb374bd7366fc51344fd21a65f900ce7a4fc273241ef919819b9d" - }, - { - "identifier": "FE-1405", - "bodySha256": "cf4bbd9f6c5f81cac7a38ae614a1a87011efdf7ca19020c95e7e14f177fc6517" - }, - { - "identifier": "FE-1406", - "bodySha256": "25af8c35ff021b508612a58d96316c4ec10415e9abed4f2e32e0ccb1fe4100c4" - }, - { - "identifier": "FE-1407", - "bodySha256": "58bff2297ba46cb4a223217bb178784550174cb0d72b951d0a4b631e97bffe9f" - }, - { - "identifier": "FE-1419", - "bodySha256": "cb43c18d19933fb4d41b43b6ba6ebf2a0bbb2ee0bf57a1837ea7cb6045bccc8e" - }, - { - "identifier": "FE-1420", - "bodySha256": "316d82c70192905999f13c7cb3a972faeca36f024e8e87de11ddd7ed3822536f" - }, - { - "identifier": "FE-1422", - "bodySha256": "e12e25753221ac1b0052d013d53da4cdb98ed42d2b4d2bfbbc0cc3c7725c4029" - }, - { - "identifier": "FE-1423", - "bodySha256": "7007aa7a3cf88c13c6d8712425b5ef60a16e648f0339551fcafc3cc79ce6cd68" - }, - { - "identifier": "FE-1424", - "bodySha256": "de8465621d99b972d89f6685ad71a166b68dec925cea24bf093a1b22938feec3" - }, - { - "identifier": "FE-1431", - "bodySha256": "99871599b344a53e1a214bc3c1079e074d02cca22299fd8af4a6b19c0cb4cfef" - }, - { - "identifier": "FE-1432", - "bodySha256": "f482b84dd957b5b765c4890f0414c8026a1d973c2dd1fbd0736cf905394057fb" - }, - { - "identifier": "FE-1433", - "bodySha256": "7ae7b7bace6b2c93833b7c71c5dbf7625d409543b73f451cbeaf52d995e727f0", - "outerReplacement": { - "from": "demo.petrinaut.org", - "to": "[demo.petrinaut.org]()" - } - }, - { - "identifier": "FE-1434", - "bodySha256": "f997a348580b20b35f3c843b022988b459306c159d91a012fce1170a2ae80a6f" - }, - { - "identifier": "FE-1435", - "bodySha256": "14076be75535ff5dbf12d8f68bb41a8292fed3a98bb41f1eb65eea02de3cf524" - }, - { - "identifier": "FE-1436", - "bodySha256": "8f8e69c34fc30483f729716e87f695f62bfcf63cb383250b0635a809ef478103" - }, - { - "identifier": "FE-1437", - "bodySha256": "ca9ca144d3aa18223c7141542955d1c39b805323030bce4a25b948a854de5bbf" - }, - { - "identifier": "FE-1438", - "bodySha256": "47857e1be8bc182f3954b31aac4e7cd10265e00458549bf0bcc47484cf2d6410" - }, - { - "identifier": "FE-1439", - "bodySha256": "98c4a00fd0ed5d31948361198b25bdc575fd637e9f2553049511f9b5c7ddcc9a" - }, - { - "identifier": "FE-1440", - "bodySha256": "1770a1db71c34b2e7f1de875342f58b3212cdcd7d8c386653675f87b80f34622", - "outerReplacement": { - "from": "demo.petrinaut.org", - "to": "[demo.petrinaut.org]()" - } - }, - { - "identifier": "FE-1441", - "bodySha256": "56da85ce45688f08c3c9bcc75a1feb6c66c33c364d7d956ab864b4dc6ac7e09c" - }, - { - "identifier": "FE-1442", - "bodySha256": "e9c8f5050856192989f953358829fce6ace7b563471ee3e2661d9f2fd80b050e" - }, - { - "identifier": "FE-1448", - "bodySha256": "95b45fea18fcca04ab562f60798aed01189c4b3010184d8eb011bd026c701d37" - }, - { - "identifier": "FE-1449", - "bodySha256": "1981c5120550d60e77ee6d16355b06cf7eeaacaeee9e7e16432422dfcb4606e3" - }, - { - "identifier": "FE-1451", - "bodySha256": "91b3b7773b3f664b22ace816a7d85d631020f9bf80348966cc6294c461dc8bcb" - } - ] -} diff --git a/libs/@hashintel/brunch-agent/docs/archive/migrations/issue-pr-legibility-2026-08-20/data/linear-proposals-FE-1366.json b/libs/@hashintel/brunch-agent/docs/archive/migrations/issue-pr-legibility-2026-08-20/data/linear-proposals-FE-1366.json deleted file mode 100644 index dd98f2121f1..00000000000 --- a/libs/@hashintel/brunch-agent/docs/archive/migrations/issue-pr-legibility-2026-08-20/data/linear-proposals-FE-1366.json +++ /dev/null @@ -1,398 +0,0 @@ -{ - "root": { - "id": "a5053b51-937d-453f-9c4e-fe8e49e912ad", - "identifier": "FE-1366" - }, - "generatedAt": "2026-08-20T09:03:27.215678Z", - "issueCount": 14, - "issues": [ - { - "id": "a5053b51-937d-453f-9c4e-fe8e49e912ad", - "identifier": "FE-1366", - "url": "https://linear.app/hash/issue/FE-1366/spec-the-elicitation-harness-architecture-archived-wayfinder-map", - "state": { - "name": "Done", - "type": "completed" - }, - "parent": null, - "sourceUpdatedAt": "2026-08-19T16:26:24.440Z", - "sourceTitle": "Spec the elicitation harness architecture (archived wayfinder map)", - "sourceDescriptionSha256": "991af4e8570535aa208f829fd81107ebcf5163186d25837f78746cee7ed77015", - "sourceTitleSha256": "3cd9b3e42040c5bd4f19c7ccb505e838266683b4098bb7d02934b03c8df8f6b5", - "oldOuter": "This is the completed planning map for the **elicitation harness**: the architecture that generalizes brunch's AI interviewer into a reusable library, able to interview people about different kinds of subject matter through pluggable target definitions — so the same interviewing machinery can produce, say, software specs in one setting and process models in another. Planning finished on 2026-08-10; the outcome is a reviewable specification, in the brunch-lite repo at `docs/planning/elicitation-kernel/spec.md`, alongside a plain-language product description. The build, and the September demo that motivates it, are planned on the successor map FE-1357.\n\nEverything below the divider is a verbatim mirror of the map as it was worked in repo markdown, kept for team visibility.", - "proposedTitle": "Document the elicitation harness architecture", - "proposedOuter": "This completed planning map defines an elicitation harness that turns the existing AI interviewer into a reusable library for different subject areas. Planning finished on 2026-08-10 and produced `docs/planning/elicitation-kernel/spec.md` plus a plain-language product description. FE-1357 plans the build and the September demo that depends on it.", - "extractionMethod": "first-standalone-divider", - "innerRecord": "\n> **Archive migration (2026-08-11).** This wayfinder map was worked and completed in local markdown (repo `brunch-lite`, `docs/planning/elicitation-kernel/`) before this repo's tracker moved to Linear; it is mirrored here retroactively for team visibility. **Destination reached 2026-08-10** — the assembled spec lives at `docs/planning/elicitation-kernel/spec.md`. Repo-relative links below refer to files in `brunch-lite`. Successor effort: FE-1357 (process-model elicitation).\n\n---\n\n# Map: Elicitation Kernel — carve-out spec\n\nLabel: wayfinder:map\nStatus: closed — destination reached 2026-08-10 (the spec is assembled: [spec.md]())\nCreated: 2026-08-06\n\n## Destination\n\nA reviewable **spec** for a standalone architecture (working name **elicitation kernel**) that generalizes brunch's elicitor into agentic interviewing against pluggable elicitation targets, on the Pi-family substrate, deployable local and remote (Flue-shaped). The spec fixes: the shipping shape (kernel library vs. Flue agent); the kernel / host / plugin contract decomposition (leading hypothesis: the four-contract + pack model from the agentic-elicitation-challenges doc, with persistence plugin-owned in accord with deploy target); the questioning-UX contract (successor to brunch's structured exchanges, critiqued not copied); and a first milestone against two live dev targets (elicit-gherkin + elicit-lean/formal, BPMN named third).\n\n## Notes\n\n* Tracker: local markdown (`docs/agents/issue-tracker.md`); this map and `issues/` are canonical. Ticket `Status:` values: `open` / `claimed` / `resolved`.\n* HITL tickets consult `/grilling` + `/domain-modeling`; research tickets resolve via `/research` subagents, AFK.\n* Plan, don't do: tickets resolve decisions. The spec (ticket 08) is the map's only deliverable.\n* Lexicon (hardened via `/domain-modeling` 2026-08-06, during the Questioning-UX grilling; canonical home: repo `CONTEXT.md`): **substrate** (the agent framework beneath: Pi family, Flue — the charter's \"harness-agnostic core\" non-goal reads \"substrate-agnostic\"), **ui** (the user-interface shell: whatever affords interaction — rendering, input, reply transport; not bound to GUI/TUI; replaces \"host\"/\"host-interface\"), **harness** (the generic capability layer: mechanism + orchestration — the effort's essence is harness-engineering; replaces \"kernel\" as shell name), **plugin** (target-defining policy; composes packs, receives harness capabilities via a narrow injected context), **pack** (unchanged: ElicitationPack = concept/observation/completion contracts; ProjectionPack = projection contract; \"kernel card\" survives only as the pack-content unit), **target** (split 2026-08-10 by the multi-session resolution: **target-domain** = the artifact family a plugin defines; **target-document** = the durable unit sessions attach to; bare \"target\" legal where context disambiguates), **issue** (typed backpressure to the elicitation controller; two producers since the capture-sweep resolution — plugin ops at the payload level, the harness itself at the envelope level).\n* **Built artifacts as proofs** (ratified 2026-08-06, from the Questioning-UX grilling): prototypes, walking skeletons, and logic-prototypes are the proof path — grilling tickets resolve decisions *provisionally* and name the proof obligations they delegate to prototype tickets, rather than pinning everything through precedent + conversation alone.\n* Primary references: [docs/reference/agentic-elicitation-challenges-2026-08-06T10-02-41Z.md](<../../docs/reference/agentic-elicitation-challenges-2026-08-06T10-02-41Z.md>) (turn 1: four contracts, packs, IR) and [docs/reference/agentic-elicitation-criteria-2026-08-06T14-11-18Z.md](<../../docs/reference/agentic-elicitation-criteria-2026-08-06T14-11-18Z.md>) (turn 2: hourglass, proof obligations, invariants, smells, test matrix)\n* Reference codebase (read-only): `../brunch` — esp. `src/agents/runtime/elicitor/`, `src/exchanges/`, `src/agents/skills/`\n* External: [Flue docs](), [zil-lean]()\n\n### Charter decisions (pre-map grilling, 2026-08-06)\n\n* Destination artifact = a spec (not a decision-set alone, not a build)\n* Elicitor-first: the elicitor is this map's spine\n* Greenfield reimplementation; brunch is reference architecture, not shared code\n* Fully decoupled from brunch's September MVP\n* Pi-family substrate committed; harness-agnostic core is a named non-goal\n* Brunch's exchange vocabulary is prior art to critique, not an incumbent to preserve\n* Persistence plugin-owned (in accord with deploy target) is a hypothesis to test, not a decision — **resolved 2026-08-10, flipped** by [Multi-session elicitation & durable target state](): storage port harness-defined, binding-implemented, plugin-blind\n* Two live dev targets from day one (forces generalization on both pack axes); BPMN/process-mining named third\n* \"brunch-lite\" is a temporary label; real name is fog\n* **Principle v2** (ratified 2026-08-06, replaces \"behavioral over procedural\" after an evidence pass over `~/Clones/mattpocock/skills` and `../brunch/docs/design/BEHAVIORAL_KERNELS.md`): *procedure for mechanism, anchors for judgment, shapes for output*. Short step sequences with checkable completion criteria where order matters; leading words wherever judgment is required; annotated shapes for everything produced; reference disclosed progressively. Design against sprawl, negation-steering, no-ops, judgment-as-procedure — not against procedure itself. `writing-for-agents` is the pack-authoring standard; kernel cards (BEHAVIORAL_KERNELS.md) are the unit of elicitation guidance.\n\n## Decisions so far\n\n\n\n* [Assemble the spec]() — **the destination: [spec.md]() assembled** (fourteen sections + adjudications appendix) from the full working set in amendment order; all seven pre-pass contradictions adjudicated (storage port binding-implemented with the `db.ts` reconciliation; ops stay pure; status derived never stored, retraction specified; tap-ness as a transport fact via a reserved reply encoding; invariant 1 reconciled with the provenance rule; per-message current-affordance surface with identity on tool output parts; no instruction interpolation + harness-mechanical reply binding, no echo token); ten harness invariants restated in envelope vocabulary; `CONTEXT.md` gained the envelope vocabulary and the kernel-compound ruling (kernel card kept, \"kernel invariants\" → harness invariants); rename propagated over tickets 02/07. **The frontier is empty — this map is complete.** Remaining fog is post-spec and graduates with the build effort.\n* [Walking skeleton: sweep seam on Flue]() — **all four capabilities hold on Flue** (two native, two binding-absorbed, none forbidden), closing pre-pass L1–L3: settlement trigger = `useAgentFinish` would-stop seam with same-response signal steering (fires on `terminate:true` suspensions too — the pending guard is load-bearing; needs a re-nudge loop guard); session-entry read = binding-absorbed via the public durable-history projection (no in-process API; `purpose` discriminates provenance) with the load-bearing HITL amendment that **evidence anchoring is harness-resolved — the model cites verbatim user quotes, never entry numbers** (sharpens issue 12's span shape: excerpt primary, pointer derived); on-behalf injection = `kind:'signal'` append/dispatch, projecting structurally non-user so the citable-evidence rule is mechanically enforced (live: unscripted conversational conflict-surfacing off the re-entry briefing; a fact recovered from a *superseded* capture, honestly marked inferred); transactional store = binding-owned entirely (content-keyed idempotence is load-bearing under Flue's at-least-once re-execution). **Amends Shipping shape's capability list to ten entries** (adds would-stop seam, entry-projection read, signal injection, transactional store). New payload-stratum spec examples: absence smuggled as payload; compound payloads make supersession lossy. Prototype on branch `prototype/13-sweep-seam`.\n* [Multi-session elicitation & durable target state]() — **durable target-document, transient sessions, sweep as the only bridge**: \"target\" splits into **target-domain** (family a plugin defines) / **target-document** (durable unit sessions attach to — capture store **plus all session logs** as truth, renders strictly derived); session = one substrate conversation (per-session state: log + swept high-water mark + pending-affordance slot), never formally closes; **interleaved-only** concurrency — serialized store, whole-sweep-atomic refusals, the single-hop refusal doubling as the stale-session guard (true concurrency + per-item application → fog); re-entry briefing = injected on-behalf-of-user state messages (Pi convention), advisory-only, with the sharpened provenance rule that **only true user entries are citable evidence**; completion = derived status, never a write gate; **persistence hypothesis flipped** — storage port harness-defined, binding-implemented, plugin-blind (plugin-addressable only via PluginContext-passed methods); evidence spans = pointer + quoted excerpt.\n* [Shipping shape: kernel library vs. Flue agent]() — **harness library in a thin host-authored agent, ratified with the second-binding test**: the spec enumerates a six-item substrate-capability list as the core/binding seam (portability = pressure test, not build target; **binding** entered the glossary); topology = Bun-workspace monorepo in this repo — `packages/core` (plugin SDK is its export surface; `core/testing` subpath), `packages/flue` (binding), `packages/plugin-gherkin` + `packages/plugin-assurance` (renamed from `plugin-proof-obligations` 2026-08-10, per the Formal-verification canon survey's category-error verdict; plugins depend on core *only* — stated spec invariant), `apps/dev`; Valibot throughout; tool prefix from product name (provisionally `bl_*`, never `elicit_*` — strings name identity, not function); workspace-internal, no publishing; testing = **generation-first fixtures over a deterministic replay driver** (kernel invariants as properties, `arbitraryFromSchema`, `fc.commands` conversation fuzzing from the envelope vocabulary, model as offline generator never CI oracle, regenerate-on-declaration-change); dev app chartered with three roles (dev loop / target-gallery demo / diagnostic probe views); one agent per target; milestone one local-only with remote-parity constraints pinned in spec; CI smoke = build + simulation suite.\n* [Walking skeleton: Flue question round-trip]() — **transport proven, one amendment**: the one-channel multiplex works end-to-end (three forms, markdown floor, string-only replies, absence strip, redirect), but the fixed data channel is a *one-live-affordance slot per message* (update-in-place at every layer — stream, history, reply read-back), so per-ask identity/payload must ride the ask tool's **output part** and the kernel must **reject a second ask per batch** as mechanism; echo token not needed (adjacency + persistent pending state bound every bare-string reply); turn suspension (`terminate: true` + persistent state + fresh dispatch) sufficient, with a wake wart — instruction-interpolated pending state triggers an extra \"instructions updated\" model turn per ask; cancelled/redirected questions read back cleanly via `record_interpretation` evidence. Flue facts: `@flue/vite` needs vite ^8; dev server gives `app.ts` the whole request space (ui is app-served). Prototype on branch `prototype/10-flue-roundtrip`.\n* [Logic-prototype: capture sweep & settlement]() — **all five capture hypotheses hold** in a working prototype (branch `prototype/11-capture-sweep`; double-clickable demo + headless driver), each sharpened: settlement splits into **trigger vs judgment** (lifecycle-event wiring *not* proven in ticket 10 — misattribution caught by the 2026-08-10 consistency pre-pass; delegated to the sweep-seam walking skeleton, ticket 13); idempotence splits into **mechanical (harness, evidence-anchored capture identity) vs semantic (plugin reconcile)** and is content-based, so re-sweeps repair omissions; absences are **evidence, not agenda** (re-ask runs through plugin validate → issues); single-hop supersession over active heads = the lost-update guard; **two supersession channels** (sweep-time link / issue-time resolution record) with read-time *derived* status suggested (cf. issue 09). Amendments: **two validation strata** (envelope-level harness-owned refusals + advisories / payload-level plugin ops — strengthens smallest-honest-plugin), op cadence = pure orchestration policy (snapshot-purity), **resume-time sweep reconciliation** (unswept tail as computed re-entry fact). Graduated the multi-session fog → ticket 12.\n* [Questioning-UX contract]() — the reframe: **no exchange-pair ontology** — conversation is primary, structured questions are **affordance emissions** committed to session as evidence, capture via idempotent **range-sweeps** on agent-judged, range-level **settlement**; shells renamed **substrate / ui / harness / plugin** (the effort's essence is *harness-engineering*; IoC via narrow injected PluginContext — the ask API is part of it); harness fixes: three baseline shapes + questionnaire chaining, absence interpret-by-default/afford-when-structured, **one multiplexed data channel** with markdown floor (Flue: outbound rich / inbound string-only), interpretation render harness-owned with plugin-optional typed renderer (JSON default), conflicts close only via explicit **resolution records**, no question-budget machinery (economical interviewing = judgment guidance); all shape-level commitments are working hypotheses with proof obligations delegated to the two prototype tickets created here (10, 11); **built-artifacts-as-proofs** ratified into Notes.\n* [Contract decomposition]() — the load-bearing resolution: **agent-forward hybrid** (agent judgment owns the loop; deterministic mechanism as tools; typed-issue backpressure); **no universal IR** — the kernel owns only a domain-free **capture envelope** (evidence spans, epistemic-status enum, absence states, alternatives, one `supersedes` link) around opaque plugin payloads, no kernel edges or graph (conflicts/equivalence are typed issues, resolved only explicitly); operations tiered `project`+`validate` required / `reconcile` optional / `observe` agent-native, all snapshot-in/deltas-out with typed loss reports; **facts computed, weights judged** dialogue policy (no scoring engine); packs = kernel cards + shapes + validators + shallow schemas + completion contract; smallest-honest-plugin test; five proof obligations + ten kernel invariants + gating tests (reprojection, minimal pairs, black-box authoring) adopted as spec acceptance material; multi-plugin composition, removal, replay, capability negotiation, versioning deferred to fog. Full ownership table in the ticket.\n* [Flue architecture deep-read]() — embedding is Flue's grain: ship the kernel as a custom hook (`useElicitation(plugin)` — final name per Shipping shape) + exportable `defineTool`s over a thin host-authored `'use agent'` module; Pi is exposed only at the provider layer (no path to Pi's loop/extensions — brunch-style Pi assumptions don't transfer); durability contract identical local/remote but storage/ownership differ; **no first-class ask-the-user primitive — the kernel must own its turn-suspension protocol** (`terminate: true` tool + persistent state + data part, answer arrives as fresh dispatch); constraints: Valibot at every schema boundary, stable-tool-set cache economics, subagents conversationally sterile, non-React hosts must build on `@flue/sdk`.\n* [Formal-verification canon survey]() — align the second target to a **GSN skeleton** (Goal/Strategy/Solution/Assumption/Justification; the one canon built for argued claims about a system) with **Dafny nouns** (`requires`/`ensures`/`invariant`/`lemma`/`assume` transfer; `modifies`/ghost don't) and **Lean sorry-taint semantics**; the headline artifact is a `dafny audit`-style **assumption ledger** (per-claim status becomes a derived label); acyclicity kept as a deliberate restriction with `decreases` as future escape hatch; sell the Datalog closure as taint-propagation, never as an assurance verdict; **\"proof obligations\" is a category error to verification readers — rename the target** (resolved 2026-08-10: the second target is the **assurance argument**, package `plugin-assurance`); Geolog is *not* an ARIA artifact (negative result) but coherent-logic saturation is the validator's genuinely canonical lineage; concrete milestone-one contract sketch (one `Statement` record, four edge kinds, five-stratum status rules) is in the ticket.\n* [Dev-target portfolio confirmation]() — confirmed `elicit-gherkin` + `elicit-proof-obligations` live (BPMN third, full elicit-lean deferred); **hybrid order**: both packs authored before the pack interface freezes, gherkin wires end-to-end first; proof-obligations format = zil-lean's ideas in our own claim-DAG serialization, hewing to existing canon (Dafny leading candidate — grounded by new ticket 09); gherkin milestone-one validation = parse validity + pack-declared step lexicon; surfaced the **behavioral-over-procedural** design principle (routed to Contract decomposition).\n* [Brunch exchange-schema audit]() — full catalog + three-way classification: **generic** (the `ask` one-terminal-four-shapes primitive, declared continuations that make offer→answer non-forgeable, answered/cancelled/unavailable property-presence union, self-contained terminals, boundary-teaching schemas, no-storage asking agenda + private scratchpad, hash-pinned prompt directives); **generalize-with-changes** (present-then-ask two-step → one `present(form, payload)` with pluggable validators, parent-link instead of enumerated prev/curr/next unions, opaque target-supplied receipts, rubrics declared by the target); **brunch-specific** (review-set node/edge schema, plane vocabulary, `graph_refs`, `map`/`tutorial` skills). Strain marks confirm the \"just a guess\" caution: wire vocabulary outlived tool names, a digest-into-review merge was retracted, doc/code drift on edge categories. 15 inherit/avoid lessons feed tickets 04 and 05.\n* [zil-lean survey]() — zil-lean contains **no elicitation** (agent-authored 2-day snapshot; Datalog/Zanzibar-style claim graph in Lean 4 + Clojure) but is a strong existence proof of the kernel's claim-graph layer: evidence-graded assurance lattice with prohibited promotions, full derivation provenance, assumption/lemma/theorem/guarantee vocabulary with derived PROVED/CONDITIONAL/WEAK/BROKEN statuses. **Verdict: full elicit-lean is not dev-sized; the \"elicit-proof-obligations\" slice is** (claim DAG + criticality + evidence refs, validated by acyclicity + Datalog closure — no Lean statements) and beats BPMN as second target on both pack axes; BPMN stays third.\n\n## Not yet specified\n\n* Diagnostic \"exploded view\" harness for HITL prototype sessions — an instrumented walking-skeleton UI with parallel state readout and labeled moving parts (persistent state, pending affordance slot, tool traffic, terminate boundaries, advisory wakes, activation timing) so the human can react to internals, not just the chat surface; surfaced by the user after the ticket-10 walking skeleton, wanted \"at some point\" — **host identified** by the Shipping-shape resolution: the dev app's probe view (role 3 of its charter); graduates with the dev app, post-spec\n* Remote deploy target choice & provisioning — pending an infra-colleagues conversation (2026-08-07); blocks nothing on this map (the spec pins remote-parity constraints without naming the target)\n* Unstructured-ingest crossover at scale — the IR inside the contract-decomposition ticket absorbs the parsing/mapping core; what remains foggy is bulk/brownfield ingest as a first-class mode\n* elicit-to-petri-net as a future plugin — abstract questions (what fits a net; what IR precedes a net) wait until the pack model exists\n* JSONL-vs-YAML storage-format comparison — persistence resolved harness-defined / binding-implemented (multi-session resolution, 2026-08-10), so the format is a binding-internal choice; graduates with the local binding implementation, post-spec\n* Concurrent-session coordination beyond interleaving — simultaneous sweeps against one target-document, per-item partial sweep application, merge machinery; the spec's stance (serialized store + single-hop refusal + re-entry advisory) covers interleaving, and this graduates only if a real concurrent consumer appears\n* The real product name\n* Deferred plugin-ecosystem machinery (from Contract decomposition): simultaneous multi-plugin composition, plugin removal, full replay, capability negotiation, version/migration (spec names the five version axes only)\n\n## Out of scope\n\n* **Executor-standalone** — a different kind of project; the spec only names the elicitor→executor handoff seam\n* **Harness-agnostic core** — every named consumer is Pi-family; revisit only if a non-Pi consumer materializes (would be a fresh effort under a redrawn destination)", - "innerSha256": "5dc0886da2a1b3b2373a1e3f0e172b2adb805003a15aa782200015be298fbd56", - "proposedBody": "This completed planning map defines an elicitation harness that turns the existing AI interviewer into a reusable library for different subject areas. Planning finished on 2026-08-10 and produced `docs/planning/elicitation-kernel/spec.md` plus a plain-language product description. FE-1357 plans the build and the September demo that depends on it.\n\n+++🏗️ Agent notes\n\n> **Archive migration (2026-08-11).** This wayfinder map was worked and completed in local markdown (repo `brunch-lite`, `docs/planning/elicitation-kernel/`) before this repo's tracker moved to Linear; it is mirrored here retroactively for team visibility. **Destination reached 2026-08-10** — the assembled spec lives at `docs/planning/elicitation-kernel/spec.md`. Repo-relative links below refer to files in `brunch-lite`. Successor effort: FE-1357 (process-model elicitation).\n\n---\n\n# Map: Elicitation Kernel — carve-out spec\n\nLabel: wayfinder:map\nStatus: closed — destination reached 2026-08-10 (the spec is assembled: [spec.md]())\nCreated: 2026-08-06\n\n## Destination\n\nA reviewable **spec** for a standalone architecture (working name **elicitation kernel**) that generalizes brunch's elicitor into agentic interviewing against pluggable elicitation targets, on the Pi-family substrate, deployable local and remote (Flue-shaped). The spec fixes: the shipping shape (kernel library vs. Flue agent); the kernel / host / plugin contract decomposition (leading hypothesis: the four-contract + pack model from the agentic-elicitation-challenges doc, with persistence plugin-owned in accord with deploy target); the questioning-UX contract (successor to brunch's structured exchanges, critiqued not copied); and a first milestone against two live dev targets (elicit-gherkin + elicit-lean/formal, BPMN named third).\n\n## Notes\n\n* Tracker: local markdown (`docs/agents/issue-tracker.md`); this map and `issues/` are canonical. Ticket `Status:` values: `open` / `claimed` / `resolved`.\n* HITL tickets consult `/grilling` + `/domain-modeling`; research tickets resolve via `/research` subagents, AFK.\n* Plan, don't do: tickets resolve decisions. The spec (ticket 08) is the map's only deliverable.\n* Lexicon (hardened via `/domain-modeling` 2026-08-06, during the Questioning-UX grilling; canonical home: repo `CONTEXT.md`): **substrate** (the agent framework beneath: Pi family, Flue — the charter's \"harness-agnostic core\" non-goal reads \"substrate-agnostic\"), **ui** (the user-interface shell: whatever affords interaction — rendering, input, reply transport; not bound to GUI/TUI; replaces \"host\"/\"host-interface\"), **harness** (the generic capability layer: mechanism + orchestration — the effort's essence is harness-engineering; replaces \"kernel\" as shell name), **plugin** (target-defining policy; composes packs, receives harness capabilities via a narrow injected context), **pack** (unchanged: ElicitationPack = concept/observation/completion contracts; ProjectionPack = projection contract; \"kernel card\" survives only as the pack-content unit), **target** (split 2026-08-10 by the multi-session resolution: **target-domain** = the artifact family a plugin defines; **target-document** = the durable unit sessions attach to; bare \"target\" legal where context disambiguates), **issue** (typed backpressure to the elicitation controller; two producers since the capture-sweep resolution — plugin ops at the payload level, the harness itself at the envelope level).\n* **Built artifacts as proofs** (ratified 2026-08-06, from the Questioning-UX grilling): prototypes, walking skeletons, and logic-prototypes are the proof path — grilling tickets resolve decisions *provisionally* and name the proof obligations they delegate to prototype tickets, rather than pinning everything through precedent + conversation alone.\n* Primary references: [docs/reference/agentic-elicitation-challenges-2026-08-06T10-02-41Z.md](<../../docs/reference/agentic-elicitation-challenges-2026-08-06T10-02-41Z.md>) (turn 1: four contracts, packs, IR) and [docs/reference/agentic-elicitation-criteria-2026-08-06T14-11-18Z.md](<../../docs/reference/agentic-elicitation-criteria-2026-08-06T14-11-18Z.md>) (turn 2: hourglass, proof obligations, invariants, smells, test matrix)\n* Reference codebase (read-only): `../brunch` — esp. `src/agents/runtime/elicitor/`, `src/exchanges/`, `src/agents/skills/`\n* External: [Flue docs](), [zil-lean]()\n\n### Charter decisions (pre-map grilling, 2026-08-06)\n\n* Destination artifact = a spec (not a decision-set alone, not a build)\n* Elicitor-first: the elicitor is this map's spine\n* Greenfield reimplementation; brunch is reference architecture, not shared code\n* Fully decoupled from brunch's September MVP\n* Pi-family substrate committed; harness-agnostic core is a named non-goal\n* Brunch's exchange vocabulary is prior art to critique, not an incumbent to preserve\n* Persistence plugin-owned (in accord with deploy target) is a hypothesis to test, not a decision — **resolved 2026-08-10, flipped** by [Multi-session elicitation & durable target state](): storage port harness-defined, binding-implemented, plugin-blind\n* Two live dev targets from day one (forces generalization on both pack axes); BPMN/process-mining named third\n* \"brunch-lite\" is a temporary label; real name is fog\n* **Principle v2** (ratified 2026-08-06, replaces \"behavioral over procedural\" after an evidence pass over `~/Clones/mattpocock/skills` and `../brunch/docs/design/BEHAVIORAL_KERNELS.md`): *procedure for mechanism, anchors for judgment, shapes for output*. Short step sequences with checkable completion criteria where order matters; leading words wherever judgment is required; annotated shapes for everything produced; reference disclosed progressively. Design against sprawl, negation-steering, no-ops, judgment-as-procedure — not against procedure itself. `writing-for-agents` is the pack-authoring standard; kernel cards (BEHAVIORAL_KERNELS.md) are the unit of elicitation guidance.\n\n## Decisions so far\n\n\n\n* [Assemble the spec]() — **the destination: [spec.md]() assembled** (fourteen sections + adjudications appendix) from the full working set in amendment order; all seven pre-pass contradictions adjudicated (storage port binding-implemented with the `db.ts` reconciliation; ops stay pure; status derived never stored, retraction specified; tap-ness as a transport fact via a reserved reply encoding; invariant 1 reconciled with the provenance rule; per-message current-affordance surface with identity on tool output parts; no instruction interpolation + harness-mechanical reply binding, no echo token); ten harness invariants restated in envelope vocabulary; `CONTEXT.md` gained the envelope vocabulary and the kernel-compound ruling (kernel card kept, \"kernel invariants\" → harness invariants); rename propagated over tickets 02/07. **The frontier is empty — this map is complete.** Remaining fog is post-spec and graduates with the build effort.\n* [Walking skeleton: sweep seam on Flue]() — **all four capabilities hold on Flue** (two native, two binding-absorbed, none forbidden), closing pre-pass L1–L3: settlement trigger = `useAgentFinish` would-stop seam with same-response signal steering (fires on `terminate:true` suspensions too — the pending guard is load-bearing; needs a re-nudge loop guard); session-entry read = binding-absorbed via the public durable-history projection (no in-process API; `purpose` discriminates provenance) with the load-bearing HITL amendment that **evidence anchoring is harness-resolved — the model cites verbatim user quotes, never entry numbers** (sharpens issue 12's span shape: excerpt primary, pointer derived); on-behalf injection = `kind:'signal'` append/dispatch, projecting structurally non-user so the citable-evidence rule is mechanically enforced (live: unscripted conversational conflict-surfacing off the re-entry briefing; a fact recovered from a *superseded* capture, honestly marked inferred); transactional store = binding-owned entirely (content-keyed idempotence is load-bearing under Flue's at-least-once re-execution). **Amends Shipping shape's capability list to ten entries** (adds would-stop seam, entry-projection read, signal injection, transactional store). New payload-stratum spec examples: absence smuggled as payload; compound payloads make supersession lossy. Prototype on branch `prototype/13-sweep-seam`.\n* [Multi-session elicitation & durable target state]() — **durable target-document, transient sessions, sweep as the only bridge**: \"target\" splits into **target-domain** (family a plugin defines) / **target-document** (durable unit sessions attach to — capture store **plus all session logs** as truth, renders strictly derived); session = one substrate conversation (per-session state: log + swept high-water mark + pending-affordance slot), never formally closes; **interleaved-only** concurrency — serialized store, whole-sweep-atomic refusals, the single-hop refusal doubling as the stale-session guard (true concurrency + per-item application → fog); re-entry briefing = injected on-behalf-of-user state messages (Pi convention), advisory-only, with the sharpened provenance rule that **only true user entries are citable evidence**; completion = derived status, never a write gate; **persistence hypothesis flipped** — storage port harness-defined, binding-implemented, plugin-blind (plugin-addressable only via PluginContext-passed methods); evidence spans = pointer + quoted excerpt.\n* [Shipping shape: kernel library vs. Flue agent]() — **harness library in a thin host-authored agent, ratified with the second-binding test**: the spec enumerates a six-item substrate-capability list as the core/binding seam (portability = pressure test, not build target; **binding** entered the glossary); topology = Bun-workspace monorepo in this repo — `packages/core` (plugin SDK is its export surface; `core/testing` subpath), `packages/flue` (binding), `packages/plugin-gherkin` + `packages/plugin-assurance` (renamed from `plugin-proof-obligations` 2026-08-10, per the Formal-verification canon survey's category-error verdict; plugins depend on core *only* — stated spec invariant), `apps/dev`; Valibot throughout; tool prefix from product name (provisionally `bl_*`, never `elicit_*` — strings name identity, not function); workspace-internal, no publishing; testing = **generation-first fixtures over a deterministic replay driver** (kernel invariants as properties, `arbitraryFromSchema`, `fc.commands` conversation fuzzing from the envelope vocabulary, model as offline generator never CI oracle, regenerate-on-declaration-change); dev app chartered with three roles (dev loop / target-gallery demo / diagnostic probe views); one agent per target; milestone one local-only with remote-parity constraints pinned in spec; CI smoke = build + simulation suite.\n* [Walking skeleton: Flue question round-trip]() — **transport proven, one amendment**: the one-channel multiplex works end-to-end (three forms, markdown floor, string-only replies, absence strip, redirect), but the fixed data channel is a *one-live-affordance slot per message* (update-in-place at every layer — stream, history, reply read-back), so per-ask identity/payload must ride the ask tool's **output part** and the kernel must **reject a second ask per batch** as mechanism; echo token not needed (adjacency + persistent pending state bound every bare-string reply); turn suspension (`terminate: true` + persistent state + fresh dispatch) sufficient, with a wake wart — instruction-interpolated pending state triggers an extra \"instructions updated\" model turn per ask; cancelled/redirected questions read back cleanly via `record_interpretation` evidence. Flue facts: `@flue/vite` needs vite ^8; dev server gives `app.ts` the whole request space (ui is app-served). Prototype on branch `prototype/10-flue-roundtrip`.\n* [Logic-prototype: capture sweep & settlement]() — **all five capture hypotheses hold** in a working prototype (branch `prototype/11-capture-sweep`; double-clickable demo + headless driver), each sharpened: settlement splits into **trigger vs judgment** (lifecycle-event wiring *not* proven in ticket 10 — misattribution caught by the 2026-08-10 consistency pre-pass; delegated to the sweep-seam walking skeleton, ticket 13); idempotence splits into **mechanical (harness, evidence-anchored capture identity) vs semantic (plugin reconcile)** and is content-based, so re-sweeps repair omissions; absences are **evidence, not agenda** (re-ask runs through plugin validate → issues); single-hop supersession over active heads = the lost-update guard; **two supersession channels** (sweep-time link / issue-time resolution record) with read-time *derived* status suggested (cf. issue 09). Amendments: **two validation strata** (envelope-level harness-owned refusals + advisories / payload-level plugin ops — strengthens smallest-honest-plugin), op cadence = pure orchestration policy (snapshot-purity), **resume-time sweep reconciliation** (unswept tail as computed re-entry fact). Graduated the multi-session fog → ticket 12.\n* [Questioning-UX contract]() — the reframe: **no exchange-pair ontology** — conversation is primary, structured questions are **affordance emissions** committed to session as evidence, capture via idempotent **range-sweeps** on agent-judged, range-level **settlement**; shells renamed **substrate / ui / harness / plugin** (the effort's essence is *harness-engineering*; IoC via narrow injected PluginContext — the ask API is part of it); harness fixes: three baseline shapes + questionnaire chaining, absence interpret-by-default/afford-when-structured, **one multiplexed data channel** with markdown floor (Flue: outbound rich / inbound string-only), interpretation render harness-owned with plugin-optional typed renderer (JSON default), conflicts close only via explicit **resolution records**, no question-budget machinery (economical interviewing = judgment guidance); all shape-level commitments are working hypotheses with proof obligations delegated to the two prototype tickets created here (10, 11); **built-artifacts-as-proofs** ratified into Notes.\n* [Contract decomposition]() — the load-bearing resolution: **agent-forward hybrid** (agent judgment owns the loop; deterministic mechanism as tools; typed-issue backpressure); **no universal IR** — the kernel owns only a domain-free **capture envelope** (evidence spans, epistemic-status enum, absence states, alternatives, one `supersedes` link) around opaque plugin payloads, no kernel edges or graph (conflicts/equivalence are typed issues, resolved only explicitly); operations tiered `project`+`validate` required / `reconcile` optional / `observe` agent-native, all snapshot-in/deltas-out with typed loss reports; **facts computed, weights judged** dialogue policy (no scoring engine); packs = kernel cards + shapes + validators + shallow schemas + completion contract; smallest-honest-plugin test; five proof obligations + ten kernel invariants + gating tests (reprojection, minimal pairs, black-box authoring) adopted as spec acceptance material; multi-plugin composition, removal, replay, capability negotiation, versioning deferred to fog. Full ownership table in the ticket.\n* [Flue architecture deep-read]() — embedding is Flue's grain: ship the kernel as a custom hook (`useElicitation(plugin)` — final name per Shipping shape) + exportable `defineTool`s over a thin host-authored `'use agent'` module; Pi is exposed only at the provider layer (no path to Pi's loop/extensions — brunch-style Pi assumptions don't transfer); durability contract identical local/remote but storage/ownership differ; **no first-class ask-the-user primitive — the kernel must own its turn-suspension protocol** (`terminate: true` tool + persistent state + data part, answer arrives as fresh dispatch); constraints: Valibot at every schema boundary, stable-tool-set cache economics, subagents conversationally sterile, non-React hosts must build on `@flue/sdk`.\n* [Formal-verification canon survey]() — align the second target to a **GSN skeleton** (Goal/Strategy/Solution/Assumption/Justification; the one canon built for argued claims about a system) with **Dafny nouns** (`requires`/`ensures`/`invariant`/`lemma`/`assume` transfer; `modifies`/ghost don't) and **Lean sorry-taint semantics**; the headline artifact is a `dafny audit`-style **assumption ledger** (per-claim status becomes a derived label); acyclicity kept as a deliberate restriction with `decreases` as future escape hatch; sell the Datalog closure as taint-propagation, never as an assurance verdict; **\"proof obligations\" is a category error to verification readers — rename the target** (resolved 2026-08-10: the second target is the **assurance argument**, package `plugin-assurance`); Geolog is *not* an ARIA artifact (negative result) but coherent-logic saturation is the validator's genuinely canonical lineage; concrete milestone-one contract sketch (one `Statement` record, four edge kinds, five-stratum status rules) is in the ticket.\n* [Dev-target portfolio confirmation]() — confirmed `elicit-gherkin` + `elicit-proof-obligations` live (BPMN third, full elicit-lean deferred); **hybrid order**: both packs authored before the pack interface freezes, gherkin wires end-to-end first; proof-obligations format = zil-lean's ideas in our own claim-DAG serialization, hewing to existing canon (Dafny leading candidate — grounded by new ticket 09); gherkin milestone-one validation = parse validity + pack-declared step lexicon; surfaced the **behavioral-over-procedural** design principle (routed to Contract decomposition).\n* [Brunch exchange-schema audit]() — full catalog + three-way classification: **generic** (the `ask` one-terminal-four-shapes primitive, declared continuations that make offer→answer non-forgeable, answered/cancelled/unavailable property-presence union, self-contained terminals, boundary-teaching schemas, no-storage asking agenda + private scratchpad, hash-pinned prompt directives); **generalize-with-changes** (present-then-ask two-step → one `present(form, payload)` with pluggable validators, parent-link instead of enumerated prev/curr/next unions, opaque target-supplied receipts, rubrics declared by the target); **brunch-specific** (review-set node/edge schema, plane vocabulary, `graph_refs`, `map`/`tutorial` skills). Strain marks confirm the \"just a guess\" caution: wire vocabulary outlived tool names, a digest-into-review merge was retracted, doc/code drift on edge categories. 15 inherit/avoid lessons feed tickets 04 and 05.\n* [zil-lean survey]() — zil-lean contains **no elicitation** (agent-authored 2-day snapshot; Datalog/Zanzibar-style claim graph in Lean 4 + Clojure) but is a strong existence proof of the kernel's claim-graph layer: evidence-graded assurance lattice with prohibited promotions, full derivation provenance, assumption/lemma/theorem/guarantee vocabulary with derived PROVED/CONDITIONAL/WEAK/BROKEN statuses. **Verdict: full elicit-lean is not dev-sized; the \"elicit-proof-obligations\" slice is** (claim DAG + criticality + evidence refs, validated by acyclicity + Datalog closure — no Lean statements) and beats BPMN as second target on both pack axes; BPMN stays third.\n\n## Not yet specified\n\n* Diagnostic \"exploded view\" harness for HITL prototype sessions — an instrumented walking-skeleton UI with parallel state readout and labeled moving parts (persistent state, pending affordance slot, tool traffic, terminate boundaries, advisory wakes, activation timing) so the human can react to internals, not just the chat surface; surfaced by the user after the ticket-10 walking skeleton, wanted \"at some point\" — **host identified** by the Shipping-shape resolution: the dev app's probe view (role 3 of its charter); graduates with the dev app, post-spec\n* Remote deploy target choice & provisioning — pending an infra-colleagues conversation (2026-08-07); blocks nothing on this map (the spec pins remote-parity constraints without naming the target)\n* Unstructured-ingest crossover at scale — the IR inside the contract-decomposition ticket absorbs the parsing/mapping core; what remains foggy is bulk/brownfield ingest as a first-class mode\n* elicit-to-petri-net as a future plugin — abstract questions (what fits a net; what IR precedes a net) wait until the pack model exists\n* JSONL-vs-YAML storage-format comparison — persistence resolved harness-defined / binding-implemented (multi-session resolution, 2026-08-10), so the format is a binding-internal choice; graduates with the local binding implementation, post-spec\n* Concurrent-session coordination beyond interleaving — simultaneous sweeps against one target-document, per-item partial sweep application, merge machinery; the spec's stance (serialized store + single-hop refusal + re-entry advisory) covers interleaving, and this graduates only if a real concurrent consumer appears\n* The real product name\n* Deferred plugin-ecosystem machinery (from Contract decomposition): simultaneous multi-plugin composition, plugin removal, full replay, capability negotiation, version/migration (spec names the five version axes only)\n\n## Out of scope\n\n* **Executor-standalone** — a different kind of project; the spec only names the elicitor→executor handoff seam\n* **Harness-agnostic core** — every named consumer is Pi-family; revisit only if a non-Pi consumer materializes (would be a fresh effort under a redrawn destination)\n+++", - "proposedBodySha256": "8ae07f2f4e89c51313aa1147df800dd286672bb5323c70a5034de3222b406314", - "ambiguity": null, - "notes": null - }, - { - "id": "4f506cd8-070b-41ad-b208-0b625f987418", - "identifier": "FE-1367", - "url": "https://linear.app/hash/issue/FE-1367/flue-architecture-deep-read-archive", - "state": { - "name": "Done", - "type": "completed" - }, - "parent": { - "id": "a5053b51-937d-453f-9c4e-fe8e49e912ad", - "identifier": "FE-1366", - "title": "Spec the elicitation harness architecture (archived wayfinder map)" - }, - "sourceUpdatedAt": "2026-08-19T16:20:27.414Z", - "sourceTitle": "Flue architecture deep-read [archive]", - "sourceDescriptionSha256": "5f6506ff45d1964c8545e824d2c5b8f989410ac176bfa5fef6b9cfe150109239", - "sourceTitleSha256": "d56adb14bff1dbfe03cac1c1c2a312d5fc3ac3693b36a8324c59c080ef5aea2a", - "oldOuter": "Read Flue — the web/agent framework the interviewing library runs on — closely enough to know how the library should ship on it. Conclusion: embed as a library inside a host-authored app, which is Flue's natural grain; Flue has no built-in \"pause and ask the user\" facility, so the library owns its own turn-taking mechanism. Resolved 2026-08-06; part of the completed elicitation-harness planning (see parent map).", - "proposedTitle": "Define how the elicitation harness uses Flue", - "proposedOuter": "This research determined how the interviewing library should run on Flue. The library belongs inside a host-authored app, and it must manage conversational turn-taking because Flue does not provide a built-in way to pause and ask the user. The research was resolved on 2026-08-06 as part of the completed elicitation-harness plan.", - "extractionMethod": "first-standalone-divider", - "innerRecord": "\n> **Archive migration (2026-08-11).** Resolved ticket from the local-markdown wayfinder effort `elicitation-kernel` (`brunch-lite:.scratch/elicitation-kernel/issues/01-flue-architecture-deep-read.md`), mirrored to Linear for team visibility. Full original content below, including the resolution.\n\n---\n\n# Flue architecture deep-read\n\nType: research\nStatus: resolved\nResolved: 2026-08-06\n\n## Question\n\nWhat does Flue's architecture offer — and constrain — for embedding an elicitation kernel, such that the shipping-shape and contract-decomposition tickets can decide on facts rather than the marketing page?\n\nSpecifically:\n\n* The agent programming model (React-like hooks API): what is \"an agent\" as a unit of code, state, and deployment?\n* How skills, tools, subagents, persistent state, sandboxes, and channels are defined and composed\n* How Pi is exposed through Flue — can an embedded library reach Pi primitives directly, or only through Flue's abstractions?\n* Local vs. remote parity: what changes between a Node local run and a Cloudflare/CI deploy? What state survives where?\n* Is a \"kernel library embedded in a thin Flue agent\" a natural pattern, or does Flue push toward the agent *being* the program?\n* Channels (Slack/Teams/Discord/GitHub) as host input pathways — what does a host surface look like in Flue terms?\n\nSources: [https://flueframework.com/docs/guide/]() (pages are fetchable as markdown at `/docs/guide//index.md`).\n\n## Answer\n\n> Resolved by `/research` subagent, 2026-08-06.\n\n# Flue Architecture — Findings for an Embedded \"Elicitation Kernel\"\n\nVersion context: `@flue/runtime` **2.0.3** (npm, published 2026-08-05; package created 2026-05-14, 48 versions). Siblings `@flue/cli`, `@flue/sdk`, `@flue/react`, `@flue/vite` all at 2.0.3. Docs pages carry \"Last updated Jul 21–23, 2026\". Repo: `github.com/withastro/flue`.\n\n## 1. Agent as a unit\n\n**Code.** An agent is a plain exported, capitalized JS function that returns its system-prompt string. Capabilities come from `use*` hooks called in its body. A module marked `'use agent'` is scanned at build time by the Vite plugin (`flue()` from `@flue/vite`); every exported capitalized function in it becomes a registered agent. One file may export several.\n\n**Lifecycle.** The function **re-renders before every model call** and rebuilds instructions and its declared resource set from scratch. Unlike React, resource hooks *may* be called conditionally — this is the framework's central design bet (\"an agent is a program to write, not an object to configure\"). Between renders the runtime diffs the declared set against a \"last-narrated snapshot\" and appends a framework-authored `resources` signal at the next turn boundary (\"New tool available: …\"). Skill/subagent catalogs are frozen on a durable baseline so flips don't bust the prompt cache; custom-tool changes rewrite the native tools array and *do* invalidate cache (documented exception: a tool unlocked by a completed tool call, on non-Haiku Anthropic models).\n\n**State.** Durable identity is the function name (or the `Fn.agentName` static) — it keys conversation storage, so renaming without pinning `agentName` is a DB migration. Each conversation is addressed by a caller-chosen `id`. `Fn.initialData` (a Valibot schema static) validates creation-time data exactly once.\n\n**Deployment.** Registration ≠ mounting. `createAgentRouter(agent)` returns a Hono sub-app you mount yourself in `src/app.ts`; agents reached only via `dispatch(...)` need no mount at all. Entry points: `flue run ` (CLI, no server), HTTP `POST /:id` (202 fire-and-forget), `dispatch(agent, {id, message, initialData, uid})`, and `start()` + `init()` for standalone Node processes.\n\n## 2. Primitive inventory (what a library could register/own)\n\n* **Tools.** `defineTool({name, description, input?, output?, harness?, durable?, run})` — Valibot schemas, frozen at module load, importable from a light `@flue/runtime/tool` entry \"for tool-only modules\". Mounted per render via `useTool(def)`. `run` receives `{data, signal, log, toolCallId}`; `harness: true` adds `harness`, `durable: true` adds `step`. Reserved names: `task`, `activate_skill`, `read_skill_resource`, plus sandbox built-ins (`read/write/edit/bash/grep/glob`). **This is the primary registration surface an embedded kernel would own.**\n* **Harness tools** (worth calling out separately). `harness.sandbox` (direct file/exec verbs, *never recorded in the conversation*) and `harness.prompt(text, {result: Schema, tools, model, thinkingLevel, images})` — runs a model operation in a private scratch conversation invisible to clients, with Valibot-validated structured output enforced via a framework-injected `finish` tool. Repeated calls continue that scratch conversation. **This is the closest thing Flue has to a sub-LLM call primitive, and it is a strong fit for a kernel's internal extraction/normalization steps.** Also `harness.compact()`.\n* **Skills.** Open Agent Skills format (`SKILL.md` + supporting files), or `defineSkill({name, description, instructions, files})` for generated/assembled content. Progressive disclosure: one catalog line always present; full instructions arrive as an `activate_skill` tool *result*, so activation never mutates the system prompt and the cached prefix survives. Supporting files are served read-only from the app bundle at virtual paths — **not** copied into the sandbox. Also auto-discovered from `/.agents/skills/` when a sandbox exists.\n* **Subagents.** `defineSubagent({name, description, agent})` + `useSubagent()`. Model-driven via the always-present `task` tool. Child inherits *environment* (sandbox, workspace context, parent model) but **nothing conversational** — no history, instructions, tools, skills, persistent state, or initialData. Only the final message returns. Explicitly *not* a second addressable agent: \"no conversation id, no persistent state, and no address.\" `GeneralSubagent` ships as a blank delegate under `flue-general`.\n* **Persistent state.** `usePersistentState(name, initial)` — React-shaped, JSON-serializable, keyed by name, durable for the life of the conversation. Writes commit **atomically with the unit of work that made them** (tool batch, or event-hook seam checkpoint), which is what makes it the correct guard for at-least-once callbacks. Prefer updater functions; the render value is a snapshot.\n* **Sandboxes.** `useSandbox(factory, {cwd})`. `local()` from `@flue/runtime/node`, or adapters (Daytona, E2B, Modal, Cloudflare Sandbox/Computer) built against the Sandbox Adapter API. Attaching one adds the six built-in file/shell tools; an adapter may replace that set entirely. Sandbox filesystems are **ephemeral by default** and independent of conversation durability.\n* **Channels.** Inbound-only verified HTTP ingress (Slack, Discord, Teams, GitHub, Stripe, …), shipped as **blueprints** (`flue add channel slack`) that generate project source rather than as opaque packages. A channel is \"an object with declarative routes\"; `createChannelRouter(routes)` builds one by hand. Handlers call `dispatch(...)` themselves. **Outbound is explicitly not Flue's job** — no send-message abstraction; you use the provider SDK and expose narrow tools with the destination bound in trusted code.\n* **Event hooks / data writers.** `useAgentStart` (async — the load-data seam), `useAgentFinish`, `useResponseStart/Finish` (return values merge onto response *metadata*). `useDataWriter(name, {schema})` streams typed structured data parts to clients (`{type: 'data-orderCard', data}`), strictly one-way out of the agent — **the model never sees data parts**, and a write never re-renders the agent.\n* **Custom hooks.** Plain `use*` functions composing the built-ins, returning instruction fragments to the caller. Docs explicitly frame this as the reuse/packaging unit.\n\n## 3. Pi exposure\n\nPartial and deliberate, concentrated at the **model-provider layer**. Documented facts:\n\n* `@flue/runtime`'s npm dependencies include `@earendil-works/pi-agent-core` and `@earendil-works/pi-ai` directly.\n* Models guide: \"Providers are Pi's own objects, and Flue accepts them directly: build one with Pi's `createProvider()` … and hand it to `setProvider()` at module top level in `app.ts`.\" Examples import from `@earendil-works/pi-ai`, `.../api/anthropic-messages.lazy`, `.../providers/anthropic`, `.../api/openai-completions.lazy`.\n* `start({providers})` registers \"the Pi providers this runtime registers, replacing the default set.\"\n* `PromptImage` \"re-exports pi-ai's `ImageContent`.\"\n* Why-Flue: \"Flue builds on Pi, the open agent harness behind OpenClaw, and integrates it deeply into every agent you build.\"\n\nEverything else — conversation loop, tool dispatch, skills, subagents, durability — sits behind Flue's own abstractions. **Inference:** there is no documented path to Pi's agent loop, message list, or Pi extension/package system from inside a Flue agent. A kernel that currently assumes Pi-level tool registration, transcript control, or `renderResult`-style TUI hooks would have to be re-expressed entirely in Flue's `defineTool` + render model. Flue also uses Durable Streams (`@durable-streams/client`) as its client transport protocol.\n\n## 4. Local vs. remote parity\n\nDurable *records and recovery decisions are identical* across targets; ownership and wake mechanics differ.\n\n| | Node | Cloudflare |\n| -- | -- | -- |\n| Unit | one server process, coordinator with **lease-based** ownership | one **Durable Object per agent conversation**, own SQLite; ownership structural |\n| Recovery | startup reconciliation + periodic lease scans | wake-on-start + self-renewing durable wake schedule, bounded supervision pass |\n| Storage | `db.ts` adapter required (sqlite, Postgres, MySQL, Mongo, Redis, libSQL, Turso, Supabase, Valkey); **without one, conversations are process-local memory and a restart loses them** | built in |\n| Constraint | must route each conversation to exactly one owner; active-active / round-robin for the same conversation is unsafe | every deployed agent needs an append-only DO migration tag in your hand-authored `wrangler.jsonc`; renames/deletes are storage migrations (`renamed_classes` / `deleted_classes`) |\n\nCore contract: \"Every accepted submission reaches exactly one durable terminal outcome — completed, failed, or aborted — no matter how many crashes happen in between.\" Submissions are recorded durably *before* any model work; that is what the 202 and the `DispatchReceipt` attest to. There is a retry budget plus a wall-clock timeout, enforced preemptively via the attempt's abort signal.\n\n**Deliberately not durable:** sandbox files (rebuilt fresh on each initialization unless the adapter keys a provider workspace on the instance id — \"a durable database does not make a sandbox durable\"); in-flight local promises (persist the `DispatchReceipt` and `read(receipt)` re-attaches from any process); and **code outside the agent** — Flue explicitly does not checkpoint arbitrary TypeScript. For that, docs point to Cloudflare Workflows / Inngest / Temporal calling Flue \"like any other service.\" External side effects are never recorded, only that a tool ran and what it returned.\n\nAlso: there is **no GitHub Actions or GitLab *target***. CI is just `flue run` in a shell step (`--new` + deterministic `--id` gives exactly-once conversation creation). GHA/GitLab appear only in the ecosystem catalog. Builds are Vite (`vite build` → `dist/server.mjs` + `dist/app.mjs`, or the Cloudflare plugin's output). Node deps are externalized, not bundled; the built server does not load `.env`.\n\n## 5. Library-in-agent vs. agent-as-product\n\n**Embedding is a natural pattern, and the docs endorse it explicitly.** Evidence:\n\n* `defineTool` / `defineSkill` / `defineSubagent` all exist specifically as *exportable, frozen, module-load-validated* units — described as \"the natural shape for tools shared across agents\" and \"the exportable unit — define a delegate once, mount it from any agent.\"\n* **Custom hooks are the documented composition unit:** \"a `useGitHub()` hook that bundles the right tools, skills, and instructions can be written once and dropped into every agent that works with GitHub.\" That is precisely a kernel-in-a-hook.\n* `@flue/runtime/tool` is a lighter entry point for tool-only modules — a library can depend on Flue without pulling in the server runtime.\n* Source-dir resolution order puts `.flue/` **first**, described as \"a self-contained Flue source area inside a larger application,\" with authored modules free to \"import ordinary supporting code from elsewhere in the project.\"\n* Per-mount overrides \"spread cleanly\": `useSubagent({ ...issueClassifier, model: 'anthropic/claude-haiku-4-5' })`.\n\nCounter-pressure: Flue insists the *agent itself* be a program, not config, and the `'use agent'` build-time scan means a library **cannot ship a pre-registered agent** — the agent module must be authored in the consuming project.\n\n**Recommendation (inference):** ship the kernel as a published custom hook — `useElicitationKernel(targetPlugin)` — that internally calls `useTool` / `useSkill` / `usePersistentState` / `useDataWriter` and returns instruction fragments, plus raw `defineTool` exports for hosts that want selective mounting. The host owns a ~10-line `'use agent'` module, the `app.ts` mount, and `db.ts`. This is library-in-a-thin-agent, and Flue's grain supports it. One real constraint: tool names are globally unique per render and collide with reserved names, so the kernel needs a namespacing convention.\n\n## 6. Channels as host surfaces\n\nA \"host input pathway\" in Flue is: verified ingress → `dispatch(agent, {id, message, initialData})`. Three distinct payload lanes:\n\n* `initialData` — recorded once at conversation creation, validated against the agent's schema static, read with `useInitialData()`, immutable thereafter. This is where an elicitation *target descriptor* belongs.\n* `kind: 'signal'` **messages** — `{type, body, attributes}` where `attributes` is a string→string map of facts *trusted code* attached. Read with `useDelivery()`. Docs push this hard as the authorization pattern: \"the model may choose an order ID to look up, but it cannot choose the customer.\" For a kernel, this is the channel for host-verified respondent identity. Channel deliveries are signals rather than `user` messages precisely because a Slack thread is multi-participant.\n* `kind: 'user'` — direct human turns.\n\n**Interviewing-UX rendering surfaces, ranked by fit:**\n\n1. `useDataWriter` **+** `@flue/react`**.** Named, schema-validated structured data parts arriving alongside text on the same message; a tool can write several times mid-run to drive live progress. `useFlueAgent({url})` gives `messages`, `parts`, `status`, `historyReady`, `sendMessage()`, `refresh()`. Message parts are `text | reasoning | dynamic-tool | file`, and **validated structured tool output is preserved on the** `dynamic-tool` **part's** `output` — the React docs say this exists \"so applications can render custom tool interfaces without a separate data-event channel.\" Direct fit for question cards, choice sets, and review panes.\n2. **Chat channels** (Slack/Teams/Discord/GitHub) for text-shaped interviewing only. There is **no outbound abstraction** — every reply is a tool you write against the provider SDK with the destination bound in trusted code (the `replyInThread(data)` pattern). Rich Slack Block Kit interviewing is entirely your code.\n\n**Gap worth flagging loudly: Flue documents no first-class human-in-the-loop / elicitation / interrupt primitive.** There is no \"ask the user and suspend\" hook. `terminate: true` on a tool result ends the turn once the current batch settles; the documented pattern for waiting on a human is state-gated tools (`record_approval` unlocks `publish_release`) plus a new inbound submission. **Inference:** the kernel must implement its own turn-suspension protocol — a `terminate: true` tool that writes the pending question into `usePersistentState` and emits a data part, with the host's answer arriving as a fresh `dispatch`. That is exactly the kind of thing a kernel *should* own, but Flue provides no scaffolding for it, so it is net-new work either way.\n\n## 7. Constraints & risks\n\n* **Maturity.** 2.0.3, with 2.0 a full API rewrite around hooks announced this cycle; a Migration Guide exists. `@flue/vite` first published 2026-07-10 (19 versions). Fast-moving — expect churn.\n* **Build-time magic is load-bearing.** `'use agent'` scanning, `SKILL.md` module imports, and agent-identity stamping all happen in the Vite plugin. **This effectively requires Vite.** A kernel published as a library must not depend on that transform internally — keep to `defineTool`/`defineSkill` (plain runtime calls) and let the host own the scanned module.\n* **Durable-identity coupling.** Conversation storage is keyed by agent function name. Always pin `agentName`.\n* **Cloudflare migration ceremony.** Adding an agent is always agent + mount + new DO migration tag. If the design ever wants *dynamic* agent creation per elicitation target, Cloudflare forbids it — agents are a build-time set. Use one agent + many conversation ids instead.\n* **Prompt-cache economics constrain dynamism.** \"Gate tools on state that changes rarely.\" An interviewing kernel that swaps tools per question would thrash the cache. Prefer one stable tool set + state-driven instructions, or skill activation (cache-safe by design).\n* **Valibot lock-in** at every schema boundary (`input`, `output`, `initialData`, `useDataWriter`, `harness.prompt({result})`). A kernel with Zod-based plugin contracts needs a conversion layer or a dual-schema strategy.\n* **Node multi-replica.** No active-active per conversation; you must own routing.\n* **Subagents are conversationally sterile** — no id, no state, no address. If elicitation-target plugins need their own durable memory, they cannot be subagents; they must be state + tools inside the parent, or separately registered agents.\n* **Petrinaut / non-React web-UI host risk:** the rich path (`useFlueAgent`) is React-specific. `@flue/sdk` (`createFlueClient` → `send/read/wait/observe/history`, built on `@durable-streams/client`) is framework-neutral and is what `@flue/react` sits on, so a non-React host is viable — but it must reimplement the materialized-snapshot / reconnect / canonical-reset layer that `useFlueAgent` provides. **Inference:** budget for that, or wrap `createFlueClient` directly and accept a thinner UI contract.\n* `skills` **frontmatter** `allowed-tools` **is accepted but not enforced** — Flue does not restrict the session toolset from a skill. Any kernel-level tool gating must be done with conditional `useTool`.\n\n## 8. Unreached sources\n\n**HTTP 404 (do not exist at those paths):** `/docs/guide/deployment/`, `/docs/guide/state/`, `/docs/guide/cloudflare/`, `/docs/guide/cli/`, `/docs/guide/github-actions-target/`, `/docs/reference/index.md`, `/llms.txt`. (Real equivalents: `/docs/guide/deploy/`, state is a section of `agent-hooks`, `/docs/guide/cloudflare-target/`.)\n\n**Exist but not fetched** (nav-confirmed; would sharpen specific answers): `/docs/guide/database/`, `/docs/guide/schedules/`, `/docs/guide/evals/`, `/docs/guide/observability/`, `/docs/guide/configuration/`, `/docs/guide/migration/`, `/docs/guide/agent-behavior/`, `/docs/reference/sandbox-api/`, `/docs/reference/provider-api/`, the full `/docs/reference/agent-hooks-api/` (indexed, read only via search), the CLI and Agent SDK reference sections, all `/docs/ecosystem/*` sub-pages, the `@flue/react` package README, and `examples/react-chat`. The GitHub repo (`withastro/flue`) source and Pi's own docs (`pi.dev/docs/latest`) were not read.", - "innerSha256": "6d565c7365ebc78f76a053a624703acd07ac03cb40a6c8cb3c127bc0de30a087", - "proposedBody": "This research determined how the interviewing library should run on Flue. The library belongs inside a host-authored app, and it must manage conversational turn-taking because Flue does not provide a built-in way to pause and ask the user. The research was resolved on 2026-08-06 as part of the completed elicitation-harness plan.\n\n+++🏗️ Agent notes\n\n> **Archive migration (2026-08-11).** Resolved ticket from the local-markdown wayfinder effort `elicitation-kernel` (`brunch-lite:.scratch/elicitation-kernel/issues/01-flue-architecture-deep-read.md`), mirrored to Linear for team visibility. Full original content below, including the resolution.\n\n---\n\n# Flue architecture deep-read\n\nType: research\nStatus: resolved\nResolved: 2026-08-06\n\n## Question\n\nWhat does Flue's architecture offer — and constrain — for embedding an elicitation kernel, such that the shipping-shape and contract-decomposition tickets can decide on facts rather than the marketing page?\n\nSpecifically:\n\n* The agent programming model (React-like hooks API): what is \"an agent\" as a unit of code, state, and deployment?\n* How skills, tools, subagents, persistent state, sandboxes, and channels are defined and composed\n* How Pi is exposed through Flue — can an embedded library reach Pi primitives directly, or only through Flue's abstractions?\n* Local vs. remote parity: what changes between a Node local run and a Cloudflare/CI deploy? What state survives where?\n* Is a \"kernel library embedded in a thin Flue agent\" a natural pattern, or does Flue push toward the agent *being* the program?\n* Channels (Slack/Teams/Discord/GitHub) as host input pathways — what does a host surface look like in Flue terms?\n\nSources: [https://flueframework.com/docs/guide/]() (pages are fetchable as markdown at `/docs/guide//index.md`).\n\n## Answer\n\n> Resolved by `/research` subagent, 2026-08-06.\n\n# Flue Architecture — Findings for an Embedded \"Elicitation Kernel\"\n\nVersion context: `@flue/runtime` **2.0.3** (npm, published 2026-08-05; package created 2026-05-14, 48 versions). Siblings `@flue/cli`, `@flue/sdk`, `@flue/react`, `@flue/vite` all at 2.0.3. Docs pages carry \"Last updated Jul 21–23, 2026\". Repo: `github.com/withastro/flue`.\n\n## 1. Agent as a unit\n\n**Code.** An agent is a plain exported, capitalized JS function that returns its system-prompt string. Capabilities come from `use*` hooks called in its body. A module marked `'use agent'` is scanned at build time by the Vite plugin (`flue()` from `@flue/vite`); every exported capitalized function in it becomes a registered agent. One file may export several.\n\n**Lifecycle.** The function **re-renders before every model call** and rebuilds instructions and its declared resource set from scratch. Unlike React, resource hooks *may* be called conditionally — this is the framework's central design bet (\"an agent is a program to write, not an object to configure\"). Between renders the runtime diffs the declared set against a \"last-narrated snapshot\" and appends a framework-authored `resources` signal at the next turn boundary (\"New tool available: …\"). Skill/subagent catalogs are frozen on a durable baseline so flips don't bust the prompt cache; custom-tool changes rewrite the native tools array and *do* invalidate cache (documented exception: a tool unlocked by a completed tool call, on non-Haiku Anthropic models).\n\n**State.** Durable identity is the function name (or the `Fn.agentName` static) — it keys conversation storage, so renaming without pinning `agentName` is a DB migration. Each conversation is addressed by a caller-chosen `id`. `Fn.initialData` (a Valibot schema static) validates creation-time data exactly once.\n\n**Deployment.** Registration ≠ mounting. `createAgentRouter(agent)` returns a Hono sub-app you mount yourself in `src/app.ts`; agents reached only via `dispatch(...)` need no mount at all. Entry points: `flue run ` (CLI, no server), HTTP `POST /:id` (202 fire-and-forget), `dispatch(agent, {id, message, initialData, uid})`, and `start()` + `init()` for standalone Node processes.\n\n## 2. Primitive inventory (what a library could register/own)\n\n* **Tools.** `defineTool({name, description, input?, output?, harness?, durable?, run})` — Valibot schemas, frozen at module load, importable from a light `@flue/runtime/tool` entry \"for tool-only modules\". Mounted per render via `useTool(def)`. `run` receives `{data, signal, log, toolCallId}`; `harness: true` adds `harness`, `durable: true` adds `step`. Reserved names: `task`, `activate_skill`, `read_skill_resource`, plus sandbox built-ins (`read/write/edit/bash/grep/glob`). **This is the primary registration surface an embedded kernel would own.**\n* **Harness tools** (worth calling out separately). `harness.sandbox` (direct file/exec verbs, *never recorded in the conversation*) and `harness.prompt(text, {result: Schema, tools, model, thinkingLevel, images})` — runs a model operation in a private scratch conversation invisible to clients, with Valibot-validated structured output enforced via a framework-injected `finish` tool. Repeated calls continue that scratch conversation. **This is the closest thing Flue has to a sub-LLM call primitive, and it is a strong fit for a kernel's internal extraction/normalization steps.** Also `harness.compact()`.\n* **Skills.** Open Agent Skills format (`SKILL.md` + supporting files), or `defineSkill({name, description, instructions, files})` for generated/assembled content. Progressive disclosure: one catalog line always present; full instructions arrive as an `activate_skill` tool *result*, so activation never mutates the system prompt and the cached prefix survives. Supporting files are served read-only from the app bundle at virtual paths — **not** copied into the sandbox. Also auto-discovered from `/.agents/skills/` when a sandbox exists.\n* **Subagents.** `defineSubagent({name, description, agent})` + `useSubagent()`. Model-driven via the always-present `task` tool. Child inherits *environment* (sandbox, workspace context, parent model) but **nothing conversational** — no history, instructions, tools, skills, persistent state, or initialData. Only the final message returns. Explicitly *not* a second addressable agent: \"no conversation id, no persistent state, and no address.\" `GeneralSubagent` ships as a blank delegate under `flue-general`.\n* **Persistent state.** `usePersistentState(name, initial)` — React-shaped, JSON-serializable, keyed by name, durable for the life of the conversation. Writes commit **atomically with the unit of work that made them** (tool batch, or event-hook seam checkpoint), which is what makes it the correct guard for at-least-once callbacks. Prefer updater functions; the render value is a snapshot.\n* **Sandboxes.** `useSandbox(factory, {cwd})`. `local()` from `@flue/runtime/node`, or adapters (Daytona, E2B, Modal, Cloudflare Sandbox/Computer) built against the Sandbox Adapter API. Attaching one adds the six built-in file/shell tools; an adapter may replace that set entirely. Sandbox filesystems are **ephemeral by default** and independent of conversation durability.\n* **Channels.** Inbound-only verified HTTP ingress (Slack, Discord, Teams, GitHub, Stripe, …), shipped as **blueprints** (`flue add channel slack`) that generate project source rather than as opaque packages. A channel is \"an object with declarative routes\"; `createChannelRouter(routes)` builds one by hand. Handlers call `dispatch(...)` themselves. **Outbound is explicitly not Flue's job** — no send-message abstraction; you use the provider SDK and expose narrow tools with the destination bound in trusted code.\n* **Event hooks / data writers.** `useAgentStart` (async — the load-data seam), `useAgentFinish`, `useResponseStart/Finish` (return values merge onto response *metadata*). `useDataWriter(name, {schema})` streams typed structured data parts to clients (`{type: 'data-orderCard', data}`), strictly one-way out of the agent — **the model never sees data parts**, and a write never re-renders the agent.\n* **Custom hooks.** Plain `use*` functions composing the built-ins, returning instruction fragments to the caller. Docs explicitly frame this as the reuse/packaging unit.\n\n## 3. Pi exposure\n\nPartial and deliberate, concentrated at the **model-provider layer**. Documented facts:\n\n* `@flue/runtime`'s npm dependencies include `@earendil-works/pi-agent-core` and `@earendil-works/pi-ai` directly.\n* Models guide: \"Providers are Pi's own objects, and Flue accepts them directly: build one with Pi's `createProvider()` … and hand it to `setProvider()` at module top level in `app.ts`.\" Examples import from `@earendil-works/pi-ai`, `.../api/anthropic-messages.lazy`, `.../providers/anthropic`, `.../api/openai-completions.lazy`.\n* `start({providers})` registers \"the Pi providers this runtime registers, replacing the default set.\"\n* `PromptImage` \"re-exports pi-ai's `ImageContent`.\"\n* Why-Flue: \"Flue builds on Pi, the open agent harness behind OpenClaw, and integrates it deeply into every agent you build.\"\n\nEverything else — conversation loop, tool dispatch, skills, subagents, durability — sits behind Flue's own abstractions. **Inference:** there is no documented path to Pi's agent loop, message list, or Pi extension/package system from inside a Flue agent. A kernel that currently assumes Pi-level tool registration, transcript control, or `renderResult`-style TUI hooks would have to be re-expressed entirely in Flue's `defineTool` + render model. Flue also uses Durable Streams (`@durable-streams/client`) as its client transport protocol.\n\n## 4. Local vs. remote parity\n\nDurable *records and recovery decisions are identical* across targets; ownership and wake mechanics differ.\n\n| | Node | Cloudflare |\n| -- | -- | -- |\n| Unit | one server process, coordinator with **lease-based** ownership | one **Durable Object per agent conversation**, own SQLite; ownership structural |\n| Recovery | startup reconciliation + periodic lease scans | wake-on-start + self-renewing durable wake schedule, bounded supervision pass |\n| Storage | `db.ts` adapter required (sqlite, Postgres, MySQL, Mongo, Redis, libSQL, Turso, Supabase, Valkey); **without one, conversations are process-local memory and a restart loses them** | built in |\n| Constraint | must route each conversation to exactly one owner; active-active / round-robin for the same conversation is unsafe | every deployed agent needs an append-only DO migration tag in your hand-authored `wrangler.jsonc`; renames/deletes are storage migrations (`renamed_classes` / `deleted_classes`) |\n\nCore contract: \"Every accepted submission reaches exactly one durable terminal outcome — completed, failed, or aborted — no matter how many crashes happen in between.\" Submissions are recorded durably *before* any model work; that is what the 202 and the `DispatchReceipt` attest to. There is a retry budget plus a wall-clock timeout, enforced preemptively via the attempt's abort signal.\n\n**Deliberately not durable:** sandbox files (rebuilt fresh on each initialization unless the adapter keys a provider workspace on the instance id — \"a durable database does not make a sandbox durable\"); in-flight local promises (persist the `DispatchReceipt` and `read(receipt)` re-attaches from any process); and **code outside the agent** — Flue explicitly does not checkpoint arbitrary TypeScript. For that, docs point to Cloudflare Workflows / Inngest / Temporal calling Flue \"like any other service.\" External side effects are never recorded, only that a tool ran and what it returned.\n\nAlso: there is **no GitHub Actions or GitLab *target***. CI is just `flue run` in a shell step (`--new` + deterministic `--id` gives exactly-once conversation creation). GHA/GitLab appear only in the ecosystem catalog. Builds are Vite (`vite build` → `dist/server.mjs` + `dist/app.mjs`, or the Cloudflare plugin's output). Node deps are externalized, not bundled; the built server does not load `.env`.\n\n## 5. Library-in-agent vs. agent-as-product\n\n**Embedding is a natural pattern, and the docs endorse it explicitly.** Evidence:\n\n* `defineTool` / `defineSkill` / `defineSubagent` all exist specifically as *exportable, frozen, module-load-validated* units — described as \"the natural shape for tools shared across agents\" and \"the exportable unit — define a delegate once, mount it from any agent.\"\n* **Custom hooks are the documented composition unit:** \"a `useGitHub()` hook that bundles the right tools, skills, and instructions can be written once and dropped into every agent that works with GitHub.\" That is precisely a kernel-in-a-hook.\n* `@flue/runtime/tool` is a lighter entry point for tool-only modules — a library can depend on Flue without pulling in the server runtime.\n* Source-dir resolution order puts `.flue/` **first**, described as \"a self-contained Flue source area inside a larger application,\" with authored modules free to \"import ordinary supporting code from elsewhere in the project.\"\n* Per-mount overrides \"spread cleanly\": `useSubagent({ ...issueClassifier, model: 'anthropic/claude-haiku-4-5' })`.\n\nCounter-pressure: Flue insists the *agent itself* be a program, not config, and the `'use agent'` build-time scan means a library **cannot ship a pre-registered agent** — the agent module must be authored in the consuming project.\n\n**Recommendation (inference):** ship the kernel as a published custom hook — `useElicitationKernel(targetPlugin)` — that internally calls `useTool` / `useSkill` / `usePersistentState` / `useDataWriter` and returns instruction fragments, plus raw `defineTool` exports for hosts that want selective mounting. The host owns a ~10-line `'use agent'` module, the `app.ts` mount, and `db.ts`. This is library-in-a-thin-agent, and Flue's grain supports it. One real constraint: tool names are globally unique per render and collide with reserved names, so the kernel needs a namespacing convention.\n\n## 6. Channels as host surfaces\n\nA \"host input pathway\" in Flue is: verified ingress → `dispatch(agent, {id, message, initialData})`. Three distinct payload lanes:\n\n* `initialData` — recorded once at conversation creation, validated against the agent's schema static, read with `useInitialData()`, immutable thereafter. This is where an elicitation *target descriptor* belongs.\n* `kind: 'signal'` **messages** — `{type, body, attributes}` where `attributes` is a string→string map of facts *trusted code* attached. Read with `useDelivery()`. Docs push this hard as the authorization pattern: \"the model may choose an order ID to look up, but it cannot choose the customer.\" For a kernel, this is the channel for host-verified respondent identity. Channel deliveries are signals rather than `user` messages precisely because a Slack thread is multi-participant.\n* `kind: 'user'` — direct human turns.\n\n**Interviewing-UX rendering surfaces, ranked by fit:**\n\n1. `useDataWriter` **+** `@flue/react`**.** Named, schema-validated structured data parts arriving alongside text on the same message; a tool can write several times mid-run to drive live progress. `useFlueAgent({url})` gives `messages`, `parts`, `status`, `historyReady`, `sendMessage()`, `refresh()`. Message parts are `text | reasoning | dynamic-tool | file`, and **validated structured tool output is preserved on the** `dynamic-tool` **part's** `output` — the React docs say this exists \"so applications can render custom tool interfaces without a separate data-event channel.\" Direct fit for question cards, choice sets, and review panes.\n2. **Chat channels** (Slack/Teams/Discord/GitHub) for text-shaped interviewing only. There is **no outbound abstraction** — every reply is a tool you write against the provider SDK with the destination bound in trusted code (the `replyInThread(data)` pattern). Rich Slack Block Kit interviewing is entirely your code.\n\n**Gap worth flagging loudly: Flue documents no first-class human-in-the-loop / elicitation / interrupt primitive.** There is no \"ask the user and suspend\" hook. `terminate: true` on a tool result ends the turn once the current batch settles; the documented pattern for waiting on a human is state-gated tools (`record_approval` unlocks `publish_release`) plus a new inbound submission. **Inference:** the kernel must implement its own turn-suspension protocol — a `terminate: true` tool that writes the pending question into `usePersistentState` and emits a data part, with the host's answer arriving as a fresh `dispatch`. That is exactly the kind of thing a kernel *should* own, but Flue provides no scaffolding for it, so it is net-new work either way.\n\n## 7. Constraints & risks\n\n* **Maturity.** 2.0.3, with 2.0 a full API rewrite around hooks announced this cycle; a Migration Guide exists. `@flue/vite` first published 2026-07-10 (19 versions). Fast-moving — expect churn.\n* **Build-time magic is load-bearing.** `'use agent'` scanning, `SKILL.md` module imports, and agent-identity stamping all happen in the Vite plugin. **This effectively requires Vite.** A kernel published as a library must not depend on that transform internally — keep to `defineTool`/`defineSkill` (plain runtime calls) and let the host own the scanned module.\n* **Durable-identity coupling.** Conversation storage is keyed by agent function name. Always pin `agentName`.\n* **Cloudflare migration ceremony.** Adding an agent is always agent + mount + new DO migration tag. If the design ever wants *dynamic* agent creation per elicitation target, Cloudflare forbids it — agents are a build-time set. Use one agent + many conversation ids instead.\n* **Prompt-cache economics constrain dynamism.** \"Gate tools on state that changes rarely.\" An interviewing kernel that swaps tools per question would thrash the cache. Prefer one stable tool set + state-driven instructions, or skill activation (cache-safe by design).\n* **Valibot lock-in** at every schema boundary (`input`, `output`, `initialData`, `useDataWriter`, `harness.prompt({result})`). A kernel with Zod-based plugin contracts needs a conversion layer or a dual-schema strategy.\n* **Node multi-replica.** No active-active per conversation; you must own routing.\n* **Subagents are conversationally sterile** — no id, no state, no address. If elicitation-target plugins need their own durable memory, they cannot be subagents; they must be state + tools inside the parent, or separately registered agents.\n* **Petrinaut / non-React web-UI host risk:** the rich path (`useFlueAgent`) is React-specific. `@flue/sdk` (`createFlueClient` → `send/read/wait/observe/history`, built on `@durable-streams/client`) is framework-neutral and is what `@flue/react` sits on, so a non-React host is viable — but it must reimplement the materialized-snapshot / reconnect / canonical-reset layer that `useFlueAgent` provides. **Inference:** budget for that, or wrap `createFlueClient` directly and accept a thinner UI contract.\n* `skills` **frontmatter** `allowed-tools` **is accepted but not enforced** — Flue does not restrict the session toolset from a skill. Any kernel-level tool gating must be done with conditional `useTool`.\n\n## 8. Unreached sources\n\n**HTTP 404 (do not exist at those paths):** `/docs/guide/deployment/`, `/docs/guide/state/`, `/docs/guide/cloudflare/`, `/docs/guide/cli/`, `/docs/guide/github-actions-target/`, `/docs/reference/index.md`, `/llms.txt`. (Real equivalents: `/docs/guide/deploy/`, state is a section of `agent-hooks`, `/docs/guide/cloudflare-target/`.)\n\n**Exist but not fetched** (nav-confirmed; would sharpen specific answers): `/docs/guide/database/`, `/docs/guide/schedules/`, `/docs/guide/evals/`, `/docs/guide/observability/`, `/docs/guide/configuration/`, `/docs/guide/migration/`, `/docs/guide/agent-behavior/`, `/docs/reference/sandbox-api/`, `/docs/reference/provider-api/`, the full `/docs/reference/agent-hooks-api/` (indexed, read only via search), the CLI and Agent SDK reference sections, all `/docs/ecosystem/*` sub-pages, the `@flue/react` package README, and `examples/react-chat`. The GitHub repo (`withastro/flue`) source and Pi's own docs (`pi.dev/docs/latest`) were not read.\n+++", - "proposedBodySha256": "7c869076eb14b8d3bef3a1e446c269ec8bd8f616eb682b19598888a22546f300", - "ambiguity": null, - "notes": null - }, - { - "id": "d3e3d8b2-170a-4fa9-8ea8-26d396caa0b6", - "identifier": "FE-1368", - "url": "https://linear.app/hash/issue/FE-1368/zil-lean-survey-archive", - "state": { - "name": "Done", - "type": "completed" - }, - "parent": { - "id": "a5053b51-937d-453f-9c4e-fe8e49e912ad", - "identifier": "FE-1366", - "title": "Spec the elicitation harness architecture (archived wayfinder map)" - }, - "sourceUpdatedAt": "2026-08-19T16:20:27.450Z", - "sourceTitle": "zil-lean survey [archive]", - "sourceDescriptionSha256": "a34174377a2da0548781191d7cbb33e606fb329065a5f14afe4d22454917596f", - "sourceTitleSha256": "aa2bdea3725452e09651b2a77e90deef72ab518995b82968d256935035bf1dad", - "oldOuter": "Examined zil-lean, a prototype claim-graph system, as a candidate second practice subject for the interviewing architecture. Conclusion: it contains no interviewing itself, but it is a working proof of the claim-graph idea; a smaller \"assurance argument\" slice was chosen as the second subject instead of the full system. Resolved 2026-08-06; part of the completed elicitation-harness planning (see parent map).", - "proposedTitle": "Assess zil-lean as an elicitation subject", - "proposedOuter": "This research assessed zil-lean, a prototype claim-graph system, as the second subject for testing the interviewing architecture. zil-lean does not include interviewing, but it demonstrates the claim-graph model. The plan therefore chose a smaller assurance-argument subject instead of the full system and resolved the research on 2026-08-06.", - "extractionMethod": "first-standalone-divider", - "innerRecord": "\n> **Archive migration (2026-08-11).** Resolved ticket from the local-markdown wayfinder effort `elicitation-kernel` (`brunch-lite:.scratch/elicitation-kernel/issues/02-zil-lean-survey.md`), mirrored to Linear for team visibility. Full original content below, including the resolution.\n\n---\n\n# zil-lean survey\n\nType: research\nStatus: resolved\nResolved: 2026-08-06\n\n> **Rename note (2026-08-10, spec assembly):** occurrences of `elicit-proof-obligations` below are the historical name; the second target is the **assurance argument**, package `plugin-assurance` (per the [Formal-verification canon survey](<09-formal-verification-canon-survey.md>)'s category-error verdict). This ticket's ElicitationPack sketch is superseded by ticket 09's `Statement` contract (pre-pass S10); the surviving contributions are the existence proof, the assurance lattice with prohibited promotions, derivation provenance, and the derived-status idea.\n\n## Question\n\nWhat elicitation-for-formal-verification insights does [https://github.com/jagg-ix/zil-lean]() hold, and is a lean/formal-flavored elicitation target *dev-sized* — small enough to develop the kernel against without wading into a massively complex target?\n\nSpecifically:\n\n* What is the repo: purpose, structure, how it relates natural-language intent to Lean formalization\n* What would an **ElicitationPack** for a lean-flavored target need: concept contract (what counts as a proposition/invariant/assumption), observation lenses, completion criteria\n* What would its **ProjectionPack** need: output contract/shape, validators (does Lean itself act as the deterministic validator?), lossiness\n* Verdict: dev-sized or too heavy? If too heavy, what is the smallest formal-flavored slice that still exercises both pack axes differently from elicit-gherkin? (Fallback second target is BPMN/process-mining.)\n\n## Answer\n\n> Resolved by `/research` subagent, 2026-08-06. Local clone for follow-up: scratchpad `zil-lean/` (session-temporary).\n\n# zil-lean survey\n\n## 1. What it is\n\n**Documented facts.**\n\n* `jagg-ix/zil-lean` — \"ZIL: a relational knowledge language implemented in Lean 4 with a Clojure runtime and toolchain.\" Primary language Clojure; no license file; 31 stars, 1 fork, 0 subscribers, 1 open issue, no topics.\n* **Provenance/activity**: repo created 2026-07-27T06:10, last push 2026-07-29T02:26. Entire history is **27 commits over \\~2 days**. First commit is \"Initial public snapshot\" by `github-actions[bot]`; all six PRs are branches named `agent/modularize-*`, `agent/complete-engine-governance-modules`. There is also a `PUBLIC-CONTENT-POLICY.md` and commits titled \"Isolate validated source publication.\" **Inference (high confidence)**: this is an agent-authored code dump published from a private working repo — ~140k tracked lines landed in one snapshot, then mechanically split into modules. Not an organically evolved project, and not battle-tested.\n* **Size/structure**: 532 files. `Zil/` (106 files, 14.2k lines Lean 4 — the native library), `src/zil/` (75 files, 17.5k lines Clojure — runtime/CLI/bridges), `spec/` (44 markdown specs, 5.3k lines), `examples/` (133 files, 85k lines — dominated by generated data), `lib/` + `libsets/` (`.zc` macro libraries), `test/` (57 files, 6.1k). Lean pinned to `leanprover/lean4:v4.31.0`.\n* **What \"ZIL\" is**: a **relation-tuple + Horn-rule (Datalog) knowledge language**, explicitly modeled on Google's Zanzibar `object#relation@user` tuple syntax, extended from authorization into general project knowledge: `declaration ─implements→ requirement`, `theorem ─validates→ component`, `claim ─supportedBy→ document`. Four primitives: nodes, relations, rules, queries. Two surface syntaxes (`.zc` tuple text; native Lean macros `zil_fact` / `zil_theorem_rule`) plus a canonical IR, snapshot format `ZILX/1`, delta format `ZILD/1`, revision log `ZILR/1`, and exporters to Soufflé and Prolog.\n* **Relation to Lean**: Lean is (a) the implementation host of the engine and (b) a **certification backend for a narrow slice**. `Zil.Trust` has three levels — `asserted` (registered fact), `graphDerived` (rule-inferred), `certified` (a rule paired with a Lean proposition + proof term, kernel-checked). Everything else about proofs is *bookkeeping*: `spec/proof-obligation-governance-v1.md` governs declared obligations across `z3 | tlaps | lean4 | acl2 | manual` and validates *presence of evidence references*, not their content (\"Lean kernel validation remain[s] the responsibility of [its] producing system\").\n\n**It is not**: a benchmark, a proof-automation system, an autoformalization pipeline, or anything with an LLM in it. `grep -ril 'openai|anthropic|llm|prompt|natural.language|gpt-'` across the repo hits **two files, both false positives** (a Terraform API schema, a config macro lib). \"assistant\" appears only as node names in examples (`assistant.formalization`, `assistant.codegen`) — agents are *modeled as nodes in the graph*, never as interviewers. **There is no elicitation, no question-asking, and no natural-language front-end anywhere in this repo.**\n\n## 2. Elicitation-relevant insights\n\nThe bookmark intuition is wrong about the surface but partly right about the substrate. zil-lean contains **no informal→formal capture**, but it is an unusually well-worked-out example of **the layer the kernel calls the claim graph** — and it is worth reading precisely for that.\n\nTransferable, documented:\n\n* **Evidence-graded claim graph as a first-class artifact.** `spec/assurance-levels-v1.md` defines five orthogonal labels — `exploratory`, `validated`, `kernel-backed`, `externally-attested`, `byte-attested` — with an explicit **prohibited-promotion lattice** (`validated ↛ kernel-backed`, `externally-attested ↛ kernel-backed`, `byte-attested ↛ validated`). Crucially: \"Assurance labels state what checked a result… must not be inferred from a successful exit code alone.\" Directly reusable as the kernel's IR annotation on captured claims, and the sharpest thing in the repo.\n* **Full derivation provenance.** `spec/derivation-provenance-v1.md`: every fact node carries `id / fact / origin / stratum`, where origin is `base` or `rule(ruleName, premiseFactIds, negativeChecks, binding)`. Query answers emit witnesses with premise fact IDs and bindings. Reports are deterministic and **timestamp-free** so they hash stably. This is the evidence-preserving IR the kernel wants, worked out concretely — including the honest caveat: \"The trace is graph-derived evidence. It is not a Lean kernel proof term.\"\n* **A concept vocabulary for formal-flavored capture that already distinguishes the kernel's hard cases.** `examples/formalization-arc-demo.zc` + `lib/theorem-dsl-macros.zc` separate **assumption** (with class + `ASSUME_HOLDS(source)` / `ASSUME_BROKEN(source, reason)`), **lemma**, **theorem**, **guarantee** (`THEOREM_ENSURES`), **evidence** (engine + token), **component**, **signal**, and **incident** (which can *break* an assumption and propagate). Derived statuses: `PROVED` (deps satisfied + witness), `CONDITIONAL` (witness but assumption under review), `WEAK` (no witness), `BROKEN` (required assumption broken). That is a completion-criteria ladder, expressed as Datalog, that the kernel could adopt nearly verbatim.\n* **Two forms of \"enough.\"** `spec/formalization-plan-v1.md` schedules `FORMALIZATION_TARGET` declarations (`module, file, declaration, status, priority, dependencies?`) over a validated acyclic dependency graph, defining *readiness* as `status ∈ {ready, in_progress} ∧ ∀dep. dep.status ∈ {verified, reviewed, proved}`, with structured blocking reasons (`status:`, `missing:`, `dependency::`). `spec/agent-context-v1.md` defines a **context bundle** for handing a task to an agent — changed nodes → reverse impact → relevant facts → originating rules → auto-selected queries and targets — with explicit incompleteness issues (`unknown-changed-node`, `missing-query`, `missing-formalization-target`) and `context_bundle_id = sha256(report bytes)`.\n* **Drift detection on formal statements.** `spec/theorem-statement-locks-v1.md` locks `(token_id, declaration, module, kind, type_fingerprint)` and reports ordered check states (`fingerprint_changed`, `declaration_changed`, `missing_token`, `current_unresolved`, `unexpected_token`…). Answers \"did the formalization silently stop meaning what the user said?\" — a re-elicitation trigger.\n* **A request-elicitation schema that already exists.** `spec/request-form-core-v0.1.md` (draft) formalizes \"a requester asks for ``\" as `Data | Action | Compound | Recursive`, with `mode ∈ {dry_run, apply}`, explicit `Effect` entities, and `Criterion` acceptance predicates, lowered to canonical tuples with derived judgments (`has_side_effect`, `is_recursive`, `execution_contract plan_only`). **Inference**: this is the closest thing in the repo to an elicitation target schema, and it is *not* Lean-flavored at all — it is intent-flavored. It may be more useful to the kernel than the theorem DSL.\n\nWhat zil-lean does **not** show, and the kernel must supply: how anyone *arrives* at these declarations. Every `.zc` file is hand-authored. There is no question generation, no ambiguity detection, no candidate extraction from prose, no clarification loop. The Lean checker is used as an oracle over *already-formal* artifacts, never as a feedback signal into a conversation.\n\n## 3. ElicitationPack sketch (elicit-lean/formal)\n\nGrounded in the vocabulary zil-lean shows is actually load-bearing.\n\n**Concept contract** — five kinds, distinguished by their *evidential obligations*, not their grammar:\n\n| Kind | Test | Obligation on capture |\n| -- | -- | -- |\n| `assumption` | Taken as given; not to be discharged here | must name a holder/source and a review status |\n| `invariant` | Must hold at all times over a named state/scope | must name scope + the state it constrains |\n| `proposition/theorem` | A claim asserted to follow from others | must name required assumptions + lemmas |\n| `guarantee` (ensures) | An outcome promised to a consumer | must attach to a producing component |\n| `constraint` | A restriction on inputs/config, not a claim about behavior | must name what it restricts |\n\nNon-negotiable per-item fields (from `THM_*` + proof-obligation governance): `id`, `criticality`, `depends_on: [assumption|lemma]`, `evidence?: (engine, token)`, `status`, and — added by the kernel, absent in zil-lean — `source_span` (the utterance it came from) and `paraphrase_confirmed: bool`.\n\n**Observation lenses** (what to notice in conversation):\n\n1. **Modal/quantifier lens** — \"always\", \"never\", \"must\", \"for every\", \"at most one\" → invariant candidate.\n2. **Hedge lens** — \"assuming\", \"as long as\", \"we can take for granted\", \"in practice X is well-formed\" → assumption candidate, and a *required* follow-up on who guarantees it. `a_input_well_formed` in the demo is exactly this shape.\n3. **Consequence lens** — \"so then\", \"which means\", \"that guarantees\" → proposition with an implicit dependency edge to name.\n4. **Break lens** — \"except when\", \"unless\", \"this fell over once when…\" → either a missing precondition on an existing item, or an incident that breaks an assumption (`INCIDENT_BREAK_ASSUMPTION`).\n5. **Undefined-term lens** — a noun used in a claim that has no node yet → must be introduced before the claim can be normalized.\n\n**Completion criteria** (ladder taken from the demo's status computation, plus dependency-closure from the plan spec):\n\n* *Structurally complete*: every claim's `depends_on` targets exist; dependency graph acyclic; every term referenced has a node. (Directly = the plan spec's set-validation rules.)\n* *Epistemically complete*: no claim is `WEAK` without the user having explicitly deferred it; every `CONDITIONAL` names the assumption under review and its owner; every `critical` item has either evidence or an explicit, reasoned waiver (governance spec forbids waiving critical items).\n* *Faithfulness*: every captured item has a user-confirmed paraphrase. This is the one criterion zil-lean cannot inform — there is no user in it.\n\n## 4. ProjectionPack sketch\n\n**Output contract**: the claim graph projects to a `.zc`-shaped tuple set plus derived-status queries — the `formalization-arc-demo.zc` shape is a working, concrete target. Textual, diffable, deterministic, machine-checkable *without Lean*.\n\n**Deterministic validators, in ascending cost** — and this is the important structural finding: **zil-lean demonstrates that Lean is the wrong first validator.** Three cheaper tiers exist and catch most errors:\n\n1. **Schema/graph validation** (free): unique IDs, nonnegative priorities, existing dependency targets, acyclicity, stratification safety, relation declared in some base fact or rule head (`unknown-relation` verdict).\n2. **Datalog closure** (cheap, deterministic, bounded — default fuel 64/stratum): derives `PROVED / CONDITIONAL / WEAK / BROKEN`, impact sets, break roots. The real workhorse oracle, and it runs on the *informal* graph.\n3. **Obligation governance** (cheap): checks that declared statuses are supported by evidence references — `proved-status-requires-evidence`, `obligation-not-discharged`, `waiver-reason-missing`, `critical-obligation-cannot-be-waived`.\n4. **Lean elaboration** (expensive, narrow): only for items actually written as Lean declarations. `spec/lean-verification-report-v1.md` runs `lake env lean ` per module + SHA-256 manifest match; `Zil.Trust.CertifiedRule` kernel-checks a proposition/proof pair.\n\nSo: **Lean serves as the deterministic validator only for the** `kernel-backed` **tier, and zil-lean's own architecture says you must not let success at tiers 1–3 masquerade as tier 4.** The prohibited-promotion table is the lossiness policy, pre-written.\n\n**What's lossy**: the natural-language statement itself (`statement` is a free-text field in the obligation schema, unvalidated); the *reason* an assumption is believed (only `source` is kept); alternative derivations (v1 provenance retains only the first witness); and — the deep one — the gap between \"Lean accepted this declaration\" and \"this declaration means what the user said.\" `theorem-statement-locks` spells it out: \"The lock does not claim that unchanged type fingerprints imply unchanged proof terms, source text, or external scientific meaning.\" The kernel must keep the utterance→claim→declaration chain because nothing downstream can reconstruct it.\n\n## 5. VERDICT — dev-sized?\n\n**A full elicit-lean/formal target is not dev-sized. A slice of it is, and it's a good one — but it is not the slice with Lean in it.**\n\nReasoning:\n\n* **The heavy part is real.** Getting from a user's claim to a Lean *statement* (not proof) requires committing to a type-theoretic encoding of the domain — the step autoformalization research finds hardest. That is deep-Lean-expertise work, and the contract is not definable without it. zil-lean quietly concedes this: it never writes theorem statements from intent. It stores `proof:Normalize.idempotent` as an opaque **token naming a declaration a human already wrote**. The demo file says so outright: \"Proof status here is bookkeeping only. The proof assistant remains the sole proof authority; proof tokens name checked declarations but do not assert them.\"\n* **The light part is genuinely there, and zil-lean is a working existence proof of it.** Eliciting an **assumption/lemma/theorem dependency graph with criticality and evidence pointers** — no Lean statements, no proofs — is weeks-scale. Every validator you need is graph-level and already specified in this repo.\n\n**Smallest formal-flavored slice: \"elicit-proof-obligations\" / the verification arc.** Interview a user about a system they believe is correct; capture assumptions (with owners and review status), lemmas, theorems, guarantees, and the dependency edges among them; attach evidence references where they exist; project to a `.zc`-style graph; validate by acyclicity + stratified Datalog closure yielding `PROVED/CONDITIONAL/WEAK/BROKEN` + break-root and impact queries.\n\n**Does it differ from elicit-gherkin on both pack axes? Yes, cleanly:**\n\n* *ElicitationPack*: Gherkin's concept contract is **scenario-shaped and example-driven** (Given/When/Then, concrete instances, no cross-item structure); completion is per-scenario coverage. This target's contract is **claim-shaped and dependency-structured** — the unit is a proposition with edges to other propositions, the hedge lens is central (Gherkin has no notion of an assumption), and completion is *graph closure plus evidence adequacy*, not enumeration. Different lenses, genuinely different \"enough.\"\n* *ProjectionPack*: Gherkin projects to a flat, independently-executable list; validation is parse + step-binding. This projects to a **DAG with derived statuses**, validated by fixpoint computation and an evidence-promotion lattice. The lossiness policy is substantive (assurance levels, first-witness-only) rather than near-absent.\n\n**Fallback comparison.** Take this slice **over** elicit-BPMN/process-mining as second target. Both differ from Gherkin, but the proof-obligation slice stresses the kernel harder on the axes the design cares about: it forces the claim-graph IR to carry *evidence grades and derivation provenance* (BPMN mostly forces sequencing and gateway structure, which Gherkin partly covers), and it gives you a deterministic non-trivial validator — a Datalog fixpoint — without any external tooling or domain SME. Keep BPMN as third; it's the better *breadth* target once the IR is stable, and it has an easier user-recruitment story. **Inference**, based on pack-axis distance, not on any BPMN sources reviewed here.\n\nOne caveat worth carrying: nothing in this repo has been validated by use. Treat the specs as well-reasoned design documents by an agent-assisted author, not as field-tested contracts — the `assurance-levels` lattice and the `THM_*` status ladder are worth stealing on their merits, not on their track record.\n\n## 6. Unreached sources\n\n* Did not build or run anything (`lake build`, `lake exe zilLeanTests`, `clojure -M:test`) — no Lean toolchain here; **all correctness claims are from specs and source, not execution.**\n* Did not read: the 129 Lean files in `Zil/` beyond filenames; `spec/zil-formal-core-v0.1.md` (427 lines, the core semantics); `spec/canonical-relational-ir-v0.1.md`; `spec/evidence-envelope-v1.md`; `spec/query-governance-v1.md`; `spec/recovery-audit-v1.md`; `spec/dmetavm-core-v0.1.md`; `formal/` (TLA+/SMT); the 17 `libsets/` domain packs; the 133 example files beyond three.\n* Repo wiki is enabled (`has_wiki: true`) — not checked. 1 open issue — not read. No discussions, no releases checked.\n* Author `jagg-ix` (Jorge A. Garcia) — no external profile or publication search performed, so no independent read on provenance beyond commit metadata.", - "innerSha256": "ae5914e467433c3a0002f56350c81322249cc6eb04ab971cf4371f994ab35d26", - "proposedBody": "This research assessed zil-lean, a prototype claim-graph system, as the second subject for testing the interviewing architecture. zil-lean does not include interviewing, but it demonstrates the claim-graph model. The plan therefore chose a smaller assurance-argument subject instead of the full system and resolved the research on 2026-08-06.\n\n+++🏗️ Agent notes\n\n> **Archive migration (2026-08-11).** Resolved ticket from the local-markdown wayfinder effort `elicitation-kernel` (`brunch-lite:.scratch/elicitation-kernel/issues/02-zil-lean-survey.md`), mirrored to Linear for team visibility. Full original content below, including the resolution.\n\n---\n\n# zil-lean survey\n\nType: research\nStatus: resolved\nResolved: 2026-08-06\n\n> **Rename note (2026-08-10, spec assembly):** occurrences of `elicit-proof-obligations` below are the historical name; the second target is the **assurance argument**, package `plugin-assurance` (per the [Formal-verification canon survey](<09-formal-verification-canon-survey.md>)'s category-error verdict). This ticket's ElicitationPack sketch is superseded by ticket 09's `Statement` contract (pre-pass S10); the surviving contributions are the existence proof, the assurance lattice with prohibited promotions, derivation provenance, and the derived-status idea.\n\n## Question\n\nWhat elicitation-for-formal-verification insights does [https://github.com/jagg-ix/zil-lean]() hold, and is a lean/formal-flavored elicitation target *dev-sized* — small enough to develop the kernel against without wading into a massively complex target?\n\nSpecifically:\n\n* What is the repo: purpose, structure, how it relates natural-language intent to Lean formalization\n* What would an **ElicitationPack** for a lean-flavored target need: concept contract (what counts as a proposition/invariant/assumption), observation lenses, completion criteria\n* What would its **ProjectionPack** need: output contract/shape, validators (does Lean itself act as the deterministic validator?), lossiness\n* Verdict: dev-sized or too heavy? If too heavy, what is the smallest formal-flavored slice that still exercises both pack axes differently from elicit-gherkin? (Fallback second target is BPMN/process-mining.)\n\n## Answer\n\n> Resolved by `/research` subagent, 2026-08-06. Local clone for follow-up: scratchpad `zil-lean/` (session-temporary).\n\n# zil-lean survey\n\n## 1. What it is\n\n**Documented facts.**\n\n* `jagg-ix/zil-lean` — \"ZIL: a relational knowledge language implemented in Lean 4 with a Clojure runtime and toolchain.\" Primary language Clojure; no license file; 31 stars, 1 fork, 0 subscribers, 1 open issue, no topics.\n* **Provenance/activity**: repo created 2026-07-27T06:10, last push 2026-07-29T02:26. Entire history is **27 commits over \\~2 days**. First commit is \"Initial public snapshot\" by `github-actions[bot]`; all six PRs are branches named `agent/modularize-*`, `agent/complete-engine-governance-modules`. There is also a `PUBLIC-CONTENT-POLICY.md` and commits titled \"Isolate validated source publication.\" **Inference (high confidence)**: this is an agent-authored code dump published from a private working repo — ~140k tracked lines landed in one snapshot, then mechanically split into modules. Not an organically evolved project, and not battle-tested.\n* **Size/structure**: 532 files. `Zil/` (106 files, 14.2k lines Lean 4 — the native library), `src/zil/` (75 files, 17.5k lines Clojure — runtime/CLI/bridges), `spec/` (44 markdown specs, 5.3k lines), `examples/` (133 files, 85k lines — dominated by generated data), `lib/` + `libsets/` (`.zc` macro libraries), `test/` (57 files, 6.1k). Lean pinned to `leanprover/lean4:v4.31.0`.\n* **What \"ZIL\" is**: a **relation-tuple + Horn-rule (Datalog) knowledge language**, explicitly modeled on Google's Zanzibar `object#relation@user` tuple syntax, extended from authorization into general project knowledge: `declaration ─implements→ requirement`, `theorem ─validates→ component`, `claim ─supportedBy→ document`. Four primitives: nodes, relations, rules, queries. Two surface syntaxes (`.zc` tuple text; native Lean macros `zil_fact` / `zil_theorem_rule`) plus a canonical IR, snapshot format `ZILX/1`, delta format `ZILD/1`, revision log `ZILR/1`, and exporters to Soufflé and Prolog.\n* **Relation to Lean**: Lean is (a) the implementation host of the engine and (b) a **certification backend for a narrow slice**. `Zil.Trust` has three levels — `asserted` (registered fact), `graphDerived` (rule-inferred), `certified` (a rule paired with a Lean proposition + proof term, kernel-checked). Everything else about proofs is *bookkeeping*: `spec/proof-obligation-governance-v1.md` governs declared obligations across `z3 | tlaps | lean4 | acl2 | manual` and validates *presence of evidence references*, not their content (\"Lean kernel validation remain[s] the responsibility of [its] producing system\").\n\n**It is not**: a benchmark, a proof-automation system, an autoformalization pipeline, or anything with an LLM in it. `grep -ril 'openai|anthropic|llm|prompt|natural.language|gpt-'` across the repo hits **two files, both false positives** (a Terraform API schema, a config macro lib). \"assistant\" appears only as node names in examples (`assistant.formalization`, `assistant.codegen`) — agents are *modeled as nodes in the graph*, never as interviewers. **There is no elicitation, no question-asking, and no natural-language front-end anywhere in this repo.**\n\n## 2. Elicitation-relevant insights\n\nThe bookmark intuition is wrong about the surface but partly right about the substrate. zil-lean contains **no informal→formal capture**, but it is an unusually well-worked-out example of **the layer the kernel calls the claim graph** — and it is worth reading precisely for that.\n\nTransferable, documented:\n\n* **Evidence-graded claim graph as a first-class artifact.** `spec/assurance-levels-v1.md` defines five orthogonal labels — `exploratory`, `validated`, `kernel-backed`, `externally-attested`, `byte-attested` — with an explicit **prohibited-promotion lattice** (`validated ↛ kernel-backed`, `externally-attested ↛ kernel-backed`, `byte-attested ↛ validated`). Crucially: \"Assurance labels state what checked a result… must not be inferred from a successful exit code alone.\" Directly reusable as the kernel's IR annotation on captured claims, and the sharpest thing in the repo.\n* **Full derivation provenance.** `spec/derivation-provenance-v1.md`: every fact node carries `id / fact / origin / stratum`, where origin is `base` or `rule(ruleName, premiseFactIds, negativeChecks, binding)`. Query answers emit witnesses with premise fact IDs and bindings. Reports are deterministic and **timestamp-free** so they hash stably. This is the evidence-preserving IR the kernel wants, worked out concretely — including the honest caveat: \"The trace is graph-derived evidence. It is not a Lean kernel proof term.\"\n* **A concept vocabulary for formal-flavored capture that already distinguishes the kernel's hard cases.** `examples/formalization-arc-demo.zc` + `lib/theorem-dsl-macros.zc` separate **assumption** (with class + `ASSUME_HOLDS(source)` / `ASSUME_BROKEN(source, reason)`), **lemma**, **theorem**, **guarantee** (`THEOREM_ENSURES`), **evidence** (engine + token), **component**, **signal**, and **incident** (which can *break* an assumption and propagate). Derived statuses: `PROVED` (deps satisfied + witness), `CONDITIONAL` (witness but assumption under review), `WEAK` (no witness), `BROKEN` (required assumption broken). That is a completion-criteria ladder, expressed as Datalog, that the kernel could adopt nearly verbatim.\n* **Two forms of \"enough.\"** `spec/formalization-plan-v1.md` schedules `FORMALIZATION_TARGET` declarations (`module, file, declaration, status, priority, dependencies?`) over a validated acyclic dependency graph, defining *readiness* as `status ∈ {ready, in_progress} ∧ ∀dep. dep.status ∈ {verified, reviewed, proved}`, with structured blocking reasons (`status:`, `missing:`, `dependency::`). `spec/agent-context-v1.md` defines a **context bundle** for handing a task to an agent — changed nodes → reverse impact → relevant facts → originating rules → auto-selected queries and targets — with explicit incompleteness issues (`unknown-changed-node`, `missing-query`, `missing-formalization-target`) and `context_bundle_id = sha256(report bytes)`.\n* **Drift detection on formal statements.** `spec/theorem-statement-locks-v1.md` locks `(token_id, declaration, module, kind, type_fingerprint)` and reports ordered check states (`fingerprint_changed`, `declaration_changed`, `missing_token`, `current_unresolved`, `unexpected_token`…). Answers \"did the formalization silently stop meaning what the user said?\" — a re-elicitation trigger.\n* **A request-elicitation schema that already exists.** `spec/request-form-core-v0.1.md` (draft) formalizes \"a requester asks for ``\" as `Data | Action | Compound | Recursive`, with `mode ∈ {dry_run, apply}`, explicit `Effect` entities, and `Criterion` acceptance predicates, lowered to canonical tuples with derived judgments (`has_side_effect`, `is_recursive`, `execution_contract plan_only`). **Inference**: this is the closest thing in the repo to an elicitation target schema, and it is *not* Lean-flavored at all — it is intent-flavored. It may be more useful to the kernel than the theorem DSL.\n\nWhat zil-lean does **not** show, and the kernel must supply: how anyone *arrives* at these declarations. Every `.zc` file is hand-authored. There is no question generation, no ambiguity detection, no candidate extraction from prose, no clarification loop. The Lean checker is used as an oracle over *already-formal* artifacts, never as a feedback signal into a conversation.\n\n## 3. ElicitationPack sketch (elicit-lean/formal)\n\nGrounded in the vocabulary zil-lean shows is actually load-bearing.\n\n**Concept contract** — five kinds, distinguished by their *evidential obligations*, not their grammar:\n\n| Kind | Test | Obligation on capture |\n| -- | -- | -- |\n| `assumption` | Taken as given; not to be discharged here | must name a holder/source and a review status |\n| `invariant` | Must hold at all times over a named state/scope | must name scope + the state it constrains |\n| `proposition/theorem` | A claim asserted to follow from others | must name required assumptions + lemmas |\n| `guarantee` (ensures) | An outcome promised to a consumer | must attach to a producing component |\n| `constraint` | A restriction on inputs/config, not a claim about behavior | must name what it restricts |\n\nNon-negotiable per-item fields (from `THM_*` + proof-obligation governance): `id`, `criticality`, `depends_on: [assumption|lemma]`, `evidence?: (engine, token)`, `status`, and — added by the kernel, absent in zil-lean — `source_span` (the utterance it came from) and `paraphrase_confirmed: bool`.\n\n**Observation lenses** (what to notice in conversation):\n\n1. **Modal/quantifier lens** — \"always\", \"never\", \"must\", \"for every\", \"at most one\" → invariant candidate.\n2. **Hedge lens** — \"assuming\", \"as long as\", \"we can take for granted\", \"in practice X is well-formed\" → assumption candidate, and a *required* follow-up on who guarantees it. `a_input_well_formed` in the demo is exactly this shape.\n3. **Consequence lens** — \"so then\", \"which means\", \"that guarantees\" → proposition with an implicit dependency edge to name.\n4. **Break lens** — \"except when\", \"unless\", \"this fell over once when…\" → either a missing precondition on an existing item, or an incident that breaks an assumption (`INCIDENT_BREAK_ASSUMPTION`).\n5. **Undefined-term lens** — a noun used in a claim that has no node yet → must be introduced before the claim can be normalized.\n\n**Completion criteria** (ladder taken from the demo's status computation, plus dependency-closure from the plan spec):\n\n* *Structurally complete*: every claim's `depends_on` targets exist; dependency graph acyclic; every term referenced has a node. (Directly = the plan spec's set-validation rules.)\n* *Epistemically complete*: no claim is `WEAK` without the user having explicitly deferred it; every `CONDITIONAL` names the assumption under review and its owner; every `critical` item has either evidence or an explicit, reasoned waiver (governance spec forbids waiving critical items).\n* *Faithfulness*: every captured item has a user-confirmed paraphrase. This is the one criterion zil-lean cannot inform — there is no user in it.\n\n## 4. ProjectionPack sketch\n\n**Output contract**: the claim graph projects to a `.zc`-shaped tuple set plus derived-status queries — the `formalization-arc-demo.zc` shape is a working, concrete target. Textual, diffable, deterministic, machine-checkable *without Lean*.\n\n**Deterministic validators, in ascending cost** — and this is the important structural finding: **zil-lean demonstrates that Lean is the wrong first validator.** Three cheaper tiers exist and catch most errors:\n\n1. **Schema/graph validation** (free): unique IDs, nonnegative priorities, existing dependency targets, acyclicity, stratification safety, relation declared in some base fact or rule head (`unknown-relation` verdict).\n2. **Datalog closure** (cheap, deterministic, bounded — default fuel 64/stratum): derives `PROVED / CONDITIONAL / WEAK / BROKEN`, impact sets, break roots. The real workhorse oracle, and it runs on the *informal* graph.\n3. **Obligation governance** (cheap): checks that declared statuses are supported by evidence references — `proved-status-requires-evidence`, `obligation-not-discharged`, `waiver-reason-missing`, `critical-obligation-cannot-be-waived`.\n4. **Lean elaboration** (expensive, narrow): only for items actually written as Lean declarations. `spec/lean-verification-report-v1.md` runs `lake env lean ` per module + SHA-256 manifest match; `Zil.Trust.CertifiedRule` kernel-checks a proposition/proof pair.\n\nSo: **Lean serves as the deterministic validator only for the** `kernel-backed` **tier, and zil-lean's own architecture says you must not let success at tiers 1–3 masquerade as tier 4.** The prohibited-promotion table is the lossiness policy, pre-written.\n\n**What's lossy**: the natural-language statement itself (`statement` is a free-text field in the obligation schema, unvalidated); the *reason* an assumption is believed (only `source` is kept); alternative derivations (v1 provenance retains only the first witness); and — the deep one — the gap between \"Lean accepted this declaration\" and \"this declaration means what the user said.\" `theorem-statement-locks` spells it out: \"The lock does not claim that unchanged type fingerprints imply unchanged proof terms, source text, or external scientific meaning.\" The kernel must keep the utterance→claim→declaration chain because nothing downstream can reconstruct it.\n\n## 5. VERDICT — dev-sized?\n\n**A full elicit-lean/formal target is not dev-sized. A slice of it is, and it's a good one — but it is not the slice with Lean in it.**\n\nReasoning:\n\n* **The heavy part is real.** Getting from a user's claim to a Lean *statement* (not proof) requires committing to a type-theoretic encoding of the domain — the step autoformalization research finds hardest. That is deep-Lean-expertise work, and the contract is not definable without it. zil-lean quietly concedes this: it never writes theorem statements from intent. It stores `proof:Normalize.idempotent` as an opaque **token naming a declaration a human already wrote**. The demo file says so outright: \"Proof status here is bookkeeping only. The proof assistant remains the sole proof authority; proof tokens name checked declarations but do not assert them.\"\n* **The light part is genuinely there, and zil-lean is a working existence proof of it.** Eliciting an **assumption/lemma/theorem dependency graph with criticality and evidence pointers** — no Lean statements, no proofs — is weeks-scale. Every validator you need is graph-level and already specified in this repo.\n\n**Smallest formal-flavored slice: \"elicit-proof-obligations\" / the verification arc.** Interview a user about a system they believe is correct; capture assumptions (with owners and review status), lemmas, theorems, guarantees, and the dependency edges among them; attach evidence references where they exist; project to a `.zc`-style graph; validate by acyclicity + stratified Datalog closure yielding `PROVED/CONDITIONAL/WEAK/BROKEN` + break-root and impact queries.\n\n**Does it differ from elicit-gherkin on both pack axes? Yes, cleanly:**\n\n* *ElicitationPack*: Gherkin's concept contract is **scenario-shaped and example-driven** (Given/When/Then, concrete instances, no cross-item structure); completion is per-scenario coverage. This target's contract is **claim-shaped and dependency-structured** — the unit is a proposition with edges to other propositions, the hedge lens is central (Gherkin has no notion of an assumption), and completion is *graph closure plus evidence adequacy*, not enumeration. Different lenses, genuinely different \"enough.\"\n* *ProjectionPack*: Gherkin projects to a flat, independently-executable list; validation is parse + step-binding. This projects to a **DAG with derived statuses**, validated by fixpoint computation and an evidence-promotion lattice. The lossiness policy is substantive (assurance levels, first-witness-only) rather than near-absent.\n\n**Fallback comparison.** Take this slice **over** elicit-BPMN/process-mining as second target. Both differ from Gherkin, but the proof-obligation slice stresses the kernel harder on the axes the design cares about: it forces the claim-graph IR to carry *evidence grades and derivation provenance* (BPMN mostly forces sequencing and gateway structure, which Gherkin partly covers), and it gives you a deterministic non-trivial validator — a Datalog fixpoint — without any external tooling or domain SME. Keep BPMN as third; it's the better *breadth* target once the IR is stable, and it has an easier user-recruitment story. **Inference**, based on pack-axis distance, not on any BPMN sources reviewed here.\n\nOne caveat worth carrying: nothing in this repo has been validated by use. Treat the specs as well-reasoned design documents by an agent-assisted author, not as field-tested contracts — the `assurance-levels` lattice and the `THM_*` status ladder are worth stealing on their merits, not on their track record.\n\n## 6. Unreached sources\n\n* Did not build or run anything (`lake build`, `lake exe zilLeanTests`, `clojure -M:test`) — no Lean toolchain here; **all correctness claims are from specs and source, not execution.**\n* Did not read: the 129 Lean files in `Zil/` beyond filenames; `spec/zil-formal-core-v0.1.md` (427 lines, the core semantics); `spec/canonical-relational-ir-v0.1.md`; `spec/evidence-envelope-v1.md`; `spec/query-governance-v1.md`; `spec/recovery-audit-v1.md`; `spec/dmetavm-core-v0.1.md`; `formal/` (TLA+/SMT); the 17 `libsets/` domain packs; the 133 example files beyond three.\n* Repo wiki is enabled (`has_wiki: true`) — not checked. 1 open issue — not read. No discussions, no releases checked.\n* Author `jagg-ix` (Jorge A. Garcia) — no external profile or publication search performed, so no independent read on provenance beyond commit metadata.\n+++", - "proposedBodySha256": "acdf4e736f42e9c2600955c0daea658a8f66ca3788a89654f4ef5c23a6477df9", - "ambiguity": null, - "notes": null - }, - { - "id": "75228d43-47c4-4197-858c-433334016a59", - "identifier": "FE-1369", - "url": "https://linear.app/hash/issue/FE-1369/brunch-exchange-schema-audit-archive", - "state": { - "name": "Done", - "type": "completed" - }, - "parent": { - "id": "a5053b51-937d-453f-9c4e-fe8e49e912ad", - "identifier": "FE-1366", - "title": "Spec the elicitation harness architecture (archived wayfinder map)" - }, - "sourceUpdatedAt": "2026-08-19T16:20:27.340Z", - "sourceTitle": "Brunch exchange-schema audit [archive]", - "sourceDescriptionSha256": "1fd94e7b433040097a806d169e9cd702bb67db2dcbf906a5e197e678ed4249c2", - "sourceTitleSha256": "dfdef0d5ba4e9548fd25e728e40fb8685c0bd6e0daa4432d3793d8d5d6cb85e6", - "oldOuter": "Catalogued how the existing brunch app structures its question-and-answer exchanges, sorting every part into keep / adapt / leave behind for the new library. The generic asking machinery survives; brunch-specific vocabulary and review structures do not. Resolved 2026-08-06; part of the completed elicitation-harness planning (see parent map).", - "proposedTitle": "Classify Brunch exchange structures for reuse", - "proposedOuter": "This audit classified each part of the existing Brunch question-and-answer exchanges by whether the new library should keep, adapt, or leave it behind. The generic asking machinery remains useful, while Brunch-specific vocabulary and review structures do not carry forward. The audit was resolved on 2026-08-06.", - "extractionMethod": "first-standalone-divider", - "innerRecord": "\n> **Archive migration (2026-08-11).** Resolved ticket from the local-markdown wayfinder effort `elicitation-kernel` (`brunch-lite:.scratch/elicitation-kernel/issues/03-brunch-exchange-schema-audit.md`), mirrored to Linear for team visibility. Full original content below, including the resolution.\n\n---\n\n# Brunch exchange-schema audit\n\nType: research\nStatus: resolved\nResolved: 2026-08-06\n\n## Question\n\nCatalog brunch's structured-exchange vocabulary and classify each element as **generic to any elicitation** vs. **brunch-specific** (coupled to the spec-graph/product), so the questioning-UX and contract-decomposition tickets inherit a map of what to keep, generalize, or drop.\n\nSpecifically, in `../brunch` (read-only):\n\n* `src/exchanges/` — schemas (`ask`, `present_candidates`, `present_digest`, `present_review_set`, `request_choice`/`request_review`, capture/editor envelopes), projections, recovery\n* `src/agents/runtime/elicitor/` — prompt/context assembly, active-tool policy, the control-ownership table (spec posture / elicitation style / asking agenda)\n* `src/agents/skills/` — the activity-move routing table (analyze/elicit/ingest/propose/project/map/review) as candidate generic elicitation routines\n* Relevant `TOPOLOGY.md` files for boundary intent\n\nFor each element: what interaction problem it solves, where it hard-codes brunch's graph/spec shape, and any right-sizedness doubts visible in the code or docs (the abstractions \"were also just a guess\" — look for strain marks: legacy kept-for-reads projections, migration notes, loud failure modes).\n\n## Answer\n\n> Resolved by `/research` subagent, 2026-08-06. Facts cited to file:line-range in `../brunch`; judgments marked **\\[J\\]**.\n\n# Brunch Structured-Exchange & Elicitor Audit\n\n## 1. Exchange catalog\n\n`ask` **(the only registered interactive terminal).** Solves: getting exactly one typed response from a human, in one of four shapes, without a separate collect tool per shape. Params (`src/exchanges/schemas/params.ts:~180-215`) carry markdown `body`, optional `options[]`, `multiple`, `allowOther`, `allowNone`, `commentPrompt`, `topLabel`/`bottomLabel`; no options ⇒ free text, options ⇒ single choice, options + `multiple` ⇒ multi-choice (`src/.pi/extensions/exchanges/TOPOLOGY.md`, \"Answer sources\"). Details (`src/exchanges/schemas/request.ts:~390-460`) echo the question *and* the answer in one result: `question` echo (body, options, `commentPrompt`, `otherPrompt`) plus exactly one of `answered | cancelled | unavailable` — a **property-presence union** rather than a status enum (`src/exchanges/schemas/TOPOLOGY.md:293-300`). Continuation: `ask({ continues: exchange_id })` reads the declared continuation off a prior present; model-authored payload fields on a continuing ask are rejected at the params boundary (`params.ts` `zAskParams.superRefine`, message `\"continuing ask payload is declared by the referenced offer\"`).\n\n`present_candidates`**.** Solves: fan-out comparison — let the user *recognize* a direction among alternatives instead of authoring one. Details (`src/exchanges/schemas/present.ts:185-235`): `display{heading, body?}` plus `candidates[]` of `{id, title, user_rubric, meta_rubric, graph_refs[]}`. `user_rubric` is six required nonblank markdown fields — `core_bet, best_fit, cost_complexity, covers_well, main_risks, lock_in_constraints` (+ optional `recommendation`); `meta_rubric` is four optional fields — `legibility_cost_of_knowing, failure_modes, coverage_range, commitment` (the D31-L four-axis meta-rubric, `memory/SPEC.md:262`). Continuation: `continuation: zOptionRequiredAskContinuationDeclaration` → `{tool: \"ask\", params: {body, options, ...}}`; the answer emits a `request_choice` detail discriminant with `tool_meta.prev = present_candidates`, and may lead to `capture_candidate`.\n\n`present_digest`**.** Solves: putting a large ingested source in front of the user as prose. Details (`present.ts:237-262`): `digest{abstract (required, nonblank), analysis?, recommendation?}`. Explicitly *not* a graph carrier — graph payload fields are rejected (`schemas/TOPOLOGY.md:289`). Continuation is the **free-text** variant (`zFreeTextAskContinuationDeclaration`, which `z.never()`s options/multiple/allowOther/allowNone/commentPrompt/labels — `shared.ts`), collecting conversational feedback only. Acceptance is deliberately *not* a review decision: a later standalone `ask({acceptsDigest, questions})` questionnaire or a `confirm|revise` single-select mints the accepted carrier, copying `accepted_abstract` from the runtime-resolved digest — caller-authored abstracts are rejected (`src/exchanges/TOPOLOGY.md:27`; `projections/ask.ts` `projectDigestQuestionnaire` / `projectDigestConfirmation`).\n\n`present_review_set`**.** Solves: batch approval of a structurally valid graph-mutation proposal. Details (`present.ts:60-180`): `review_set{nodes[], edges[]}` where each node is `{draft_id, proposed_code, settlement: advisory|settled, plane: intent|oracle|design|plan, kind, title, body?, detail?}` and each edge is a role-named discriminated union over nine categories (`dependency, witness, rationale, realization, refinement, exclusion, composition, cross_reference, supersession`) with `{draft_id}|{existing_code}` endpoint refs. Continuation is option-required. Approval is the commit: an approved continuation invokes shared settlement *before* `ask.execute` returns, so only a committed, `receipt`-bearing result is appended (`.pi/extensions/exchanges/TOPOLOGY.md`; `ask/continuation.ts:527` throws `'Review-set approval must route through shared settlement'`). `request_changes` requires a comment; `request_changes`/`reject` are terminal-only, no graph effect.\n\n**Legacy** `present_question`**.** A merged question/offer anchor (`response_kind: answer|choice|choices` + options inline). Unregistered; its Pi adapter is deleted; the projection survives only for old persisted reads (`schemas/TOPOLOGY.md:132-134`; `projections/present-question.ts`).\n\n`request_choice` **/** `request_review` **/** `request_answer` **/** `request_choices`**.** No longer registered tools — they are the *preserved wire vocabulary* for terminal details, so capture/sweep readers keep reading what they always read (`.pi/extensions/exchanges/TOPOLOGY.md:81-84`). `request_review` projection callers must pass a present-tool discriminator because review-set and digest close through the same detail kind but capture reads them differently (`projections/request-response/review.ts` `ReviewPresentTool`).\n\n**Capture envelopes.** `capture_answer|choice|choices|review|candidate` (`schemas/capture.ts`), each a `zCaptureDetailsHeader` + `tool_meta{prev, curr}`. Graph payloads intentionally undesigned in this pass (`schemas/TOPOLOGY.md:386-390`); `capture_candidate` consumes only the selected id.\n\n**Editor envelope.** `editor.ts` is a *wire* envelope, not transcript detail: a JSON blob prefilled into `ctx.ui.editor` for the one payload Pi built-ins can't carry over RPC (multi-choice). Its `status: 'answered'|'cancelled'` string never enters details, which carry outcome as key presence (`schemas/TOPOLOGY.md:36-38`).\n\n**Questionnaires.** `schemas/questionnaire.ts`: `{id, kind: free-text|single-select|multi-select, prompt, options[]}` questions and kind-matched answers, replayed on persisted completion so IDs/kinds/option-membership/completeness can't diverge.\n\n## 2. Classification\n\n| Element | Class | Change needed / coupling |\n| -- | -- | -- |\n| `ask` single-tool-four-shapes | **Generic** | The strongest abstraction here. **\\[J\\]** One terminal covering free-text/single/multi + a declared-continuation mode is the right primitive. |\n| Property-presence terminal union (`answered`/`cancelled`/`unavailable`) | **Generic** | Distinguishes \"user declined\" from \"no UI available\" — both are non-answers, and conflating them is the classic bug. |\n| `comment` (user-authored) vs `message` (system-authored) split | **Generic** | `schemas/TOPOLOGY.md:108-112`. Cheap, high-value provenance discipline. |\n| Question-echo-in-result (self-contained terminal) | **Generic** | Makes the transcript replayable without joining back to the present. |\n| Declared continuation (`present` names its own terminal; collector rejects model re-authoring) | **Generic** | **\\[J\\]** The single most transferable idea: the offer owns the answer vocabulary, so the model cannot drift the options between showing and collecting. |\n| `exchange_id` + `tool_meta{prev,curr,next}` chain | **Generalize-with-changes** | Currently a hand-maintained discriminated union of ~15 literal prev/curr/next triples (`shared.ts`). Replace with a generic `{exchangeId, parentId?}` link + one `form` tag; the pairwise enumeration is combinatorial and buys little. |\n| Recovery scan / pending-present resumption | **Generic** | `recovery.ts:78-81`: only an *answered* terminal closes an exchange; cancelled/unavailable stay resumable. Any interviewer needs this. |\n| Present-then-ask two-step (offer ≠ collection) | **Generalize-with-changes** | Generic as \"render surface separate from input surface\", but brunch pins the offer set at three named tools. A kernel wants one `present(form, payload)` with pluggable payload validators. |\n| `present_candidates` as an *interaction* | **Generic** | Fan-out/compare/recognize is plane-invariant (A31-L, `memory/SPEC.md:111`). |\n| Candidate `user_rubric` (six required fields) | **Brunch-specific** | Hard-codes brunch's product judgment about what makes specs comparable (`core_bet`, `lock_in_constraints`). A kernel should take a caller-declared rubric schema. |\n| Candidate `meta_rubric` (D31-L four axes) | **Generalize-with-changes** | SPEC calls it \"a soft heuristic… not architecturally enforced\" (`memory/SPEC.md:262`) yet it is a required schema key. Demote to optional caller-supplied axes. |\n| `graph_refs[]` on candidates | **Brunch-specific** | Node-id coupling to the spec graph. |\n| `present_digest` as an interaction | **Generic** | \"Here is my reading of your source; react to it\" is universal to ingestion-shaped interviewing. |\n| Digest accept-via-later-carrier (feedback ≠ acceptance) | **Generalize-with-changes** | **\\[J\\]** The *separation* is a real lesson; the specific `acceptsDigest`/`accepted_abstract` field names and the \"runtime resolves the final eligible digest\" recognizer (`recovery.ts:18`) are brunch plumbing. |\n| `present_review_set` as an interaction | **Generalize-with-changes** | \"Approve/request-changes/reject a proposed batch of writes, atomically\" is generic. Brunch's version hardwires the target. |\n| Review-set node/edge draft schema | **Brunch-specific** | `plane: intent\\|oracle\\|design\\|plan`, nine graph edge categories, `settlement: advisory\\|settled` (D27-L), `proposed_code`. Params import `zReviewSetProposalPayloadForBoundary` from `graph/review-set.ts` — the only inbound coupling `exchanges/` allows (`exchanges/TOPOLOGY.md`, dependency direction). |\n| \"Boundary-teaching\" schema (advertise nested shape so the model sees it before the deep validator runs) | **Generic** | `schemas/TOPOLOGY.md:47-52`. **\\[J\\]** A genuinely good pattern: shallow schema for model legibility, deep validator for correctness. |\n| Approval-commits-atomically + receipt | **Generalize-with-changes** | Generic as \"the approve branch runs the target's commit and the terminal carries the target's receipt\"; `MutateGraphSuccess` (`request.ts:1`) is the coupling. Kernel needs an opaque target-supplied receipt type. |\n| Capture layer | **Brunch-specific** | Exists as envelope only, graph payloads undesigned; `capture_candidate` semantics are graph-shaped. |\n| Editor envelope (`editor.ts`) | **Generalize-with-changes** | Real problem (host UI can't carry a payload over RPC), brunch-shaped solution. Kernel: name it a \"degraded-transport fallback\", don't bind it to multi-choice. |\n| Option-id regex `^[^>\\r\\n]+$` | **Generalize-with-changes** | The comment says ids round-trip through an HTML-comment marker recovered by a regex stopping at `>` (`params.ts`). **\\[J\\]** A rendering leak in a validation schema — a kernel should encode ids opaquely instead. |\n| `active-tools.ts` allowlist | **Generalize-with-changes** | Generic pattern (fixed tool policy per role), brunch-specific contents (`read_graph`, `mutate_graph`, `read_elicitation_scratchpad`). |\n| Elicitation scratchpad (private, non-authoritative obligations) | **Generic** | D101-L: session-local, last-snapshot-wins, \"durable truth stays in the graph; low-confidence noticings land here.\" Every interviewer needs a place for \"noticed, not yet asked.\" |\n| `elicitation_style: interrogate\\|disambiguate\\|propose` | **Generic** | `session/elicitation-style.ts`. Style ≠ authority ≠ capability. |\n| `warrant-before-commit` directive (hash-pinned, ablatable) | **Generic** | `compose-live-prompt.ts` validates exactly one directive block, hashes it, exposes it as `providerVisibleText`, and supports dev-only ablation. **\\[J\\]** Treating one prompt paragraph as a versioned, A/B-testable artifact is a pattern worth stealing outright. |\n| Skill moves `analyze / elicit / ingest / propose / project / review` | **Generic** | These six are the actual elicitation routine vocabulary. |\n| Skill move `map` | **Brunch-specific** | Graph vocabulary/routing/persistence. |\n| Skill move `tutorial` | **Brunch-specific** | Product walkthrough of Brunch itself. |\n| `elicit` topology-driven question ranking table | **Generalize-with-changes** | The *shape* — structural signal in the target ⇒ question shape — is generic; every row names graph kinds (`assumption`, `witness` path, `criterion`, `exclusion`). Kernel: targets supply their own signal→question-shape table. |\n| Readiness bands as \"concentric concern envelopes, not workflow stages\" | **Generalize-with-changes** | **\\[J\\]** Excellent dialogue policy (\"absence matters *later*, capture is never illegal earlier\"); the band vocabulary is brunch's. |\n| Prompt-injected skill manifest (`` with absolute paths, \"do not infer additional skills\") | **Generic** | `skills/registry.ts` `renderBrunchSkills`. |\n| `intent -> design -> verification -> scope -> build` handoff sequence, frontier/scope edge directions, `PROJECT_EXECUTION_HARNESS_TITLE` verify-recipe block | **Brunch-specific** | `prompts/elicitor.md`; `compose-live-prompt.ts` `renderProjectExecutionHarnessGuidance`. |\n\n## 3. Strain marks\n\n**Legacy-kept-for-reads.** `present-question.ts` and `request-response.ts` survive as projections with no registered tool (`exchanges/TOPOLOGY.md:22`; `.pi/.../TOPOLOGY.md:81-84`). `shared.ts` literally names the residue: `STRUCTURED_EXCHANGE_TERMINAL_NAMES = { current: 'ask', legacyRequestPrefix: 'request_' }`. The tool topology collapsed from `present_question → request_response` to `ask`, but the *detail* vocabulary could not follow because capture/sweep readers were written against it.\n\n**Same detail kind, two meanings.** `request_review` closes both review-set and digest, so `projectRequestReview` demands a `ReviewPresentTool` discriminator from callers (`projections/request-response/review.ts`). Receipts are required for review-set approval and *strictly rejected* on every digest branch (`schemas/TOPOLOGY.md:~325`). **\\[J\\]** Two different transactions wearing one name — a merge that shouldn't have happened.\n\n**A retracted merge.** Digest acceptance *was* a review decision and was pulled back out: \"digest acceptance is no longer a review decision\" (`exchanges/TOPOLOGY.md:27`), with `recovery.ts:31-36` retaining a `legacyReview` parse path for digests approved under the old model.\n\n**Doc/code drift on edge categories.** `schemas/TOPOLOGY.md:194` documents `dependency | proof | support | realization | boundary | composition | association | supersession`; the Zod in `present.ts:89-160` implements `dependency | witness | rationale | realization | refinement | exclusion | composition | cross_reference | supersession`. Four categories were renamed and the schema doc was not updated.\n\n**Retired axes.** D98-L retired strategy/lens/method as runtime or manifest state; A35-L records the retreat: the axis model \"may still be useful as prompt-resource organization, but it is no longer trusted as user-changeable or transcript-backed runtime state\" (`memory/SPEC.md:115`). The migration note in `skills/TOPOLOGY.md` maps seven old constructs onto activity homes and ends with a hard rule: *\"if a skill is live, it appears in the first-level registry; if guidance is not there, it is not switchable product state.\"* Retired axes also left dead vocabulary behind — `lens` is still advertised in the review-set boundary schema (`schemas/TOPOLOGY.md:50`) and the elicitor prompt still says \"plan-lens review set\".\n\n**Loud failure modes.** Only two `throw`s in the whole exchange surface, both about authority: `'Review-set approval must route through shared settlement'` (`ask/continuation.ts:527`) and `'parsed ask parameters do not describe a runtime variant'` (`ask.ts:748`). Everything user-facing degrades to an `unavailable` terminal with a message instead (`continuation.ts:222` — a missing continuation declaration is an error string, not a crash). Params failures return bounded `TOOL_INPUT_INVALID` tool results with no human-visible transcript line.\n\n**Where validation lives (revised once).** D105-L: validate at trust boundaries — LLM params, RPC input, editor replies, transcript read-back — *not* inside constructors, which \"do not parse objects [they] just built.\" D108-L consolidated the whole contract out of two prior homes (`src/.pi/extensions/exchanges/schemas/` and `src/projections/exchanges/`) into `src/exchanges/`.\n\n**Lessons \\[J\\].** (a) Wire vocabulary outlives tool names — version the *detail* schema independently of the tool registry from day one, or every tool rename leaves a fossil. (b) Do not merge two interaction forms because their outcomes look alike; brunch merged digest into review and had to un-merge it while keeping a legacy read path. (c) An enumerated `prev/curr/next` chain across N forms is O(N²) maintenance for a link that could be one parent pointer. (d) The `settlement: advisory|settled` retrofit (D27-L, FE-1187) shows that *strength of assertion* is a first-class field, not a later annotation.\n\n## 4. Elicitor control model\n\nThree controls, three owners, three lifetimes (`runtime/elicitor/TOPOLOGY.md`, \"Control ownership\"):\n\n* **Spec posture** (`kind`/`origin`/`relatesToSpecId`) — owned by the persisted product row, spec lifetime. Session establishment decides *whether to ask*, not what the fact is; the live context renderer \"cannot establish or overwrite it.\" Enforced in code: `context.ts:14-19` comments that `workspace.posture` is a *workspace* stub, \"not D118-L spec posture.\"\n* **Elicitation style** (`interrogate|disambiguate|propose`) — last valid `brunch.elicitation_style` entry on the active branch, session lifetime across kicks. The prompt adapter \"projects it into the live elicitor control block without changing capability or authority.\"\n* **Asking agenda** — *has no state field at all*. Turn lifetime, \"reconsidered from current conversation and on-demand reads.\" Origination supplies neutral graph facts once; the prompt directs `establish orientation` then `focus a vein`.\n\nThe closing rule is the sharp part: **\"Formatting is not authority: shared text helpers may render facts, but spec posture cannot satisfy style, style cannot persist or gate an agenda, and the prompt cannot establish product posture.\"**\n\nThe conduct itself lives in `prompts/elicitor.md`: a new session \"starts from graph facts and an empty or inherited elicitation scratchpad, **never a scored or ranked agenda**\"; establish orientation, then \"pick one concrete thread worth pursuing this session and let the scratchpad track obligations you notice along the way, rather than trying to cover every absence at once.\" Scratchpad obligations are private working state — \"do not disclose even a summary… unless the user explicitly asks.\"\n\n**Judgment \\[J\\].** *Generic dialogue policy:* the three-way separation of persisted-fact / session-style / turn-agenda; the refusal to persist an agenda (D101-L retired the persisted spec-scoped register in favour of a session scratchpad — `memory/SPEC.md:180`); \"one vein, not full coverage\"; private-by-default working state; the three style values; \"formatting is not authority.\" *Product policy:* that spec posture is the persisted axis at all; the specific graph facts constituting orientation; the `intent→design→verification→scope→build` sequence and execution-harness verify-recipe block; the `mutate_graph`/`read_graph` tool allowlist. **\\[J\\]** The most under-appreciated design move is the *negative* one — an agenda deliberately has no storage, so it cannot go stale, cannot be gamed, and cannot become a scoring engine the prompt defers to instead of reading the conversation.\n\n## 5. Design lessons for a greenfield questioning-UX contract\n\n**Inherit**\n\n 1. **Declared continuations.** The offer names its own terminal in its result; the collector fills body/options from that declaration and rejects model-authored payload on a continuing call. This is the mechanism that makes offer→answer non-forgeable.\n 2. **Property-presence outcome union with three arms** — answered / cancelled / unavailable — plus the recovery rule that *only answered closes an exchange* (`recovery.ts:78-81`). Cancel must leave the offer resumable, and the user-facing hint must stay honest after a cancel.\n 3. **One terminal tool, several shapes.** Not one collect-tool per question type. Params-level cross-field refinement decides the shape; the runtime asserts the parsed params \"describe a runtime variant\" and throws if not.\n 4. **Self-contained terminals.** Echo the question, the options, and the sub-prompts into the answer detail so a transcript reader never needs the present.\n 5. `comment` **vs** `message` — never let user text and system text share a field.\n 6. **Boundary-teaching schemas.** Advertise the nested shape shallowly for model legibility; keep the deep requiredness contract in one validator the target owns.\n 7. **Agenda as derived state, not stored state**, with a private, non-authoritative scratchpad for \"noticed but not asked.\"\n 8. **Version and hash load-bearing prompt paragraphs.** `LIVE_ELICITOR_DIRECTIVES` pins one directive by sha256, validates its stable opening sentence, and supports dev-only ablation — prompt text as a testable artifact.\n 9. **Separate \"react to this\" from \"accept this.\"** Digest feedback and digest acceptance are different exchanges for a reason brunch learned by reverting the merge.\n10. **Approval-commits, with the target's receipt in the terminal.** Never leave \"approved\" and \"committed\" as two states the model can straddle — the settlement runs before the terminal is appended.\n\n**Avoid**\n\n11. **Enumerated** `prev/curr/next` **tool-meta unions.** Use one parent link and one form tag.\n12. **Product judgment baked into required schema fields** — the six-field `user_rubric` and the four-axis `meta_rubric` (documented as \"a soft heuristic… not architecturally enforced\" yet schema-required). Rubrics belong to the target, declared per call.\n13. **Rendering constraints leaking into validation** — the option-id regex exists because ids round-trip through an HTML comment. Encode ids opaquely.\n14. **One detail kind for two transactions.** `request_review` forces every caller to hand-carry a discriminator and forks receipt rules downstream.\n15. **Tool names as the versioning unit.** Brunch's wire vocabulary froze independently of its tool registry; plan for that from the start rather than maintaining `legacyRequestPrefix` and a parallel unregistered projection tree.\n\n**Key paths:** `../brunch/src/exchanges/{TOPOLOGY.md,recovery.ts,editor-envelope.ts,text.ts}`, `../brunch/src/exchanges/schemas/{TOPOLOGY.md,shared.ts,present.ts,request.ts,params.ts,questionnaire.ts,capture.ts,editor.ts}`, `../brunch/src/exchanges/projections/`, `../brunch/src/.pi/extensions/exchanges/{TOPOLOGY.md,ask.ts,ask/continuation.ts,index.ts}`, `../brunch/src/agents/runtime/elicitor/{TOPOLOGY.md,active-tools.ts,compose-live-prompt.ts,context.ts}`, `../brunch/src/agents/prompts/elicitor.md`, `../brunch/src/agents/skills/{TOPOLOGY.md,registry.ts,elicit/SKILL.md,propose/SKILL.md}`, `../brunch/src/session/elicitation-style.ts`, `../brunch/memory/SPEC.md`.", - "innerSha256": "09086b43d756206ba140c9019b0a1b0d89cb74d9783bfede2317e047e884e130", - "proposedBody": "This audit classified each part of the existing Brunch question-and-answer exchanges by whether the new library should keep, adapt, or leave it behind. The generic asking machinery remains useful, while Brunch-specific vocabulary and review structures do not carry forward. The audit was resolved on 2026-08-06.\n\n+++🏗️ Agent notes\n\n> **Archive migration (2026-08-11).** Resolved ticket from the local-markdown wayfinder effort `elicitation-kernel` (`brunch-lite:.scratch/elicitation-kernel/issues/03-brunch-exchange-schema-audit.md`), mirrored to Linear for team visibility. Full original content below, including the resolution.\n\n---\n\n# Brunch exchange-schema audit\n\nType: research\nStatus: resolved\nResolved: 2026-08-06\n\n## Question\n\nCatalog brunch's structured-exchange vocabulary and classify each element as **generic to any elicitation** vs. **brunch-specific** (coupled to the spec-graph/product), so the questioning-UX and contract-decomposition tickets inherit a map of what to keep, generalize, or drop.\n\nSpecifically, in `../brunch` (read-only):\n\n* `src/exchanges/` — schemas (`ask`, `present_candidates`, `present_digest`, `present_review_set`, `request_choice`/`request_review`, capture/editor envelopes), projections, recovery\n* `src/agents/runtime/elicitor/` — prompt/context assembly, active-tool policy, the control-ownership table (spec posture / elicitation style / asking agenda)\n* `src/agents/skills/` — the activity-move routing table (analyze/elicit/ingest/propose/project/map/review) as candidate generic elicitation routines\n* Relevant `TOPOLOGY.md` files for boundary intent\n\nFor each element: what interaction problem it solves, where it hard-codes brunch's graph/spec shape, and any right-sizedness doubts visible in the code or docs (the abstractions \"were also just a guess\" — look for strain marks: legacy kept-for-reads projections, migration notes, loud failure modes).\n\n## Answer\n\n> Resolved by `/research` subagent, 2026-08-06. Facts cited to file:line-range in `../brunch`; judgments marked **\\[J\\]**.\n\n# Brunch Structured-Exchange & Elicitor Audit\n\n## 1. Exchange catalog\n\n`ask` **(the only registered interactive terminal).** Solves: getting exactly one typed response from a human, in one of four shapes, without a separate collect tool per shape. Params (`src/exchanges/schemas/params.ts:~180-215`) carry markdown `body`, optional `options[]`, `multiple`, `allowOther`, `allowNone`, `commentPrompt`, `topLabel`/`bottomLabel`; no options ⇒ free text, options ⇒ single choice, options + `multiple` ⇒ multi-choice (`src/.pi/extensions/exchanges/TOPOLOGY.md`, \"Answer sources\"). Details (`src/exchanges/schemas/request.ts:~390-460`) echo the question *and* the answer in one result: `question` echo (body, options, `commentPrompt`, `otherPrompt`) plus exactly one of `answered | cancelled | unavailable` — a **property-presence union** rather than a status enum (`src/exchanges/schemas/TOPOLOGY.md:293-300`). Continuation: `ask({ continues: exchange_id })` reads the declared continuation off a prior present; model-authored payload fields on a continuing ask are rejected at the params boundary (`params.ts` `zAskParams.superRefine`, message `\"continuing ask payload is declared by the referenced offer\"`).\n\n`present_candidates`**.** Solves: fan-out comparison — let the user *recognize* a direction among alternatives instead of authoring one. Details (`src/exchanges/schemas/present.ts:185-235`): `display{heading, body?}` plus `candidates[]` of `{id, title, user_rubric, meta_rubric, graph_refs[]}`. `user_rubric` is six required nonblank markdown fields — `core_bet, best_fit, cost_complexity, covers_well, main_risks, lock_in_constraints` (+ optional `recommendation`); `meta_rubric` is four optional fields — `legibility_cost_of_knowing, failure_modes, coverage_range, commitment` (the D31-L four-axis meta-rubric, `memory/SPEC.md:262`). Continuation: `continuation: zOptionRequiredAskContinuationDeclaration` → `{tool: \"ask\", params: {body, options, ...}}`; the answer emits a `request_choice` detail discriminant with `tool_meta.prev = present_candidates`, and may lead to `capture_candidate`.\n\n`present_digest`**.** Solves: putting a large ingested source in front of the user as prose. Details (`present.ts:237-262`): `digest{abstract (required, nonblank), analysis?, recommendation?}`. Explicitly *not* a graph carrier — graph payload fields are rejected (`schemas/TOPOLOGY.md:289`). Continuation is the **free-text** variant (`zFreeTextAskContinuationDeclaration`, which `z.never()`s options/multiple/allowOther/allowNone/commentPrompt/labels — `shared.ts`), collecting conversational feedback only. Acceptance is deliberately *not* a review decision: a later standalone `ask({acceptsDigest, questions})` questionnaire or a `confirm|revise` single-select mints the accepted carrier, copying `accepted_abstract` from the runtime-resolved digest — caller-authored abstracts are rejected (`src/exchanges/TOPOLOGY.md:27`; `projections/ask.ts` `projectDigestQuestionnaire` / `projectDigestConfirmation`).\n\n`present_review_set`**.** Solves: batch approval of a structurally valid graph-mutation proposal. Details (`present.ts:60-180`): `review_set{nodes[], edges[]}` where each node is `{draft_id, proposed_code, settlement: advisory|settled, plane: intent|oracle|design|plan, kind, title, body?, detail?}` and each edge is a role-named discriminated union over nine categories (`dependency, witness, rationale, realization, refinement, exclusion, composition, cross_reference, supersession`) with `{draft_id}|{existing_code}` endpoint refs. Continuation is option-required. Approval is the commit: an approved continuation invokes shared settlement *before* `ask.execute` returns, so only a committed, `receipt`-bearing result is appended (`.pi/extensions/exchanges/TOPOLOGY.md`; `ask/continuation.ts:527` throws `'Review-set approval must route through shared settlement'`). `request_changes` requires a comment; `request_changes`/`reject` are terminal-only, no graph effect.\n\n**Legacy** `present_question`**.** A merged question/offer anchor (`response_kind: answer|choice|choices` + options inline). Unregistered; its Pi adapter is deleted; the projection survives only for old persisted reads (`schemas/TOPOLOGY.md:132-134`; `projections/present-question.ts`).\n\n`request_choice` **/** `request_review` **/** `request_answer` **/** `request_choices`**.** No longer registered tools — they are the *preserved wire vocabulary* for terminal details, so capture/sweep readers keep reading what they always read (`.pi/extensions/exchanges/TOPOLOGY.md:81-84`). `request_review` projection callers must pass a present-tool discriminator because review-set and digest close through the same detail kind but capture reads them differently (`projections/request-response/review.ts` `ReviewPresentTool`).\n\n**Capture envelopes.** `capture_answer|choice|choices|review|candidate` (`schemas/capture.ts`), each a `zCaptureDetailsHeader` + `tool_meta{prev, curr}`. Graph payloads intentionally undesigned in this pass (`schemas/TOPOLOGY.md:386-390`); `capture_candidate` consumes only the selected id.\n\n**Editor envelope.** `editor.ts` is a *wire* envelope, not transcript detail: a JSON blob prefilled into `ctx.ui.editor` for the one payload Pi built-ins can't carry over RPC (multi-choice). Its `status: 'answered'|'cancelled'` string never enters details, which carry outcome as key presence (`schemas/TOPOLOGY.md:36-38`).\n\n**Questionnaires.** `schemas/questionnaire.ts`: `{id, kind: free-text|single-select|multi-select, prompt, options[]}` questions and kind-matched answers, replayed on persisted completion so IDs/kinds/option-membership/completeness can't diverge.\n\n## 2. Classification\n\n| Element | Class | Change needed / coupling |\n| -- | -- | -- |\n| `ask` single-tool-four-shapes | **Generic** | The strongest abstraction here. **\\[J\\]** One terminal covering free-text/single/multi + a declared-continuation mode is the right primitive. |\n| Property-presence terminal union (`answered`/`cancelled`/`unavailable`) | **Generic** | Distinguishes \"user declined\" from \"no UI available\" — both are non-answers, and conflating them is the classic bug. |\n| `comment` (user-authored) vs `message` (system-authored) split | **Generic** | `schemas/TOPOLOGY.md:108-112`. Cheap, high-value provenance discipline. |\n| Question-echo-in-result (self-contained terminal) | **Generic** | Makes the transcript replayable without joining back to the present. |\n| Declared continuation (`present` names its own terminal; collector rejects model re-authoring) | **Generic** | **\\[J\\]** The single most transferable idea: the offer owns the answer vocabulary, so the model cannot drift the options between showing and collecting. |\n| `exchange_id` + `tool_meta{prev,curr,next}` chain | **Generalize-with-changes** | Currently a hand-maintained discriminated union of ~15 literal prev/curr/next triples (`shared.ts`). Replace with a generic `{exchangeId, parentId?}` link + one `form` tag; the pairwise enumeration is combinatorial and buys little. |\n| Recovery scan / pending-present resumption | **Generic** | `recovery.ts:78-81`: only an *answered* terminal closes an exchange; cancelled/unavailable stay resumable. Any interviewer needs this. |\n| Present-then-ask two-step (offer ≠ collection) | **Generalize-with-changes** | Generic as \"render surface separate from input surface\", but brunch pins the offer set at three named tools. A kernel wants one `present(form, payload)` with pluggable payload validators. |\n| `present_candidates` as an *interaction* | **Generic** | Fan-out/compare/recognize is plane-invariant (A31-L, `memory/SPEC.md:111`). |\n| Candidate `user_rubric` (six required fields) | **Brunch-specific** | Hard-codes brunch's product judgment about what makes specs comparable (`core_bet`, `lock_in_constraints`). A kernel should take a caller-declared rubric schema. |\n| Candidate `meta_rubric` (D31-L four axes) | **Generalize-with-changes** | SPEC calls it \"a soft heuristic… not architecturally enforced\" (`memory/SPEC.md:262`) yet it is a required schema key. Demote to optional caller-supplied axes. |\n| `graph_refs[]` on candidates | **Brunch-specific** | Node-id coupling to the spec graph. |\n| `present_digest` as an interaction | **Generic** | \"Here is my reading of your source; react to it\" is universal to ingestion-shaped interviewing. |\n| Digest accept-via-later-carrier (feedback ≠ acceptance) | **Generalize-with-changes** | **\\[J\\]** The *separation* is a real lesson; the specific `acceptsDigest`/`accepted_abstract` field names and the \"runtime resolves the final eligible digest\" recognizer (`recovery.ts:18`) are brunch plumbing. |\n| `present_review_set` as an interaction | **Generalize-with-changes** | \"Approve/request-changes/reject a proposed batch of writes, atomically\" is generic. Brunch's version hardwires the target. |\n| Review-set node/edge draft schema | **Brunch-specific** | `plane: intent\\|oracle\\|design\\|plan`, nine graph edge categories, `settlement: advisory\\|settled` (D27-L), `proposed_code`. Params import `zReviewSetProposalPayloadForBoundary` from `graph/review-set.ts` — the only inbound coupling `exchanges/` allows (`exchanges/TOPOLOGY.md`, dependency direction). |\n| \"Boundary-teaching\" schema (advertise nested shape so the model sees it before the deep validator runs) | **Generic** | `schemas/TOPOLOGY.md:47-52`. **\\[J\\]** A genuinely good pattern: shallow schema for model legibility, deep validator for correctness. |\n| Approval-commits-atomically + receipt | **Generalize-with-changes** | Generic as \"the approve branch runs the target's commit and the terminal carries the target's receipt\"; `MutateGraphSuccess` (`request.ts:1`) is the coupling. Kernel needs an opaque target-supplied receipt type. |\n| Capture layer | **Brunch-specific** | Exists as envelope only, graph payloads undesigned; `capture_candidate` semantics are graph-shaped. |\n| Editor envelope (`editor.ts`) | **Generalize-with-changes** | Real problem (host UI can't carry a payload over RPC), brunch-shaped solution. Kernel: name it a \"degraded-transport fallback\", don't bind it to multi-choice. |\n| Option-id regex `^[^>\\r\\n]+$` | **Generalize-with-changes** | The comment says ids round-trip through an HTML-comment marker recovered by a regex stopping at `>` (`params.ts`). **\\[J\\]** A rendering leak in a validation schema — a kernel should encode ids opaquely instead. |\n| `active-tools.ts` allowlist | **Generalize-with-changes** | Generic pattern (fixed tool policy per role), brunch-specific contents (`read_graph`, `mutate_graph`, `read_elicitation_scratchpad`). |\n| Elicitation scratchpad (private, non-authoritative obligations) | **Generic** | D101-L: session-local, last-snapshot-wins, \"durable truth stays in the graph; low-confidence noticings land here.\" Every interviewer needs a place for \"noticed, not yet asked.\" |\n| `elicitation_style: interrogate\\|disambiguate\\|propose` | **Generic** | `session/elicitation-style.ts`. Style ≠ authority ≠ capability. |\n| `warrant-before-commit` directive (hash-pinned, ablatable) | **Generic** | `compose-live-prompt.ts` validates exactly one directive block, hashes it, exposes it as `providerVisibleText`, and supports dev-only ablation. **\\[J\\]** Treating one prompt paragraph as a versioned, A/B-testable artifact is a pattern worth stealing outright. |\n| Skill moves `analyze / elicit / ingest / propose / project / review` | **Generic** | These six are the actual elicitation routine vocabulary. |\n| Skill move `map` | **Brunch-specific** | Graph vocabulary/routing/persistence. |\n| Skill move `tutorial` | **Brunch-specific** | Product walkthrough of Brunch itself. |\n| `elicit` topology-driven question ranking table | **Generalize-with-changes** | The *shape* — structural signal in the target ⇒ question shape — is generic; every row names graph kinds (`assumption`, `witness` path, `criterion`, `exclusion`). Kernel: targets supply their own signal→question-shape table. |\n| Readiness bands as \"concentric concern envelopes, not workflow stages\" | **Generalize-with-changes** | **\\[J\\]** Excellent dialogue policy (\"absence matters *later*, capture is never illegal earlier\"); the band vocabulary is brunch's. |\n| Prompt-injected skill manifest (`` with absolute paths, \"do not infer additional skills\") | **Generic** | `skills/registry.ts` `renderBrunchSkills`. |\n| `intent -> design -> verification -> scope -> build` handoff sequence, frontier/scope edge directions, `PROJECT_EXECUTION_HARNESS_TITLE` verify-recipe block | **Brunch-specific** | `prompts/elicitor.md`; `compose-live-prompt.ts` `renderProjectExecutionHarnessGuidance`. |\n\n## 3. Strain marks\n\n**Legacy-kept-for-reads.** `present-question.ts` and `request-response.ts` survive as projections with no registered tool (`exchanges/TOPOLOGY.md:22`; `.pi/.../TOPOLOGY.md:81-84`). `shared.ts` literally names the residue: `STRUCTURED_EXCHANGE_TERMINAL_NAMES = { current: 'ask', legacyRequestPrefix: 'request_' }`. The tool topology collapsed from `present_question → request_response` to `ask`, but the *detail* vocabulary could not follow because capture/sweep readers were written against it.\n\n**Same detail kind, two meanings.** `request_review` closes both review-set and digest, so `projectRequestReview` demands a `ReviewPresentTool` discriminator from callers (`projections/request-response/review.ts`). Receipts are required for review-set approval and *strictly rejected* on every digest branch (`schemas/TOPOLOGY.md:~325`). **\\[J\\]** Two different transactions wearing one name — a merge that shouldn't have happened.\n\n**A retracted merge.** Digest acceptance *was* a review decision and was pulled back out: \"digest acceptance is no longer a review decision\" (`exchanges/TOPOLOGY.md:27`), with `recovery.ts:31-36` retaining a `legacyReview` parse path for digests approved under the old model.\n\n**Doc/code drift on edge categories.** `schemas/TOPOLOGY.md:194` documents `dependency | proof | support | realization | boundary | composition | association | supersession`; the Zod in `present.ts:89-160` implements `dependency | witness | rationale | realization | refinement | exclusion | composition | cross_reference | supersession`. Four categories were renamed and the schema doc was not updated.\n\n**Retired axes.** D98-L retired strategy/lens/method as runtime or manifest state; A35-L records the retreat: the axis model \"may still be useful as prompt-resource organization, but it is no longer trusted as user-changeable or transcript-backed runtime state\" (`memory/SPEC.md:115`). The migration note in `skills/TOPOLOGY.md` maps seven old constructs onto activity homes and ends with a hard rule: *\"if a skill is live, it appears in the first-level registry; if guidance is not there, it is not switchable product state.\"* Retired axes also left dead vocabulary behind — `lens` is still advertised in the review-set boundary schema (`schemas/TOPOLOGY.md:50`) and the elicitor prompt still says \"plan-lens review set\".\n\n**Loud failure modes.** Only two `throw`s in the whole exchange surface, both about authority: `'Review-set approval must route through shared settlement'` (`ask/continuation.ts:527`) and `'parsed ask parameters do not describe a runtime variant'` (`ask.ts:748`). Everything user-facing degrades to an `unavailable` terminal with a message instead (`continuation.ts:222` — a missing continuation declaration is an error string, not a crash). Params failures return bounded `TOOL_INPUT_INVALID` tool results with no human-visible transcript line.\n\n**Where validation lives (revised once).** D105-L: validate at trust boundaries — LLM params, RPC input, editor replies, transcript read-back — *not* inside constructors, which \"do not parse objects [they] just built.\" D108-L consolidated the whole contract out of two prior homes (`src/.pi/extensions/exchanges/schemas/` and `src/projections/exchanges/`) into `src/exchanges/`.\n\n**Lessons \\[J\\].** (a) Wire vocabulary outlives tool names — version the *detail* schema independently of the tool registry from day one, or every tool rename leaves a fossil. (b) Do not merge two interaction forms because their outcomes look alike; brunch merged digest into review and had to un-merge it while keeping a legacy read path. (c) An enumerated `prev/curr/next` chain across N forms is O(N²) maintenance for a link that could be one parent pointer. (d) The `settlement: advisory|settled` retrofit (D27-L, FE-1187) shows that *strength of assertion* is a first-class field, not a later annotation.\n\n## 4. Elicitor control model\n\nThree controls, three owners, three lifetimes (`runtime/elicitor/TOPOLOGY.md`, \"Control ownership\"):\n\n* **Spec posture** (`kind`/`origin`/`relatesToSpecId`) — owned by the persisted product row, spec lifetime. Session establishment decides *whether to ask*, not what the fact is; the live context renderer \"cannot establish or overwrite it.\" Enforced in code: `context.ts:14-19` comments that `workspace.posture` is a *workspace* stub, \"not D118-L spec posture.\"\n* **Elicitation style** (`interrogate|disambiguate|propose`) — last valid `brunch.elicitation_style` entry on the active branch, session lifetime across kicks. The prompt adapter \"projects it into the live elicitor control block without changing capability or authority.\"\n* **Asking agenda** — *has no state field at all*. Turn lifetime, \"reconsidered from current conversation and on-demand reads.\" Origination supplies neutral graph facts once; the prompt directs `establish orientation` then `focus a vein`.\n\nThe closing rule is the sharp part: **\"Formatting is not authority: shared text helpers may render facts, but spec posture cannot satisfy style, style cannot persist or gate an agenda, and the prompt cannot establish product posture.\"**\n\nThe conduct itself lives in `prompts/elicitor.md`: a new session \"starts from graph facts and an empty or inherited elicitation scratchpad, **never a scored or ranked agenda**\"; establish orientation, then \"pick one concrete thread worth pursuing this session and let the scratchpad track obligations you notice along the way, rather than trying to cover every absence at once.\" Scratchpad obligations are private working state — \"do not disclose even a summary… unless the user explicitly asks.\"\n\n**Judgment \\[J\\].** *Generic dialogue policy:* the three-way separation of persisted-fact / session-style / turn-agenda; the refusal to persist an agenda (D101-L retired the persisted spec-scoped register in favour of a session scratchpad — `memory/SPEC.md:180`); \"one vein, not full coverage\"; private-by-default working state; the three style values; \"formatting is not authority.\" *Product policy:* that spec posture is the persisted axis at all; the specific graph facts constituting orientation; the `intent→design→verification→scope→build` sequence and execution-harness verify-recipe block; the `mutate_graph`/`read_graph` tool allowlist. **\\[J\\]** The most under-appreciated design move is the *negative* one — an agenda deliberately has no storage, so it cannot go stale, cannot be gamed, and cannot become a scoring engine the prompt defers to instead of reading the conversation.\n\n## 5. Design lessons for a greenfield questioning-UX contract\n\n**Inherit**\n\n 1. **Declared continuations.** The offer names its own terminal in its result; the collector fills body/options from that declaration and rejects model-authored payload on a continuing call. This is the mechanism that makes offer→answer non-forgeable.\n 2. **Property-presence outcome union with three arms** — answered / cancelled / unavailable — plus the recovery rule that *only answered closes an exchange* (`recovery.ts:78-81`). Cancel must leave the offer resumable, and the user-facing hint must stay honest after a cancel.\n 3. **One terminal tool, several shapes.** Not one collect-tool per question type. Params-level cross-field refinement decides the shape; the runtime asserts the parsed params \"describe a runtime variant\" and throws if not.\n 4. **Self-contained terminals.** Echo the question, the options, and the sub-prompts into the answer detail so a transcript reader never needs the present.\n 5. `comment` **vs** `message` — never let user text and system text share a field.\n 6. **Boundary-teaching schemas.** Advertise the nested shape shallowly for model legibility; keep the deep requiredness contract in one validator the target owns.\n 7. **Agenda as derived state, not stored state**, with a private, non-authoritative scratchpad for \"noticed but not asked.\"\n 8. **Version and hash load-bearing prompt paragraphs.** `LIVE_ELICITOR_DIRECTIVES` pins one directive by sha256, validates its stable opening sentence, and supports dev-only ablation — prompt text as a testable artifact.\n 9. **Separate \"react to this\" from \"accept this.\"** Digest feedback and digest acceptance are different exchanges for a reason brunch learned by reverting the merge.\n10. **Approval-commits, with the target's receipt in the terminal.** Never leave \"approved\" and \"committed\" as two states the model can straddle — the settlement runs before the terminal is appended.\n\n**Avoid**\n\n11. **Enumerated** `prev/curr/next` **tool-meta unions.** Use one parent link and one form tag.\n12. **Product judgment baked into required schema fields** — the six-field `user_rubric` and the four-axis `meta_rubric` (documented as \"a soft heuristic… not architecturally enforced\" yet schema-required). Rubrics belong to the target, declared per call.\n13. **Rendering constraints leaking into validation** — the option-id regex exists because ids round-trip through an HTML comment. Encode ids opaquely.\n14. **One detail kind for two transactions.** `request_review` forces every caller to hand-carry a discriminator and forks receipt rules downstream.\n15. **Tool names as the versioning unit.** Brunch's wire vocabulary froze independently of its tool registry; plan for that from the start rather than maintaining `legacyRequestPrefix` and a parallel unregistered projection tree.\n\n**Key paths:** `../brunch/src/exchanges/{TOPOLOGY.md,recovery.ts,editor-envelope.ts,text.ts}`, `../brunch/src/exchanges/schemas/{TOPOLOGY.md,shared.ts,present.ts,request.ts,params.ts,questionnaire.ts,capture.ts,editor.ts}`, `../brunch/src/exchanges/projections/`, `../brunch/src/.pi/extensions/exchanges/{TOPOLOGY.md,ask.ts,ask/continuation.ts,index.ts}`, `../brunch/src/agents/runtime/elicitor/{TOPOLOGY.md,active-tools.ts,compose-live-prompt.ts,context.ts}`, `../brunch/src/agents/prompts/elicitor.md`, `../brunch/src/agents/skills/{TOPOLOGY.md,registry.ts,elicit/SKILL.md,propose/SKILL.md}`, `../brunch/src/session/elicitation-style.ts`, `../brunch/memory/SPEC.md`.\n+++", - "proposedBodySha256": "bf35cca443ec1319a47374634cfca1234dbb65939b2f8a9c12dac9b9ba773d3a", - "ambiguity": null, - "notes": null - }, - { - "id": "9d7da48f-bd0d-4320-ab00-08e9060625f0", - "identifier": "FE-1370", - "url": "https://linear.app/hash/issue/FE-1370/contract-decomposition-kernel-host-plugin-pack-boundary-archive", - "state": { - "name": "Done", - "type": "completed" - }, - "parent": { - "id": "a5053b51-937d-453f-9c4e-fe8e49e912ad", - "identifier": "FE-1366", - "title": "Spec the elicitation harness architecture (archived wayfinder map)" - }, - "sourceUpdatedAt": "2026-08-19T16:20:27.501Z", - "sourceTitle": "Contract decomposition: kernel / host / plugin / pack boundary [archive]", - "sourceDescriptionSha256": "2234383db65ad51aafd0ef50877851b0449988ebd0e1c57173120549a6f95c11", - "sourceTitleSha256": "e26e5d7d5c992fae3d3148ce48e892d0f0246c65aace5cb2f76f686181b778a8", - "oldOuter": "Decided how responsibility splits between the generic interviewing engine and the pluggable subject-matter definitions. The engine owns a domain-free envelope around whatever a plugin stores — with evidence links, confidence, and explicit \"no answer\" states — and typed issues as its feedback channel; there is deliberately no universal data model. Resolved 2026-08-07; part of the completed elicitation-harness planning (see parent map).", - "proposedTitle": "Define the harness and plugin responsibilities", - "proposedOuter": "This decision assigns responsibilities between the generic interviewing engine and the subject-matter plugins. The engine owns a domain-free envelope for plugin data, including evidence links, confidence, explicit absence states, and typed issues for feedback. The design does not impose one universal data model and was resolved on 2026-08-07.", - "extractionMethod": "first-standalone-divider", - "innerRecord": "\n> **Archive migration (2026-08-11).** Resolved ticket from the local-markdown wayfinder effort `elicitation-kernel` (`brunch-lite:.scratch/elicitation-kernel/issues/04-contract-decomposition.md`), mirrored to Linear for team visibility. Full original content below, including the resolution.\n\n---\n\n# Contract decomposition: kernel / host / plugin / pack boundary\n\nType: grilling\nStatus: resolved\nResolved: 2026-08-06\nBlocked by: 01, 03 (both resolved)\n\n## Question\n\nWhat exactly does the kernel own (mechanism + orchestration), what does the host supply (input shapes and pathways, deploy target), and what do plugins define (target policy) — and does the four-contract + pack decomposition (ElicitationPack: concept/observation/completion; ProjectionPack: projection) survive contact with the Flue facts and the brunch audit?\n\nSub-questions this grilling must close:\n\n* Are the A-axis (semantic target) and B-axis (representation target) genuinely separately swappable in our first milestone, or bundled per plugin?\n* Where does the evidence-preserving IR / claim graph live, and how thin is its common core?\n* Persistence: does the plugin-owned-persistence hypothesis hold, or does the host (deploy target) own it with plugins declaring shape? What state must the kernel externalize (session, transcript refs, artifact-in-progress, episteme ledger)?\n* Where is control inverted: typed issues as backpressure (projector → elicitation controller) — is that the only inversion, or do observation lenses invert too?\n* The policy-vs-mechanism rule: enumerate what would otherwise become the central `switch` and check each is on the plugin side.\n\nPrimary input: docs/reference/agentic-elicitation-challenges-2026-08-06T10-02-41Z.md\n\nNamed input from the portfolio decision (issue 07): **behavioral over procedural** — agents do better with behavioral guidance than procedural scripts, and with clear shapes/patterns to fill rather than schemas that require extensive parsing to build a model of the output shape. Brunch's unsolved problem — specifying how an elicitation process should work plus skill material to guide an agent through it, without over-proceduralizing — is a core stress test for the pack contract. The decomposition must say what a pack *feels like* to the agent consuming it, not only what it validates.\n\nConcrete test cases for every boundary claim (from issue 07): how would `elicit-gherkin` do this vs. `elicit-proof-obligations`? The spec mandates both packs are authored before the pack interface freezes.\n\n## Answer\n\n> Resolved by HITL grilling, 2026-08-06 (four rounds, including an evidence pass over `~/Clones/mattpocock/skills` + `../brunch/docs/design/BEHAVIORAL_KERNELS.md`, and integration of [agentic-elicitation-criteria](<../../docs/reference/agentic-elicitation-criteria-2026-08-06T14-11-18Z.md>), the second inbox doc).\n\n### Ownership table\n\n| | Owns |\n| -- | -- |\n| **Kernel** *(mechanism + orchestration)* | The conversation loop, agent-forward (agent judgment at the helm) · the questioning-UX contract (issue 05's subject) · the **capture envelope** (below) around opaque plugin payloads · the **typed issue queue** (vocabulary, storage, factual attributes; the only stored agenda-like state; also where conflict/equivalence live) · the private scratchpad · the **turn-suspension protocol** (Flue has no ask-primitive; the kernel owns one) · operation *signatures* (`observe/reconcile/project/validate`) with snapshot-in/deltas-out calling convention, validation, and application of returned deltas · completion evaluation (running plugin-declared criteria) · pack loading, progressive disclosure, kernel-card activation · capture-id minting · the **storage port** definition |\n| **Host** *(embedding + affordances)* | Input surfaces and pathways (TUI / web / chat channel / Petrinaut later) · verified respondent identity · deploy target · **storage port implementation** · artifact delivery (repo write, API, post) · model/provider via the Pi family |\n| **Plugin** *(target policy)* | Its **own IR payload structure** — graph, flat list, whatever fits; shared between its packs, never universal; namespaced concepts · **ElicitationPack**: kernel cards (Detects / Goal / contrastive Questions / Artifacts), completion contract, clarification hints · **ProjectionPack(s)**: `project` + `validate` (required), `reconcile` (optional), output contract, annotated shapes, **typed loss reports**, lossiness policy · artifact persistence *shape* · domain vocabulary |\n\n### The capture envelope (the hourglass waist)\n\nKernel-defined, domain-free — semantically rich, structurally minimal:\n\n* `id` (kernel-minted), evidence spans (utterance provenance, phrase-level where possible)\n* **Epistemic status** enum, distinct from confidence: `explicit | inferred | tentative | defaulted | external-lookup`\n* Confidence (qualitative), status: `active | superseded | retracted`, one `supersedes` link\n* **Absence states** as first-class capture values: `not-mentioned | unknown-to-user | not-yet-decided | not-applicable | explicitly-absent | declined | deferred`\n* **Alternatives** grouping: >1 live interpretation of the same evidence may coexist until resolved\n* Opaque, plugin-typed payload. **No kernel edges, no graph, no kind taxonomy** — structure is payload business. Conflict (`conflicting`) and equivalence (`possibly-equivalent`) are **typed issues referencing capture ids**, not edges; resolution must be an explicit event (supersession or recorded decision) — \"no silent conflict resolution.\"\n\n### Operations\n\n* **Required**: `project` (captures → draft artifact **+ typed loss report**: `mapped-exactly / normalized / approximate / collapsed / omitted / defaulted / unrepresentable`) and `validate` (→ typed issues).\n* **Optional**: `reconcile` (dedup/merge over the plugin's own structure); kernel calls it when present.\n* **Agent-native**: `observe` — noticing is the agent's work guided by pack kernel cards; code-level extractors are an optimization, never the required path.\n* **Calling convention**: plugin ops receive an **immutable state snapshot**, return observations/issues/deltas; the kernel validates and applies. Buys atomic plugin failure, semantically idempotent retries (a retry never counts as a second user assertion), and tracing.\n* **Backpressure**: validators and projectors never address the user; they return typed issues the agent consumes.\n\n### Dialogue policy\n\nBehavioral guidance + factual issue queue. **Facts computed, weights judged**: the kernel computes issue facts (blocks-required-criterion, origin semantic|representational, can_default); the agent weighs them qualitatively. The inbox doc's priority formula is adopted as *prose the agent thinks with*, never as a computed score — computed-priority dimensions are judgments wearing metric costumes, and a stored ranking becomes an authority the agent defers to instead of reading the conversation (brunch's no-stored-agenda lesson).\n\n### Cross-cutting decisions\n\n1. **No universal IR** — the kernel's slice is the envelope; structure is plugin-unique. Typed-entity-graph maximalism explicitly not adopted; a graph remains any plugin's private choice (including a future brunch-target plugin).\n2. **Smallest-honest-plugin test** — a flat record list + one validator must suffice; every kernel-contract addition is checked against the bar it raises. Empirical form: the black-box authoring test.\n3. **Axes separated in contract, bundled in shipping** — one ElicitationPack + N ProjectionPacks per plugin sharing the plugin's IR; swappability proven by reprojection.\n4. **Principle v2 (ratified, replaces \"behavioral over procedural\")**: *procedure for mechanism, anchors for judgment, shapes for output*. Evidence: the mattpocock skills are full of procedure that works because it is short, carried by leading words (pretrained concepts recruited deliberately), ends on checkable completion criteria, and rides on shapes/templates with progressive disclosure. Failure modes to design against: sprawl, negation-steering, no-ops, judgment-encoded-as-procedure — not procedure itself. `writing-for-agents` is the cited pack-authoring standard.\n5. **Kernel cards** (from BEHAVIORAL_KERNELS.md) are the unit of ElicitationPack content: Detects (signal-phrase activation) / Goal / contrastive Question patterns / Artifacts (typed claims emitted) / validator hooks. The fifteen-kernel ontology itself stays brunch prior art; packs declare their own kernels. Contrastive classification over open-ended essays.\n6. **Pack physical form**: kernel cards + annotated shapes + deterministic validators + small wire schemas (boundary-teaching: shallow for model legibility, deep requiredness in validators) + completion contract as checkable bounds.\n\n### Acceptance material adopted into the spec (from the criteria doc)\n\n* The **five proof obligations** as contract acceptance criteria: independent variability, semantic conservation, explicit transformation, controlled elicitation, local implementation.\n* The **ten kernel invariants** (§9) as kernel-enforced test properties (no unsupported value without provenance; no silent conflict resolution; no silent projection loss; corrections don't erase history; retries idempotent; target issues namespaced; plugin failures atomic; equivalent state → equivalent projection; unknown ≠ false; explicit ≠ inferred/defaulted).\n* Gating tests: **reprojection/projector substitution**, **minimal pairs** (\"the budget is / might be €20,000\"), **black-box authoring test** (count concepts, boilerplate, escape hatches).\n* Named smells as review vocabulary: opaque payload waist, giant context bag, schema-shaped questioning, null collapse, silent coercion/loss, correction-as-duplication, hidden target leakage.\n\n### Deferred to fog (post-milestone)\n\nSimultaneous multi-plugin composition · plugin removal · full replay · capability negotiation · version/migration machinery (the spec names the five version axes — API contract / plugin impl / concept-schema / target-schema / persisted state — implements nothing).\n\n### Routed onward\n\n* §4 SDK-machinery list (evidence anchoring, issue construction, fixtures, **local simulation harness** — \"debugging should not require reading an entire agent transcript\") → issue 06 (Shipping shape).\n* Envelope + absence states + turn-suspension → issue 05 (Questioning-UX contract).\n\n## Comments\n\n**2026-08-07 (vocabulary clarification from issue 05's resolution).** The ownership table's \"Host (embedding + affordances)\" bundled two concerns that later split: the **ui** shell (interface: rendering, input, reply transport, identity) and the **substrate** (the embedding environment: deploy target, storage-port implementation, artifact delivery, model/provider). Under the hardened lexicon (`CONTEXT.md`), this table's \"Host\" row reads as substrate concerns plus ui concerns; \"kernel\" reads as **harness**. No substantive change to the decomposition.", - "innerSha256": "a8617883eff469f2b9a5dce712067f072907d7ca666141998a19d46323477384", - "proposedBody": "This decision assigns responsibilities between the generic interviewing engine and the subject-matter plugins. The engine owns a domain-free envelope for plugin data, including evidence links, confidence, explicit absence states, and typed issues for feedback. The design does not impose one universal data model and was resolved on 2026-08-07.\n\n+++🏗️ Agent notes\n\n> **Archive migration (2026-08-11).** Resolved ticket from the local-markdown wayfinder effort `elicitation-kernel` (`brunch-lite:.scratch/elicitation-kernel/issues/04-contract-decomposition.md`), mirrored to Linear for team visibility. Full original content below, including the resolution.\n\n---\n\n# Contract decomposition: kernel / host / plugin / pack boundary\n\nType: grilling\nStatus: resolved\nResolved: 2026-08-06\nBlocked by: 01, 03 (both resolved)\n\n## Question\n\nWhat exactly does the kernel own (mechanism + orchestration), what does the host supply (input shapes and pathways, deploy target), and what do plugins define (target policy) — and does the four-contract + pack decomposition (ElicitationPack: concept/observation/completion; ProjectionPack: projection) survive contact with the Flue facts and the brunch audit?\n\nSub-questions this grilling must close:\n\n* Are the A-axis (semantic target) and B-axis (representation target) genuinely separately swappable in our first milestone, or bundled per plugin?\n* Where does the evidence-preserving IR / claim graph live, and how thin is its common core?\n* Persistence: does the plugin-owned-persistence hypothesis hold, or does the host (deploy target) own it with plugins declaring shape? What state must the kernel externalize (session, transcript refs, artifact-in-progress, episteme ledger)?\n* Where is control inverted: typed issues as backpressure (projector → elicitation controller) — is that the only inversion, or do observation lenses invert too?\n* The policy-vs-mechanism rule: enumerate what would otherwise become the central `switch` and check each is on the plugin side.\n\nPrimary input: docs/reference/agentic-elicitation-challenges-2026-08-06T10-02-41Z.md\n\nNamed input from the portfolio decision (issue 07): **behavioral over procedural** — agents do better with behavioral guidance than procedural scripts, and with clear shapes/patterns to fill rather than schemas that require extensive parsing to build a model of the output shape. Brunch's unsolved problem — specifying how an elicitation process should work plus skill material to guide an agent through it, without over-proceduralizing — is a core stress test for the pack contract. The decomposition must say what a pack *feels like* to the agent consuming it, not only what it validates.\n\nConcrete test cases for every boundary claim (from issue 07): how would `elicit-gherkin` do this vs. `elicit-proof-obligations`? The spec mandates both packs are authored before the pack interface freezes.\n\n## Answer\n\n> Resolved by HITL grilling, 2026-08-06 (four rounds, including an evidence pass over `~/Clones/mattpocock/skills` + `../brunch/docs/design/BEHAVIORAL_KERNELS.md`, and integration of [agentic-elicitation-criteria](<../../docs/reference/agentic-elicitation-criteria-2026-08-06T14-11-18Z.md>), the second inbox doc).\n\n### Ownership table\n\n| | Owns |\n| -- | -- |\n| **Kernel** *(mechanism + orchestration)* | The conversation loop, agent-forward (agent judgment at the helm) · the questioning-UX contract (issue 05's subject) · the **capture envelope** (below) around opaque plugin payloads · the **typed issue queue** (vocabulary, storage, factual attributes; the only stored agenda-like state; also where conflict/equivalence live) · the private scratchpad · the **turn-suspension protocol** (Flue has no ask-primitive; the kernel owns one) · operation *signatures* (`observe/reconcile/project/validate`) with snapshot-in/deltas-out calling convention, validation, and application of returned deltas · completion evaluation (running plugin-declared criteria) · pack loading, progressive disclosure, kernel-card activation · capture-id minting · the **storage port** definition |\n| **Host** *(embedding + affordances)* | Input surfaces and pathways (TUI / web / chat channel / Petrinaut later) · verified respondent identity · deploy target · **storage port implementation** · artifact delivery (repo write, API, post) · model/provider via the Pi family |\n| **Plugin** *(target policy)* | Its **own IR payload structure** — graph, flat list, whatever fits; shared between its packs, never universal; namespaced concepts · **ElicitationPack**: kernel cards (Detects / Goal / contrastive Questions / Artifacts), completion contract, clarification hints · **ProjectionPack(s)**: `project` + `validate` (required), `reconcile` (optional), output contract, annotated shapes, **typed loss reports**, lossiness policy · artifact persistence *shape* · domain vocabulary |\n\n### The capture envelope (the hourglass waist)\n\nKernel-defined, domain-free — semantically rich, structurally minimal:\n\n* `id` (kernel-minted), evidence spans (utterance provenance, phrase-level where possible)\n* **Epistemic status** enum, distinct from confidence: `explicit | inferred | tentative | defaulted | external-lookup`\n* Confidence (qualitative), status: `active | superseded | retracted`, one `supersedes` link\n* **Absence states** as first-class capture values: `not-mentioned | unknown-to-user | not-yet-decided | not-applicable | explicitly-absent | declined | deferred`\n* **Alternatives** grouping: >1 live interpretation of the same evidence may coexist until resolved\n* Opaque, plugin-typed payload. **No kernel edges, no graph, no kind taxonomy** — structure is payload business. Conflict (`conflicting`) and equivalence (`possibly-equivalent`) are **typed issues referencing capture ids**, not edges; resolution must be an explicit event (supersession or recorded decision) — \"no silent conflict resolution.\"\n\n### Operations\n\n* **Required**: `project` (captures → draft artifact **+ typed loss report**: `mapped-exactly / normalized / approximate / collapsed / omitted / defaulted / unrepresentable`) and `validate` (→ typed issues).\n* **Optional**: `reconcile` (dedup/merge over the plugin's own structure); kernel calls it when present.\n* **Agent-native**: `observe` — noticing is the agent's work guided by pack kernel cards; code-level extractors are an optimization, never the required path.\n* **Calling convention**: plugin ops receive an **immutable state snapshot**, return observations/issues/deltas; the kernel validates and applies. Buys atomic plugin failure, semantically idempotent retries (a retry never counts as a second user assertion), and tracing.\n* **Backpressure**: validators and projectors never address the user; they return typed issues the agent consumes.\n\n### Dialogue policy\n\nBehavioral guidance + factual issue queue. **Facts computed, weights judged**: the kernel computes issue facts (blocks-required-criterion, origin semantic|representational, can_default); the agent weighs them qualitatively. The inbox doc's priority formula is adopted as *prose the agent thinks with*, never as a computed score — computed-priority dimensions are judgments wearing metric costumes, and a stored ranking becomes an authority the agent defers to instead of reading the conversation (brunch's no-stored-agenda lesson).\n\n### Cross-cutting decisions\n\n1. **No universal IR** — the kernel's slice is the envelope; structure is plugin-unique. Typed-entity-graph maximalism explicitly not adopted; a graph remains any plugin's private choice (including a future brunch-target plugin).\n2. **Smallest-honest-plugin test** — a flat record list + one validator must suffice; every kernel-contract addition is checked against the bar it raises. Empirical form: the black-box authoring test.\n3. **Axes separated in contract, bundled in shipping** — one ElicitationPack + N ProjectionPacks per plugin sharing the plugin's IR; swappability proven by reprojection.\n4. **Principle v2 (ratified, replaces \"behavioral over procedural\")**: *procedure for mechanism, anchors for judgment, shapes for output*. Evidence: the mattpocock skills are full of procedure that works because it is short, carried by leading words (pretrained concepts recruited deliberately), ends on checkable completion criteria, and rides on shapes/templates with progressive disclosure. Failure modes to design against: sprawl, negation-steering, no-ops, judgment-encoded-as-procedure — not procedure itself. `writing-for-agents` is the cited pack-authoring standard.\n5. **Kernel cards** (from BEHAVIORAL_KERNELS.md) are the unit of ElicitationPack content: Detects (signal-phrase activation) / Goal / contrastive Question patterns / Artifacts (typed claims emitted) / validator hooks. The fifteen-kernel ontology itself stays brunch prior art; packs declare their own kernels. Contrastive classification over open-ended essays.\n6. **Pack physical form**: kernel cards + annotated shapes + deterministic validators + small wire schemas (boundary-teaching: shallow for model legibility, deep requiredness in validators) + completion contract as checkable bounds.\n\n### Acceptance material adopted into the spec (from the criteria doc)\n\n* The **five proof obligations** as contract acceptance criteria: independent variability, semantic conservation, explicit transformation, controlled elicitation, local implementation.\n* The **ten kernel invariants** (§9) as kernel-enforced test properties (no unsupported value without provenance; no silent conflict resolution; no silent projection loss; corrections don't erase history; retries idempotent; target issues namespaced; plugin failures atomic; equivalent state → equivalent projection; unknown ≠ false; explicit ≠ inferred/defaulted).\n* Gating tests: **reprojection/projector substitution**, **minimal pairs** (\"the budget is / might be €20,000\"), **black-box authoring test** (count concepts, boilerplate, escape hatches).\n* Named smells as review vocabulary: opaque payload waist, giant context bag, schema-shaped questioning, null collapse, silent coercion/loss, correction-as-duplication, hidden target leakage.\n\n### Deferred to fog (post-milestone)\n\nSimultaneous multi-plugin composition · plugin removal · full replay · capability negotiation · version/migration machinery (the spec names the five version axes — API contract / plugin impl / concept-schema / target-schema / persisted state — implements nothing).\n\n### Routed onward\n\n* §4 SDK-machinery list (evidence anchoring, issue construction, fixtures, **local simulation harness** — \"debugging should not require reading an entire agent transcript\") → issue 06 (Shipping shape).\n* Envelope + absence states + turn-suspension → issue 05 (Questioning-UX contract).\n\n## Comments\n\n**2026-08-07 (vocabulary clarification from issue 05's resolution).** The ownership table's \"Host (embedding + affordances)\" bundled two concerns that later split: the **ui** shell (interface: rendering, input, reply transport, identity) and the **substrate** (the embedding environment: deploy target, storage-port implementation, artifact delivery, model/provider). Under the hardened lexicon (`CONTEXT.md`), this table's \"Host\" row reads as substrate concerns plus ui concerns; \"kernel\" reads as **harness**. No substantive change to the decomposition.\n+++", - "proposedBodySha256": "db79ebd21fa13286a626889b0a58932527104dc8c4413769fe313a4b5e681a8f", - "ambiguity": null, - "notes": null - }, - { - "id": "4302b30b-db51-41f6-ab3c-8fbfa97fa1fb", - "identifier": "FE-1371", - "url": "https://linear.app/hash/issue/FE-1371/questioning-ux-contract-archive", - "state": { - "name": "Done", - "type": "completed" - }, - "parent": { - "id": "a5053b51-937d-453f-9c4e-fe8e49e912ad", - "identifier": "FE-1366", - "title": "Spec the elicitation harness architecture (archived wayfinder map)" - }, - "sourceUpdatedAt": "2026-08-19T16:20:27.539Z", - "sourceTitle": "Questioning-UX contract [archive]", - "sourceDescriptionSha256": "7ec025083f39f180878790e096ec19c369a7d4120c839730dacc7fe7980cc9fb", - "sourceTitleSha256": "9c5bb4c0020f39dc129390162dbb882bf463fa6e089c5000047f64924d49f497", - "oldOuter": "Decided how structured questions live inside a free-flowing conversation: the conversation stays primary, forms and choice strips are dropped into it as interactive elements, and captured knowledge is extracted afterwards in repeatable sweeps rather than question-by-question. Also fixed the system's four-layer naming (substrate / ui / harness / plugin). Resolved 2026-08-06; part of the completed elicitation-harness planning (see parent map).", - "proposedTitle": "Define structured questions within conversation", - "proposedOuter": "This decision keeps free-flowing conversation primary while placing forms and choices within it as interactive elements. The system extracts captured knowledge through repeatable range sweeps after the conversation rather than after each question. It also establishes the four component names: platform, UI, harness, and plugin. The decision was resolved on 2026-08-06.", - "extractionMethod": "first-standalone-divider", - "innerRecord": "\n> **Archive migration (2026-08-11).** Resolved ticket from the local-markdown wayfinder effort `elicitation-kernel` (`brunch-lite:.scratch/elicitation-kernel/issues/05-questioning-ux-contract.md`), mirrored to Linear for team visibility. Full original content below, including the resolution.\n\n---\n\n# Questioning-UX contract\n\nType: grilling\nStatus: resolved\nResolved: 2026-08-07\nBlocked by: 03\n\n## Question\n\nWhat is the kernel's generic questioning-UX contract — the successor to brunch's `ask` / `present_*` / `request_*` exchange family — critiqued rather than copied?\n\nSub-questions:\n\n* Which of brunch's exchange forms earn a place in the generic contract, which generalize with changes, which are brunch-specific and stay behind?\n* What does the \"one high-value question over several low-value questions\" dialogue policy need from the UX contract (question budgets, visible current interpretation, distinguish not-mentioned/no/unknown/N-A)?\n* How do typed issues (missing/ambiguous/conflicting/invalid/unsupported/unmapped/low-confidence) render as user-facing exchanges?\n* What must the contract leave to the host surface (TUI vs. web vs. chat channel) vs. fix in the kernel?\n\nInput from Contract decomposition (issue 04): the exchange contract must carry the envelope's conversation-level semantics — **absence states** (`unknown-to-user | declined | deferred | not-applicable`… as answer outcomes, not null), **alternatives** (letting more than one interpretation stay live through an exchange), and conflict-resolution exchanges (an explicit resolution event for a `conflicting` issue). Dialogue policy is behavioral guidance + factual issue queue — the UX contract renders issues to the agent, never scores them.\n\nNote from the Flue deep-read (issue 01): Flue has **no first-class ask-the-user primitive** — the kernel must own a turn-suspension protocol (`terminate: true` tool + pending question in persistent state + structured data part; the answer arrives as a fresh dispatch). The UX contract should be designed with that as the remote rendering path (`useDataWriter` / `dynamic-tool` output parts), alongside richer local surfaces.\n\n## Answer\n\n> Resolved by HITL grilling, 2026-08-06 (five rounds), with a live fact-finding pass over Flue's docs mid-session. All shape-level commitments are **working hypotheses** per the built-artifacts-as-proofs preference (ratified into the map's Notes during this session); delegated proof obligations live in tickets 10 (walking skeleton) and 11 (logic-prototype). Vocabulary hardened via `/domain-modeling` into repo `CONTEXT.md`.\n\n### The load-bearing reframe: no exchange-pair ontology\n\nBrunch's exchange machinery (offer→terminal pairs, pending-exchange state, recovery scans) was baggage from its older agent-initiated turn-by-turn model and is **not inherited**. The free-flowing conversation is primary. When the agent poses a structured question it emits an **affordance** — a rendered enhancement in the stream, not a state machine the harness maintains. There is no \"pending exchange\" concept, hence no cardinality rule, no recovery scan, no terminal union.\n\n* Ask invocations **do commit** structured question payloads to the session (design clue: Claude Code's `AskUserQuestion` — question set as `tool_use`, selections/interruption as adjacent `tool_result`; self-contained, replayable, no separate exchange store). The answer may follow as a structured response, or the ask may be cancelled/redirected — all of it is **session evidence**.\n* **Capture is decoupled from asking**: a **range-sweep** over session entries, run on **settlement** (agent-judged, range-level — a vein closing — never per-question, which would resurrect exchange-pairs through the back door). Harness owns sweep bookkeeping (high-water mark, idempotence); agent owns settlement judgment. → ticket 11.\n* Audit lessons demoted by the reframe: *declared continuations / non-forgeability* (the failure mode shifts from forgery to misinterpretation, which the envelope's `epistemic_status: inferred` already covers; whether a widget reply needs an echo token is empirical → ticket 10); *only-answered-closes recovery* and *self-contained terminals* (patterns at most, not structure).\n\n### Shells, vocabulary, and control (hardened)\n\n**substrate** (Pi family, Flue) → **ui** (the user-interface shell: whatever affords interaction — rendering, input, reply transport; not bound to GUI/TUI) → **harness** (the generic capability layer — mechanism + orchestration; the effort's essence is *harness-engineering*; replaces \"kernel\" as shell name) → **plugin** (target policy). Control is IoC per the Hollywood principle: the plugin declares and registers; the harness discovers, orders, invokes; harness capabilities (the ask API, capture envelope, issue queue, sweep bookkeeping) reach the plugin as a **narrow injected context** — the questioning-UX contract is literally part of the PluginContext surface. Composition is the plugin's at authoring time; control flow is the harness's at runtime. Schema ownership follows capability ownership: *the shell that defines a capability owns its affordance schemas.*\n\n### What the harness fixes (the generic contract)\n\n1. **Baseline question forms**: free-text / single-choice / multi-choice, plus **questionnaire chaining** as first-class baseline (beyond brunch's `ask`). The harness owns the standard ask API and payload shapes; plugins add custom presentation/collection forms through the plugin API.\n2. **Absence**: interpret by default, afford when structured. Agent-interpreted absence from free conversation carries `epistemic_status: inferred`; structured affordances carry a one-tap absence strip (don't-know / not-applicable / decide-later — generalizing `allowNone`), keeping `unknown-to-user` vs `declined` vs `not-applicable` explicit. Transport outcome and epistemic absence never conflate.\n3. **One harness data channel** multiplexing all affordance forms (`form` tag + plugin-typed body + markdown baseline), because Flue channel names are static structural identity in a flat collision-prone namespace. Plugin widgets are progressive enhancement keyed on the form tag; hosts that know only the envelope render everything via markdown. → ticket 10.\n4. **Interpretation render** (\"visible current interpretation\"): the one affordance form that must be harness-owned, since it renders the harness's own envelope vocabulary — captures with epistemic status, absence states, live alternatives. The **plugin may supply a renderer/projector definition with typed arguments** (typed against its own payload shapes), which the harness uses to produce the ui-level view; **when it doesn't, the harness falls back to a default renderer (plain JSON view of payloads)** — keeping the renderer optional, consistent with the smallest-honest-plugin test. React vs. accept are two *capture semantics*, not exchange steps. Renderer-seam exercised once real packs exist (ticket 07's portfolio).\n5. **Issues → exchanges**: only `conflicting` / `possibly-equivalent` get forced treatment — a `conflicting` issue closes **only via an explicit resolution record** (capture-layer event citing the user's utterance as evidence); the guarantee moved from wire to store. Every other issue type renders however the agent judges best, guided by kernel cards — prescribing seven mappings would be judgment-as-procedure. → ticket 11.\n6. **No question-budget machinery**: economical interviewing is implemented through strategy and judgment guidance in pack kernel cards; with asks committed to session, anything countable is derivable — no stored counters, no ask-to-issue attribution model, no stored number for the agent to defer to.\n\n### What the ui owns\n\nRendering (zero built-in widgets in Flue — the embedding app branches on part types), reply transport, identity. Flue facts recorded for the spec: outbound is rich (Valibot-validated `data-*` parts, dynamic-tool outputs), **inbound is string-only** (`sendMessage(text, images)`; SDK signals are string-body too) — so answer typing/validation happens entirely harness-side on read-back; unknown part types are silently dropped (hence the markdown-baseline floor); one documented contradiction (data-part update-in-place vs append) needs the ticket-10 runtime check; turn suspension confirmed (`terminate: true`, answer as fresh dispatch; multi-tool batches terminate only when every result terminates).\n\n### Brunch disposition (sub-question 1)\n\n**Earns a place**: the three answer shapes as interaction vocabulary; questionnaire (promoted); absence affordances (generalized from `allowNone`); react≠accept (as capture semantics); no-stored-agenda (extended: no stored counters either). **Generalizes with changes**: named present tools → plugin-declared forms over one channel; approval-commits-atomically → the resolution-record store guarantee. **Stays behind**: the exchange-pair ontology and everything predicated on it (terminal unions, recovery scans, prev/curr/next chains, declared continuations as wire mechanism), plus everything the audit already classed brunch-specific (rubric schemas, review-set node/edge machinery, graph_refs). Audit lessons not touched by the reframe (comment-vs-message provenance, boundary-teaching schemas, hash-pinned prompt directives, private scratchpad) stand as pattern guidance for the spec.\n\n### Process decision (map-level)\n\n**Built artifacts as proofs** ratified into the map Notes: grilling tickets resolve decisions provisionally and name the proof obligations they delegate. Two prototype tickets created from this session: [Walking skeleton: Flue question round-trip](<10-walking-skeleton-flue-roundtrip.md>) and [Logic-prototype: capture sweep & settlement](<11-logic-prototype-capture-sweep.md>). Plugin lifecycle, fault containment, and contract versioning routed to [Shipping shape](<06-shipping-shape.md>).", - "innerSha256": "6abd613018b32d1ce53af2ef93b056e10c3c89ad68f43950a792f102edfb5db3", - "proposedBody": "This decision keeps free-flowing conversation primary while placing forms and choices within it as interactive elements. The system extracts captured knowledge through repeatable range sweeps after the conversation rather than after each question. It also establishes the four component names: platform, UI, harness, and plugin. The decision was resolved on 2026-08-06.\n\n+++🏗️ Agent notes\n\n> **Archive migration (2026-08-11).** Resolved ticket from the local-markdown wayfinder effort `elicitation-kernel` (`brunch-lite:.scratch/elicitation-kernel/issues/05-questioning-ux-contract.md`), mirrored to Linear for team visibility. Full original content below, including the resolution.\n\n---\n\n# Questioning-UX contract\n\nType: grilling\nStatus: resolved\nResolved: 2026-08-07\nBlocked by: 03\n\n## Question\n\nWhat is the kernel's generic questioning-UX contract — the successor to brunch's `ask` / `present_*` / `request_*` exchange family — critiqued rather than copied?\n\nSub-questions:\n\n* Which of brunch's exchange forms earn a place in the generic contract, which generalize with changes, which are brunch-specific and stay behind?\n* What does the \"one high-value question over several low-value questions\" dialogue policy need from the UX contract (question budgets, visible current interpretation, distinguish not-mentioned/no/unknown/N-A)?\n* How do typed issues (missing/ambiguous/conflicting/invalid/unsupported/unmapped/low-confidence) render as user-facing exchanges?\n* What must the contract leave to the host surface (TUI vs. web vs. chat channel) vs. fix in the kernel?\n\nInput from Contract decomposition (issue 04): the exchange contract must carry the envelope's conversation-level semantics — **absence states** (`unknown-to-user | declined | deferred | not-applicable`… as answer outcomes, not null), **alternatives** (letting more than one interpretation stay live through an exchange), and conflict-resolution exchanges (an explicit resolution event for a `conflicting` issue). Dialogue policy is behavioral guidance + factual issue queue — the UX contract renders issues to the agent, never scores them.\n\nNote from the Flue deep-read (issue 01): Flue has **no first-class ask-the-user primitive** — the kernel must own a turn-suspension protocol (`terminate: true` tool + pending question in persistent state + structured data part; the answer arrives as a fresh dispatch). The UX contract should be designed with that as the remote rendering path (`useDataWriter` / `dynamic-tool` output parts), alongside richer local surfaces.\n\n## Answer\n\n> Resolved by HITL grilling, 2026-08-06 (five rounds), with a live fact-finding pass over Flue's docs mid-session. All shape-level commitments are **working hypotheses** per the built-artifacts-as-proofs preference (ratified into the map's Notes during this session); delegated proof obligations live in tickets 10 (walking skeleton) and 11 (logic-prototype). Vocabulary hardened via `/domain-modeling` into repo `CONTEXT.md`.\n\n### The load-bearing reframe: no exchange-pair ontology\n\nBrunch's exchange machinery (offer→terminal pairs, pending-exchange state, recovery scans) was baggage from its older agent-initiated turn-by-turn model and is **not inherited**. The free-flowing conversation is primary. When the agent poses a structured question it emits an **affordance** — a rendered enhancement in the stream, not a state machine the harness maintains. There is no \"pending exchange\" concept, hence no cardinality rule, no recovery scan, no terminal union.\n\n* Ask invocations **do commit** structured question payloads to the session (design clue: Claude Code's `AskUserQuestion` — question set as `tool_use`, selections/interruption as adjacent `tool_result`; self-contained, replayable, no separate exchange store). The answer may follow as a structured response, or the ask may be cancelled/redirected — all of it is **session evidence**.\n* **Capture is decoupled from asking**: a **range-sweep** over session entries, run on **settlement** (agent-judged, range-level — a vein closing — never per-question, which would resurrect exchange-pairs through the back door). Harness owns sweep bookkeeping (high-water mark, idempotence); agent owns settlement judgment. → ticket 11.\n* Audit lessons demoted by the reframe: *declared continuations / non-forgeability* (the failure mode shifts from forgery to misinterpretation, which the envelope's `epistemic_status: inferred` already covers; whether a widget reply needs an echo token is empirical → ticket 10); *only-answered-closes recovery* and *self-contained terminals* (patterns at most, not structure).\n\n### Shells, vocabulary, and control (hardened)\n\n**substrate** (Pi family, Flue) → **ui** (the user-interface shell: whatever affords interaction — rendering, input, reply transport; not bound to GUI/TUI) → **harness** (the generic capability layer — mechanism + orchestration; the effort's essence is *harness-engineering*; replaces \"kernel\" as shell name) → **plugin** (target policy). Control is IoC per the Hollywood principle: the plugin declares and registers; the harness discovers, orders, invokes; harness capabilities (the ask API, capture envelope, issue queue, sweep bookkeeping) reach the plugin as a **narrow injected context** — the questioning-UX contract is literally part of the PluginContext surface. Composition is the plugin's at authoring time; control flow is the harness's at runtime. Schema ownership follows capability ownership: *the shell that defines a capability owns its affordance schemas.*\n\n### What the harness fixes (the generic contract)\n\n1. **Baseline question forms**: free-text / single-choice / multi-choice, plus **questionnaire chaining** as first-class baseline (beyond brunch's `ask`). The harness owns the standard ask API and payload shapes; plugins add custom presentation/collection forms through the plugin API.\n2. **Absence**: interpret by default, afford when structured. Agent-interpreted absence from free conversation carries `epistemic_status: inferred`; structured affordances carry a one-tap absence strip (don't-know / not-applicable / decide-later — generalizing `allowNone`), keeping `unknown-to-user` vs `declined` vs `not-applicable` explicit. Transport outcome and epistemic absence never conflate.\n3. **One harness data channel** multiplexing all affordance forms (`form` tag + plugin-typed body + markdown baseline), because Flue channel names are static structural identity in a flat collision-prone namespace. Plugin widgets are progressive enhancement keyed on the form tag; hosts that know only the envelope render everything via markdown. → ticket 10.\n4. **Interpretation render** (\"visible current interpretation\"): the one affordance form that must be harness-owned, since it renders the harness's own envelope vocabulary — captures with epistemic status, absence states, live alternatives. The **plugin may supply a renderer/projector definition with typed arguments** (typed against its own payload shapes), which the harness uses to produce the ui-level view; **when it doesn't, the harness falls back to a default renderer (plain JSON view of payloads)** — keeping the renderer optional, consistent with the smallest-honest-plugin test. React vs. accept are two *capture semantics*, not exchange steps. Renderer-seam exercised once real packs exist (ticket 07's portfolio).\n5. **Issues → exchanges**: only `conflicting` / `possibly-equivalent` get forced treatment — a `conflicting` issue closes **only via an explicit resolution record** (capture-layer event citing the user's utterance as evidence); the guarantee moved from wire to store. Every other issue type renders however the agent judges best, guided by kernel cards — prescribing seven mappings would be judgment-as-procedure. → ticket 11.\n6. **No question-budget machinery**: economical interviewing is implemented through strategy and judgment guidance in pack kernel cards; with asks committed to session, anything countable is derivable — no stored counters, no ask-to-issue attribution model, no stored number for the agent to defer to.\n\n### What the ui owns\n\nRendering (zero built-in widgets in Flue — the embedding app branches on part types), reply transport, identity. Flue facts recorded for the spec: outbound is rich (Valibot-validated `data-*` parts, dynamic-tool outputs), **inbound is string-only** (`sendMessage(text, images)`; SDK signals are string-body too) — so answer typing/validation happens entirely harness-side on read-back; unknown part types are silently dropped (hence the markdown-baseline floor); one documented contradiction (data-part update-in-place vs append) needs the ticket-10 runtime check; turn suspension confirmed (`terminate: true`, answer as fresh dispatch; multi-tool batches terminate only when every result terminates).\n\n### Brunch disposition (sub-question 1)\n\n**Earns a place**: the three answer shapes as interaction vocabulary; questionnaire (promoted); absence affordances (generalized from `allowNone`); react≠accept (as capture semantics); no-stored-agenda (extended: no stored counters either). **Generalizes with changes**: named present tools → plugin-declared forms over one channel; approval-commits-atomically → the resolution-record store guarantee. **Stays behind**: the exchange-pair ontology and everything predicated on it (terminal unions, recovery scans, prev/curr/next chains, declared continuations as wire mechanism), plus everything the audit already classed brunch-specific (rubric schemas, review-set node/edge machinery, graph_refs). Audit lessons not touched by the reframe (comment-vs-message provenance, boundary-teaching schemas, hash-pinned prompt directives, private scratchpad) stand as pattern guidance for the spec.\n\n### Process decision (map-level)\n\n**Built artifacts as proofs** ratified into the map Notes: grilling tickets resolve decisions provisionally and name the proof obligations they delegate. Two prototype tickets created from this session: [Walking skeleton: Flue question round-trip](<10-walking-skeleton-flue-roundtrip.md>) and [Logic-prototype: capture sweep & settlement](<11-logic-prototype-capture-sweep.md>). Plugin lifecycle, fault containment, and contract versioning routed to [Shipping shape](<06-shipping-shape.md>).\n+++", - "proposedBodySha256": "cbe10c07bcba59bfe3982e316635a10ad1c18893cf1df8b56ecb04f4bb589580", - "ambiguity": null, - "notes": null - }, - { - "id": "dc49b036-3e7f-4663-85c9-64fa741c64f8", - "identifier": "FE-1372", - "url": "https://linear.app/hash/issue/FE-1372/shipping-shape-kernel-library-vs-flue-agent-archive", - "state": { - "name": "Done", - "type": "completed" - }, - "parent": { - "id": "a5053b51-937d-453f-9c4e-fe8e49e912ad", - "identifier": "FE-1366", - "title": "Spec the elicitation harness architecture (archived wayfinder map)" - }, - "sourceUpdatedAt": "2026-08-19T16:20:27.583Z", - "sourceTitle": "Shipping shape: kernel library vs. Flue agent [archive]", - "sourceDescriptionSha256": "b0f7c959583b663416d505a36c08f6db34f037d1bf8842b950cddce5797462c5", - "sourceTitleSha256": "1dab9baee26c9752955fa86d601f08aed2a362b5cf4c721084b6f2259ab68b68", - "oldOuter": "Decided what actually ships: a harness library inside a thin host-authored agent — not a standalone product — organized as a workspace monorepo with the plugin SDK as its public surface, and tested by generated, replayable interview fixtures rather than live model calls. Resolved 2026-08-08; part of the completed elicitation-harness planning (see parent map).", - "proposedTitle": "Choose how the elicitation harness ships", - "proposedOuter": "This decision makes the deliverable a harness library inside a thin host-authored agent rather than a standalone product. A workspace monorepo exposes the plugin SDK, and generated replayable interview fixtures test the system without live model calls. The decision was resolved on 2026-08-08.", - "extractionMethod": "first-standalone-divider", - "innerRecord": "\n> **Archive migration (2026-08-11).** Resolved ticket from the local-markdown wayfinder effort `elicitation-kernel` (`brunch-lite:.scratch/elicitation-kernel/issues/06-shipping-shape.md`), mirrored to Linear for team visibility. Full original content below, including the resolution.\n\n---\n\n# Shipping shape: kernel library vs. Flue agent\n\nType: grilling\nStatus: resolved\nResolved: 2026-08-07\nBlocked by: 01, 04\n\n## Question\n\nWhat does the carve-out physically ship as — a kernel library that a thin Flue agent (and later Petrinaut/web/brunch hosts) embeds, or a Flue agent as the product itself — and what is the viable/ideal package structure?\n\nSub-questions:\n\n* Given the Flue deep-read: is library-embedded-in-agent natural in Flue, or fighting the framework?\n* What does each option cost the Petrinaut-UI and web-UI futures?\n* Package topology: one package or kernel + packs as separate packages? Where do dev targets (elicit-gherkin, elicit-lean) live?\n* What is the local dev loop (run against both targets) vs. the remote deploy story?\n\nInput from Contract decomposition (issue 04): the plugin **SDK surface** is part of the shipping shape — standard machinery for evidence anchoring, claim identity, issue construction, schema validation, retries, idempotency, state-delta application, tracing, test fixtures, and a **local simulation harness** (fixture-driven pack testing: conversation in → expected claims/issues/projections out; \"debugging should not require reading an entire agent transcript\"). The black-box authoring test and change-surface metric are the acceptance bar.\n\n## Answer\n\n> Resolved by HITL grilling, 2026-08-07 (two rounds + a testing-strategy revision pass grounded in `expert-property-based-testing`).\n\n### Root: harness library in a thin host-authored agent — ratified, with the second-binding test\n\nThe product is the **harness library** (core + packs/plugins); every host authors its own thin `'use agent'` module, `app.ts` mount, and storage adapter, and calls a hook shaped like `useElicitation(plugin)`. The Flue facts make the alternative structurally unavailable anyway (build-time `'use agent'` scan lives in the consuming project; a library cannot ship a pre-registered agent). A runnable reference app ships alongside as a dev/demo vehicle, not as the product.\n\n**Amendment — portability as a named pressure test, not a build target.** The deliverable decomposes into (1) pure runtime mechanism, (2) a tool surface, (3) prompt/skill material — and (3) is already portable for free (Open Agent Skills format is shared by Flue, Claude Code, and Pi-family harnesses). The substrate-agnostic-core non-goal stands as written (no ports for hypothetical consumers, no second maintained binding), but the decomposition must keep a second binding *demonstrably small*:\n\n* The spec **enumerates the substrate-facing surface as a short named capability list** the binding must supply: register a tool · contribute instructions · persist state · emit an affordance payload · suspend-for-reply · private model call. Porting = reimplementing that list; if it grows exotic Flue-shaped entries, that is an early smell signal.\n* **Second-binding test** (sibling to smallest-honest-plugin, adopted into spec acceptance material): every time mechanism wants to land in the binding rather than the core, ask \"is this genuinely substrate-specific, or is mechanism leaking into Flue's dialect?\"\n* Binding-size asymmetry is expected, not a failure: Flue's binding carries the turn-suspension compensation (ticket 10); a terminal binding gets ask-the-user nearly free but may only afford the questioning-UX markdown floor (which issue 05 already fixed as universal). Claude Code/Codex would be the awkward cousins (out-of-process: MCP server + skills dir). The core stays identical; each binding absorbs what its substrate lacks or forbids.\n* **Binding** entered the glossary (`CONTEXT.md`): the harness defines the capability list; a binding imports both harness and substrate; the harness imports no substrate.\n\n### Package topology\n\n* **Core and Flue binding as two workspace packages from day one.** The package boundary is the enforcement mechanism for the portability property; extracting a subpath later is visible churn. Acknowledged as mild ceremony now — accepted because the cost is low if kept clean.\n* **Monorepo in this repo** (brunch-lite becomes the workspace; rename is cheap once the real name resolves). Bun workspaces: `packages/core`, `packages/flue` (binding), `packages/plugin-gherkin`, `packages/plugin-proof-obligations`, `apps/dev`. The dev app owns the `'use agent'` module, `app.ts`, `db.ts`, and the Vite build Flue requires. Spec records the layout as intended structure; nothing is scaffolded during this map.\n* **Plugin packages are** `plugin-*`**, not** `elicit-*` — the prefix names what they are architecturally; \"elicit\" is the function, not the identity.\n* **Plugin SDK is core's public export surface** (authoring types + machinery; test/fixture machinery on a `core/testing` subpath so prod bundles stay clean). A separate SDK package would re-export core with no seam-value.\n* **Dependency rule, stated as a spec invariant: plugins depend on** `core` **only** — never on the binding, never on Flue. Every plugin is substrate-portable by construction, and the black-box authoring test stays honest (a plugin author's world is one package's exports).\n* **Envisioned horizon** (named, not built): per-substrate binding packages (`flue-`, `pi-`, `codex-`), same harness inside each. The payoff *if the second-binding test keeps passing*, not a commitment.\n\n### Cross-cutting choices\n\n* **Valibot throughout** — Flue locks it at every boundary; Standard-Schema-at-the-waist would buy plugin-author comfort at the cost of a conversion seam that can silently drop constraints (named smell: silent coercion/loss).\n* **Tool namespacing: prefix derived from the product name**, provisionally `bl_*` — never `elicit_*` (function vs. identity again). Core names ops abstractly; the binding renders them as substrate tool names. All model-facing tools are harness-owned (plugins expose ops, not tools).\n* **Naming principle** (recurring, carried forward): architectural strings name *identity* (product, role), not *function*; the name-fog eventually resolves every one of these strings, so nothing bakes \"elicit\" or \"brunch\" into structure.\n* **Publishing posture: workspace-internal** — no npm publishing until the real name resolves and an external consumer exists. The spec describes the publishable shape; publishing waits.\n\n### Testing strategy: generation-first fixtures, deterministic replay\n\nThe scripted deterministic driver (ticket 11's headless-driver pattern) is the **execution/replay layer**: fixtures are data, replay is pure, everything runs in plain `bun test` — no model, no substrate. But hand-written fixtures demote to **seeds**; the corpus is generated, answering the two untruthfulness modes:\n\n1. **Circularity** (fixtures tailored to the plugin-as-written): properties come from the **kernel contract** — the ten kernel invariants (ticket 04) are literally properties (re-sweep idempotence, equivalent-state → equivalent-projection, corrections don't erase history, retries idempotent, …) — and generators come from the **plugin's declarations**, never its implementation. The SDK ships `arbitraryFromSchema` (Valibot → fast-check arbitraries) for generated capture populations, plus negative-space properties for plugin code (validators total: never throw, always typed issues; `project` never emits an undeclared loss category).\n2. **Unrealistic conversational dynamics**: (i) most invariants hold over capture/state space directly — no conversation needed; (ii) where dynamics are the subject (sweep/settlement, supersession, absences), **model-based command-sequence testing** (`fc.commands`) over a small command alphabet — utter · settle-range · sweep · correct · contradict · reply-with-absence · redirect — derived from the envelope vocabulary; the fuzzer explores interleavings no hand-scripted conversation contains; (iii) language realism via a **model as offline generator, never CI oracle**: a model plays the respondent against the plugin's own kernel cards (Detects/Questions = targeting spec), varied by persona/curveball, plus a **mutation library** generalizing the minimal-pairs test (epistemic-status flips, absence injections, supersession injections). Outputs freeze as replayable fixture files; **regenerate when declarations change** (the anti-drift mechanism).\n\nBonus adopted: shrunk counterexamples from broken invariants *are* minimal pathological conversations — pinned as regressions and read first as type-design feedback on envelope/payload types. SDK surface therefore includes: schema-driven arbitraries, the command alphabet, mutation operators, fixture freeze/replay format — alongside ticket 04's list (evidence anchoring, capture identity, issue construction, retries, tracing).\n\n### Dev loop, demo, deploy\n\n* **One agent per target** in one dev app (`ElicitGherkin`, `ElicitProofObligations`): static per-agent tool sets (Flue cache economics), and the shape Cloudflare forces later anyway (build-time agent set; plugin choice is conversation-lifetime-immutable via `initialData` regardless).\n* **The dev app is chartered with three roles, spec'd as roles not features**: (1) local dev loop against both plugins; (2) the colleague-facing **target-gallery demo** — parallel tabbed sessions: start a BDD-spec elicitation, open another tab for a proof obligation, another for a process model; (3) the diagnostic/probe surface — provisional affordance renderers now (the deferred UI package's exploratory material), exploded-view instrumented readout when it graduates. Demo-polish and probe-internals pull opposite ways, so they are different views/routes of one app.\n* **UI affordance package deferred**: spec names it as intended (React renderers + reply transport over `@flue/react`; non-React hosts build on `@flue/sdk`), milestone one keeps renderers in the dev app.\n* **Remote deploy: milestone one is local-only; the spec pins the remote-parity constraints** (one-agent-many-conversations, pinned `agentName`, host-owned storage port, no dynamic agent creation) so nothing local-only creeps in. CI smoke = `vite build` + the simulation suite (no model key, no flake); an optional secret-gated real-model `flue run` smoke once a provider key exists. Actual deploy-target choice waits on an infra conversation (user, 2026-08-07) and blocks nothing on this map.\n\n## Comments\n\n**2026-08-07 (context pointer, from issue 11's cleanup).** The prototype branches are the implementation seeds for this topology, kept as primary sources out of main: `prototype/11-capture-sweep` holds the pure capture-layer reducer (no DOM, envelope semantics only) whose behavior `packages/core`'s capture module lifts *as semantics, not code-by-copy*, plus a headless driver whose 32 checks seed the invariant-property suite described under Testing strategy (several are already kernel-invariant instances: re-sweep idempotence, atomic sweep application, corrections-don't-erase-history, single-hop supersession). Likewise `prototype/10-flue-roundtrip` is the sketch `packages/flue` starts from. Working-tree copies were deleted after HITL review; retrieve any file with e.g. `git show prototype/11-capture-sweep:.scratch/elicitation-kernel/prototypes/11-capture-sweep.html`.\n\n**2026-08-10 (amendment, from [Multi-session elicitation & durable target state](<12-multi-session-durable-target.md>)).** The charter's persistence hypothesis resolved: **flipped** — the storage port is harness-defined and binding-implemented, plugin-blind; plugin-addressable storage exists only as harness-defined methods passed through the injected PluginContext. This sharpens two things already in this ticket: the capability-list entry \"persist state\" and the remote-parity constraint \"host-owned storage port\". Additionally, evidence spans carry pointer + quoted excerpt, so capture provenance is self-contained regardless of how a deploy target exposes substrate session logs.\n\n**2026-08-10 (amendment, second-target rename).** Per the [Formal-verification canon survey](<09-formal-verification-canon-survey.md>)'s category-error verdict, the second target is now the **assurance argument**; the topology's `packages/plugin-proof-obligations` reads `packages/plugin-assurance`.", - "innerSha256": "92eed4355aa2a82a0ba5d4e65d53ac27de767087bbce2f98c387b70f0d9e3478", - "proposedBody": "This decision makes the deliverable a harness library inside a thin host-authored agent rather than a standalone product. A workspace monorepo exposes the plugin SDK, and generated replayable interview fixtures test the system without live model calls. The decision was resolved on 2026-08-08.\n\n+++🏗️ Agent notes\n\n> **Archive migration (2026-08-11).** Resolved ticket from the local-markdown wayfinder effort `elicitation-kernel` (`brunch-lite:.scratch/elicitation-kernel/issues/06-shipping-shape.md`), mirrored to Linear for team visibility. Full original content below, including the resolution.\n\n---\n\n# Shipping shape: kernel library vs. Flue agent\n\nType: grilling\nStatus: resolved\nResolved: 2026-08-07\nBlocked by: 01, 04\n\n## Question\n\nWhat does the carve-out physically ship as — a kernel library that a thin Flue agent (and later Petrinaut/web/brunch hosts) embeds, or a Flue agent as the product itself — and what is the viable/ideal package structure?\n\nSub-questions:\n\n* Given the Flue deep-read: is library-embedded-in-agent natural in Flue, or fighting the framework?\n* What does each option cost the Petrinaut-UI and web-UI futures?\n* Package topology: one package or kernel + packs as separate packages? Where do dev targets (elicit-gherkin, elicit-lean) live?\n* What is the local dev loop (run against both targets) vs. the remote deploy story?\n\nInput from Contract decomposition (issue 04): the plugin **SDK surface** is part of the shipping shape — standard machinery for evidence anchoring, claim identity, issue construction, schema validation, retries, idempotency, state-delta application, tracing, test fixtures, and a **local simulation harness** (fixture-driven pack testing: conversation in → expected claims/issues/projections out; \"debugging should not require reading an entire agent transcript\"). The black-box authoring test and change-surface metric are the acceptance bar.\n\n## Answer\n\n> Resolved by HITL grilling, 2026-08-07 (two rounds + a testing-strategy revision pass grounded in `expert-property-based-testing`).\n\n### Root: harness library in a thin host-authored agent — ratified, with the second-binding test\n\nThe product is the **harness library** (core + packs/plugins); every host authors its own thin `'use agent'` module, `app.ts` mount, and storage adapter, and calls a hook shaped like `useElicitation(plugin)`. The Flue facts make the alternative structurally unavailable anyway (build-time `'use agent'` scan lives in the consuming project; a library cannot ship a pre-registered agent). A runnable reference app ships alongside as a dev/demo vehicle, not as the product.\n\n**Amendment — portability as a named pressure test, not a build target.** The deliverable decomposes into (1) pure runtime mechanism, (2) a tool surface, (3) prompt/skill material — and (3) is already portable for free (Open Agent Skills format is shared by Flue, Claude Code, and Pi-family harnesses). The substrate-agnostic-core non-goal stands as written (no ports for hypothetical consumers, no second maintained binding), but the decomposition must keep a second binding *demonstrably small*:\n\n* The spec **enumerates the substrate-facing surface as a short named capability list** the binding must supply: register a tool · contribute instructions · persist state · emit an affordance payload · suspend-for-reply · private model call. Porting = reimplementing that list; if it grows exotic Flue-shaped entries, that is an early smell signal.\n* **Second-binding test** (sibling to smallest-honest-plugin, adopted into spec acceptance material): every time mechanism wants to land in the binding rather than the core, ask \"is this genuinely substrate-specific, or is mechanism leaking into Flue's dialect?\"\n* Binding-size asymmetry is expected, not a failure: Flue's binding carries the turn-suspension compensation (ticket 10); a terminal binding gets ask-the-user nearly free but may only afford the questioning-UX markdown floor (which issue 05 already fixed as universal). Claude Code/Codex would be the awkward cousins (out-of-process: MCP server + skills dir). The core stays identical; each binding absorbs what its substrate lacks or forbids.\n* **Binding** entered the glossary (`CONTEXT.md`): the harness defines the capability list; a binding imports both harness and substrate; the harness imports no substrate.\n\n### Package topology\n\n* **Core and Flue binding as two workspace packages from day one.** The package boundary is the enforcement mechanism for the portability property; extracting a subpath later is visible churn. Acknowledged as mild ceremony now — accepted because the cost is low if kept clean.\n* **Monorepo in this repo** (brunch-lite becomes the workspace; rename is cheap once the real name resolves). Bun workspaces: `packages/core`, `packages/flue` (binding), `packages/plugin-gherkin`, `packages/plugin-proof-obligations`, `apps/dev`. The dev app owns the `'use agent'` module, `app.ts`, `db.ts`, and the Vite build Flue requires. Spec records the layout as intended structure; nothing is scaffolded during this map.\n* **Plugin packages are** `plugin-*`**, not** `elicit-*` — the prefix names what they are architecturally; \"elicit\" is the function, not the identity.\n* **Plugin SDK is core's public export surface** (authoring types + machinery; test/fixture machinery on a `core/testing` subpath so prod bundles stay clean). A separate SDK package would re-export core with no seam-value.\n* **Dependency rule, stated as a spec invariant: plugins depend on** `core` **only** — never on the binding, never on Flue. Every plugin is substrate-portable by construction, and the black-box authoring test stays honest (a plugin author's world is one package's exports).\n* **Envisioned horizon** (named, not built): per-substrate binding packages (`flue-`, `pi-`, `codex-`), same harness inside each. The payoff *if the second-binding test keeps passing*, not a commitment.\n\n### Cross-cutting choices\n\n* **Valibot throughout** — Flue locks it at every boundary; Standard-Schema-at-the-waist would buy plugin-author comfort at the cost of a conversion seam that can silently drop constraints (named smell: silent coercion/loss).\n* **Tool namespacing: prefix derived from the product name**, provisionally `bl_*` — never `elicit_*` (function vs. identity again). Core names ops abstractly; the binding renders them as substrate tool names. All model-facing tools are harness-owned (plugins expose ops, not tools).\n* **Naming principle** (recurring, carried forward): architectural strings name *identity* (product, role), not *function*; the name-fog eventually resolves every one of these strings, so nothing bakes \"elicit\" or \"brunch\" into structure.\n* **Publishing posture: workspace-internal** — no npm publishing until the real name resolves and an external consumer exists. The spec describes the publishable shape; publishing waits.\n\n### Testing strategy: generation-first fixtures, deterministic replay\n\nThe scripted deterministic driver (ticket 11's headless-driver pattern) is the **execution/replay layer**: fixtures are data, replay is pure, everything runs in plain `bun test` — no model, no substrate. But hand-written fixtures demote to **seeds**; the corpus is generated, answering the two untruthfulness modes:\n\n1. **Circularity** (fixtures tailored to the plugin-as-written): properties come from the **kernel contract** — the ten kernel invariants (ticket 04) are literally properties (re-sweep idempotence, equivalent-state → equivalent-projection, corrections don't erase history, retries idempotent, …) — and generators come from the **plugin's declarations**, never its implementation. The SDK ships `arbitraryFromSchema` (Valibot → fast-check arbitraries) for generated capture populations, plus negative-space properties for plugin code (validators total: never throw, always typed issues; `project` never emits an undeclared loss category).\n2. **Unrealistic conversational dynamics**: (i) most invariants hold over capture/state space directly — no conversation needed; (ii) where dynamics are the subject (sweep/settlement, supersession, absences), **model-based command-sequence testing** (`fc.commands`) over a small command alphabet — utter · settle-range · sweep · correct · contradict · reply-with-absence · redirect — derived from the envelope vocabulary; the fuzzer explores interleavings no hand-scripted conversation contains; (iii) language realism via a **model as offline generator, never CI oracle**: a model plays the respondent against the plugin's own kernel cards (Detects/Questions = targeting spec), varied by persona/curveball, plus a **mutation library** generalizing the minimal-pairs test (epistemic-status flips, absence injections, supersession injections). Outputs freeze as replayable fixture files; **regenerate when declarations change** (the anti-drift mechanism).\n\nBonus adopted: shrunk counterexamples from broken invariants *are* minimal pathological conversations — pinned as regressions and read first as type-design feedback on envelope/payload types. SDK surface therefore includes: schema-driven arbitraries, the command alphabet, mutation operators, fixture freeze/replay format — alongside ticket 04's list (evidence anchoring, capture identity, issue construction, retries, tracing).\n\n### Dev loop, demo, deploy\n\n* **One agent per target** in one dev app (`ElicitGherkin`, `ElicitProofObligations`): static per-agent tool sets (Flue cache economics), and the shape Cloudflare forces later anyway (build-time agent set; plugin choice is conversation-lifetime-immutable via `initialData` regardless).\n* **The dev app is chartered with three roles, spec'd as roles not features**: (1) local dev loop against both plugins; (2) the colleague-facing **target-gallery demo** — parallel tabbed sessions: start a BDD-spec elicitation, open another tab for a proof obligation, another for a process model; (3) the diagnostic/probe surface — provisional affordance renderers now (the deferred UI package's exploratory material), exploded-view instrumented readout when it graduates. Demo-polish and probe-internals pull opposite ways, so they are different views/routes of one app.\n* **UI affordance package deferred**: spec names it as intended (React renderers + reply transport over `@flue/react`; non-React hosts build on `@flue/sdk`), milestone one keeps renderers in the dev app.\n* **Remote deploy: milestone one is local-only; the spec pins the remote-parity constraints** (one-agent-many-conversations, pinned `agentName`, host-owned storage port, no dynamic agent creation) so nothing local-only creeps in. CI smoke = `vite build` + the simulation suite (no model key, no flake); an optional secret-gated real-model `flue run` smoke once a provider key exists. Actual deploy-target choice waits on an infra conversation (user, 2026-08-07) and blocks nothing on this map.\n\n## Comments\n\n**2026-08-07 (context pointer, from issue 11's cleanup).** The prototype branches are the implementation seeds for this topology, kept as primary sources out of main: `prototype/11-capture-sweep` holds the pure capture-layer reducer (no DOM, envelope semantics only) whose behavior `packages/core`'s capture module lifts *as semantics, not code-by-copy*, plus a headless driver whose 32 checks seed the invariant-property suite described under Testing strategy (several are already kernel-invariant instances: re-sweep idempotence, atomic sweep application, corrections-don't-erase-history, single-hop supersession). Likewise `prototype/10-flue-roundtrip` is the sketch `packages/flue` starts from. Working-tree copies were deleted after HITL review; retrieve any file with e.g. `git show prototype/11-capture-sweep:.scratch/elicitation-kernel/prototypes/11-capture-sweep.html`.\n\n**2026-08-10 (amendment, from [Multi-session elicitation & durable target state](<12-multi-session-durable-target.md>)).** The charter's persistence hypothesis resolved: **flipped** — the storage port is harness-defined and binding-implemented, plugin-blind; plugin-addressable storage exists only as harness-defined methods passed through the injected PluginContext. This sharpens two things already in this ticket: the capability-list entry \"persist state\" and the remote-parity constraint \"host-owned storage port\". Additionally, evidence spans carry pointer + quoted excerpt, so capture provenance is self-contained regardless of how a deploy target exposes substrate session logs.\n\n**2026-08-10 (amendment, second-target rename).** Per the [Formal-verification canon survey](<09-formal-verification-canon-survey.md>)'s category-error verdict, the second target is now the **assurance argument**; the topology's `packages/plugin-proof-obligations` reads `packages/plugin-assurance`.\n+++", - "proposedBodySha256": "5c432f49fb3ad02e7c3e3a20df7fb171ea71204b1176c89ebafdcb40459b8688", - "ambiguity": null, - "notes": null - }, - { - "id": "4b48c94d-9bf0-4c40-beeb-7c5192117580", - "identifier": "FE-1373", - "url": "https://linear.app/hash/issue/FE-1373/dev-target-portfolio-confirmation-archive", - "state": { - "name": "Done", - "type": "completed" - }, - "parent": { - "id": "a5053b51-937d-453f-9c4e-fe8e49e912ad", - "identifier": "FE-1366", - "title": "Spec the elicitation harness architecture (archived wayfinder map)" - }, - "sourceUpdatedAt": "2026-08-19T16:20:27.618Z", - "sourceTitle": "Dev-target portfolio confirmation [archive]", - "sourceDescriptionSha256": "a1a499c06b23735a1b16cabcc6c787f35c1e3fda3af30f09c87927173b54f426", - "sourceTitleSha256": "f716acc84bd7e8b18c26d9f5020804c103bd5a28cd5f957f643d47885903a973", - "oldOuter": "Confirmed the two practice subjects the architecture is built against — Gherkin scenarios and assurance arguments (BPMN third) — because building two very different subjects at once forces the plugin model to stay genuinely general. Resolved 2026-08-07; part of the completed elicitation-harness planning (see parent map).", - "proposedTitle": "Choose the initial elicitation subjects", - "proposedOuter": "This decision selects Gherkin scenarios and assurance arguments as the two initial subjects, with BPMN third. Developing two different subjects together tests whether the plugin model remains general. The decision was resolved on 2026-08-07.", - "extractionMethod": "first-standalone-divider", - "innerRecord": "\n> **Archive migration (2026-08-11).** Resolved ticket from the local-markdown wayfinder effort `elicitation-kernel` (`brunch-lite:.scratch/elicitation-kernel/issues/07-dev-target-portfolio.md`), mirrored to Linear for team visibility. Full original content below, including the resolution.\n\n---\n\n# Dev-target portfolio confirmation\n\nType: grilling\nStatus: resolved\nResolved: 2026-08-06\nBlocked by: 02\n\n> **Rename note (2026-08-10, spec assembly):** occurrences of `elicit-proof-obligations` below are the historical name; the second target is the **assurance argument**, package `plugin-assurance` (per the [Formal-verification canon survey](<09-formal-verification-canon-survey.md>)'s category-error verdict). Also superseded here: the four-rung status ladder is demoted to a derived UI label with the assumption ledger as headline (pre-pass S8), and the Geolog/ARIA adjacency guess is refuted (pre-pass S9).\n\n## Question\n\nConfirm the first milestone's two live dev targets. The zil-lean survey resolved the shape of the second: full elicit-lean is **not** dev-sized (writing Lean statements from intent is the deep-expertise step), but the **elicit-proof-obligations** slice is — capture an assumption/lemma/theorem/guarantee dependency graph with criticality and evidence refs, validated by acyclicity + Datalog closure (no Lean statements, no proofs), per the ElicitationPack/ProjectionPack sketches in the survey answer. The survey judges it a *better* second target than BPMN on both pack axes; BPMN stays third.\n\nProposed portfolio to confirm: **elicit-gherkin** (tracer) + **elicit-proof-obligations** (second, forces the pack swap and the evidence-graded IR).\n\n## Answer\n\n> Resolved by HITL grilling, 2026-08-06.\n\n**Portfolio confirmed**: `elicit-gherkin` + `elicit-proof-obligations` are the first milestone's two live dev targets; `elicit-BPMN/process-mining` is named third; full elicit-lean is deferred (writing Lean statements from intent is the deep-expertise step zil-lean itself never attempts).\n\n**Order — hybrid, addressing the real risk at the design layer**: the spec mandates that **both packs are authored before the pack interface freezes** (design against both simultaneously, on paper — the \"two targets on each axis from the beginning\" rule), while **elicit-gherkin wires end-to-end first** as the cheap mechanism proof, with elicit-proof-obligations immediately after. Rationale: the user's identified risk — brunch failed to find a scalable, modular way to specify an elicitation process plus guiding skill material without over-proceduralizing — lives in interface design, not wiring order. The trivial target must not freeze the pack contract before the hard target has stressed it.\n\n**Design principle surfaced (routed to Contract decomposition as a named input)**: agents do better with *behavioral* guidance than procedural, and with *clear shapes/patterns to fill* rather than schemas that require extensive parsing to build a model of the output shape. Packs are shapes-to-fill plus behavioral guidance — not procedural scripts, not parse-heavy schemas.\n\n**Proof-obligations output format**: steal the ideas, own the format. Adopt zil-lean's load-bearing vocabulary (evidence-graded assurance lattice with prohibited promotions; PROVED/CONDITIONAL/WEAK/BROKEN status ladder; acyclicity + Datalog-closure validation) in our own claim-DAG serialization, **hewing to whatever existing canon fits** — Dafny's `requires/ensures/invariant` contract vocabulary is the leading candidate; Geolog (ARIA program, axioms addressed via Datalog-like queries) is plausibly adjacent. Grounding this is the new **Formal-verification canon survey** ticket (09), which blocks Assemble-the-spec so the milestone lands canon-grounded. `.zc` export is someday-maybe; no dependency on the unproven zil-lean repo.\n\n**Gherkin validator depth (milestone one)**: parse validity + optional **pack-declared step-lexicon** binding check. The apparent codebase coupling dissolves: a step lexicon carried as pack policy needs no external project; only live-codebase step binding defers, named as the target's growth path.\n\nSub-questions:\n\n* Do the two chosen targets differ materially on *both* pack axes (semantic + representation)?\n* What is each target's smallest honest output contract for milestone one?\n* Which target is the tracer (built first) and which trails to force the second-pack swap?", - "innerSha256": "af4bb40e8c7c41c4f1c03c942e33f45a93b4934d3850af95029ff474e268a702", - "proposedBody": "This decision selects Gherkin scenarios and assurance arguments as the two initial subjects, with BPMN third. Developing two different subjects together tests whether the plugin model remains general. The decision was resolved on 2026-08-07.\n\n+++🏗️ Agent notes\n\n> **Archive migration (2026-08-11).** Resolved ticket from the local-markdown wayfinder effort `elicitation-kernel` (`brunch-lite:.scratch/elicitation-kernel/issues/07-dev-target-portfolio.md`), mirrored to Linear for team visibility. Full original content below, including the resolution.\n\n---\n\n# Dev-target portfolio confirmation\n\nType: grilling\nStatus: resolved\nResolved: 2026-08-06\nBlocked by: 02\n\n> **Rename note (2026-08-10, spec assembly):** occurrences of `elicit-proof-obligations` below are the historical name; the second target is the **assurance argument**, package `plugin-assurance` (per the [Formal-verification canon survey](<09-formal-verification-canon-survey.md>)'s category-error verdict). Also superseded here: the four-rung status ladder is demoted to a derived UI label with the assumption ledger as headline (pre-pass S8), and the Geolog/ARIA adjacency guess is refuted (pre-pass S9).\n\n## Question\n\nConfirm the first milestone's two live dev targets. The zil-lean survey resolved the shape of the second: full elicit-lean is **not** dev-sized (writing Lean statements from intent is the deep-expertise step), but the **elicit-proof-obligations** slice is — capture an assumption/lemma/theorem/guarantee dependency graph with criticality and evidence refs, validated by acyclicity + Datalog closure (no Lean statements, no proofs), per the ElicitationPack/ProjectionPack sketches in the survey answer. The survey judges it a *better* second target than BPMN on both pack axes; BPMN stays third.\n\nProposed portfolio to confirm: **elicit-gherkin** (tracer) + **elicit-proof-obligations** (second, forces the pack swap and the evidence-graded IR).\n\n## Answer\n\n> Resolved by HITL grilling, 2026-08-06.\n\n**Portfolio confirmed**: `elicit-gherkin` + `elicit-proof-obligations` are the first milestone's two live dev targets; `elicit-BPMN/process-mining` is named third; full elicit-lean is deferred (writing Lean statements from intent is the deep-expertise step zil-lean itself never attempts).\n\n**Order — hybrid, addressing the real risk at the design layer**: the spec mandates that **both packs are authored before the pack interface freezes** (design against both simultaneously, on paper — the \"two targets on each axis from the beginning\" rule), while **elicit-gherkin wires end-to-end first** as the cheap mechanism proof, with elicit-proof-obligations immediately after. Rationale: the user's identified risk — brunch failed to find a scalable, modular way to specify an elicitation process plus guiding skill material without over-proceduralizing — lives in interface design, not wiring order. The trivial target must not freeze the pack contract before the hard target has stressed it.\n\n**Design principle surfaced (routed to Contract decomposition as a named input)**: agents do better with *behavioral* guidance than procedural, and with *clear shapes/patterns to fill* rather than schemas that require extensive parsing to build a model of the output shape. Packs are shapes-to-fill plus behavioral guidance — not procedural scripts, not parse-heavy schemas.\n\n**Proof-obligations output format**: steal the ideas, own the format. Adopt zil-lean's load-bearing vocabulary (evidence-graded assurance lattice with prohibited promotions; PROVED/CONDITIONAL/WEAK/BROKEN status ladder; acyclicity + Datalog-closure validation) in our own claim-DAG serialization, **hewing to whatever existing canon fits** — Dafny's `requires/ensures/invariant` contract vocabulary is the leading candidate; Geolog (ARIA program, axioms addressed via Datalog-like queries) is plausibly adjacent. Grounding this is the new **Formal-verification canon survey** ticket (09), which blocks Assemble-the-spec so the milestone lands canon-grounded. `.zc` export is someday-maybe; no dependency on the unproven zil-lean repo.\n\n**Gherkin validator depth (milestone one)**: parse validity + optional **pack-declared step-lexicon** binding check. The apparent codebase coupling dissolves: a step lexicon carried as pack policy needs no external project; only live-codebase step binding defers, named as the target's growth path.\n\nSub-questions:\n\n* Do the two chosen targets differ materially on *both* pack axes (semantic + representation)?\n* What is each target's smallest honest output contract for milestone one?\n* Which target is the tracer (built first) and which trails to force the second-pack swap?\n+++", - "proposedBodySha256": "396075c231ecc27d168bfbf7eb9aec920ea14eef5fa50f3fbd460c8fd1d5aa6b", - "ambiguity": null, - "notes": null - }, - { - "id": "fa06f304-4d60-457e-ba14-801664e34c49", - "identifier": "FE-1374", - "url": "https://linear.app/hash/issue/FE-1374/assemble-the-spec-archive", - "state": { - "name": "Done", - "type": "completed" - }, - "parent": { - "id": "a5053b51-937d-453f-9c4e-fe8e49e912ad", - "identifier": "FE-1366", - "title": "Spec the elicitation harness architecture (archived wayfinder map)" - }, - "sourceUpdatedAt": "2026-08-19T16:20:26.406Z", - "sourceTitle": "Assemble the spec [archive]", - "sourceDescriptionSha256": "a61865e7695eb3e3170612f0101ffd8bf4dd97c8322fc08c4bd129d2ea0828c1", - "sourceTitleSha256": "e0ef68909d31cd8e17da3910dd6beb59c3fd02068b82f7a8179a35a498f40f44", - "oldOuter": "The destination of the whole planning effort: assembled the full specification from every resolved ticket — fourteen sections plus an appendix of adjudicated contradictions. The spec lives at `docs/planning/elicitation-kernel/spec.md` (brunch-lite repo), with a companion plain-language product description. Resolved 2026-08-10; this closed the parent map.", - "proposedTitle": "Assemble the elicitation harness specification", - "proposedOuter": "This task assembled the completed planning decisions into a fourteen-section specification with an appendix that resolves prior contradictions. The specification is at `docs/planning/elicitation-kernel/spec.md`, with a companion plain-language product description. It was completed on 2026-08-10 and closed the parent map.", - "extractionMethod": "first-standalone-divider", - "innerRecord": "\n> **Archive migration (2026-08-11).** Resolved ticket from the local-markdown wayfinder effort `elicitation-kernel` (`brunch-lite:.scratch/elicitation-kernel/issues/08-assemble-the-spec.md`), mirrored to Linear for team visibility. Full original content below, including the resolution.\n\n---\n\n# Assemble the spec\n\nType: task\nStatus: resolved\nResolved: 2026-08-10\nBlocked by: 04, 05, 06, 07, 09, 10, 11, 12, 13\n\n## Question\n\nAssemble the destination spec from the resolved decisions: architecture + contract decomposition + questioning-UX contract + shipping shape + first milestone against the confirmed dev-target portfolio. Non-goals (harness-agnosticism) and the elicitor→executor seam named explicitly. This is the map's terminal deliverable; resolving it ends the effort.\n\n## Answer\n\n> Resolved by spec assembly, 2026-08-10. Full read of the ~54K-token working set (map, glossary, tickets 01–13 in the pre-pass's amendment order, both inbox docs), one session, no digest subagents — as sized.\n\n**The spec is assembled: [spec.md](<../spec.md>)** — the map's terminal deliverable. Fourteen sections + an adjudications appendix: purpose · non-goals & the elicitor→executor seam · vocabulary · four shells + binding · the capture envelope (derived-status principle, three strata) · operations & validation strata · questioning-UX contract · capture mechanics · sessions/durability/storage port · the ten-item substrate-capability list · plugins & packs · shipping shape · dev targets & milestone one (gherkin + assurance argument, `Statement` contract) · acceptance material (five proof obligations, ten harness invariants restated in envelope vocabulary, gating tests, testing strategy, open verification items).\n\n**All seven pre-pass contradictions adjudicated** (spec Appendix A): C1 storage port binding-implemented, with the `db.ts`-vs-capture-store reconciliation stated; C2 the four operations stay pure — ticket 12's PluginContext-storage clause scoped to non-op code, milestone one defines no such methods; C3 status derived, never stored — retraction specified as an explicit user-cited event with no successor; C4 tap-ness made a transport fact via a harness-defined reserved reply encoding (else absences are inferred); C5 invariant 1 reconciled with the provenance rule — user-derived captures cite user entries, `defaulted`/`external-lookup` cite declared defaults/documented transformations; C6 the channel is a per-message current-affordance surface, durable identity on tool output parts, reject-second-interactive-affordance as mechanism; C7 picked together — no instruction interpolation (kills the wake wart's cause), pending question on the ask tool result + pending-affordance slot, reply binding harness-mechanical via the single-pending invariant, no echo token.\n\n**Notable assembler adjudications beyond the seven** (each flagged inline in the spec): retraction semantics; `not-mentioned` demoted to computed fact; absence-strip label mapping and the `not-yet-decided`/`deferred` distinction; advisories as computed-ephemeral vs. stored issues; issue namespacing; domain labels computed via `project` at read time; transport outcomes `answered | redirected | unanswered` (`unavailable` retired); session→target-document binding via `initialData`; milestone-one store format constrained by whole-sweep atomicity; **kernel card** kept as a term of art, **\"kernel invariants\" renamed harness invariants**.\n\n**Fold-ins completed alongside**: `CONTEXT.md` gained the envelope-vocabulary section (capture envelope, evidence span, epistemic status, absence state, supersession, resolution record, issue, advisory, pack, kernel card, PluginContext, storage port — pre-pass L11) and the kernel-compound ruling (L12); the `plugin-assurance` rename propagated as header notes on tickets 02 and 07; the 42-item obligation checklist verified covered (§ mapping held during drafting); the ten kernel invariants restated in current vocabulary (S14) so only one vocabulary exists in the acceptance criteria.\n\nResolving this ticket ends the effort: the map's frontier is empty. Remaining fog (dev-app probe view, remote deploy target, storage format, concurrent-session coordination, plugin-ecosystem machinery, the real name) is post-spec by construction and graduates with the build effort the spec now enables.\n\n## Comments\n\n**2026-08-10 (pre-assembly prep, HITL).** Three prep items completed ahead of this ticket:\n\n* **Second-target rename decided**: the target is the **assurance argument**, package `plugin-assurance` (recorded on the [Formal-verification canon survey](<09-formal-verification-canon-survey.md>); ticket 09's category-error verdict discharged). Propagate over the historical `elicit-proof-obligations` occurrences in tickets 02 and 07.\n* **Cross-ticket consistency pre-pass**: [notes/consistency-prepass-2026-08-10.md](<../notes/consistency-prepass-2026-08-10.md>) — the assembler's working checklist. Contents: **seven contradictions** the spec must adjudicate, each with an authoritative-side recommendation (C1 storage-port implementer; C2 op purity vs PluginContext storage methods; C3 derived capture status changes 04's envelope schema; C4 `deferred (explicit)` lacks a transport mechanism; C5 provenance rule vs kernel invariant 1's declared defaults; C6 one-live-affordance slot vs multiplexed forms; C7 wake-wart remedy and reply binding must be picked together); **fourteen stale statements** (S1–S14) so nobody re-imports superseded material — notably 03's partly-overturned classification table and the requirement to restate the ten kernel invariants in current vocabulary; the **42-item \"the spec must…\" checklist** (§3b) collected from every ticket; and the **amendment-order reading guide** (§4) — read tickets in that order, treating 01/02/03 as lookup sources.\n* **Sizing corrected**: ~54K tokens total — map + glossary + tickets (~42K) **plus the two inbox docs** (`agentic-elicitation-challenges`, `agentic-elicitation-criteria`, ~13K), which ticket 04 adopts by reference and by count, so the spec cannot be assembled without reading them. Fits one session raw; no digest subagents needed.\n\n**New blocker added**: [Walking skeleton: sweep seam on Flue](<13-walking-skeleton-sweep-seam.md>) — the pre-pass found the sweep seam unproven on the committed substrate (no settlement-trigger lifecycle event exercised, no proven harness path to read a session entry range; items L1–L3). Decision (HITL, 2026-08-10): prove it before assembly. Its resolution also completes the substrate-capability list this spec must enumerate.\n\nAlso fold in when drafting: the glossary needs the envelope vocabulary added (pre-pass L11–L12) — capture envelope, evidence span, epistemic status, absence state, resolution record, supersession, pack, issue, kernel card, PluginContext, storage port — and a ruling on the \"kernel card\" / \"kernel invariants\" compounds vs the glossary's \"avoid: kernel\".\n\n**2026-08-10 (HITL review round 1, via tuicr).** Seven comments on the draft; five spec amendments: (1) **executor/handoff language removed entirely** — brunch adoption leakage; the spec now names *no privileged downstream consumer* (partially supersedes obligation 17's \"elicitor→executor seam\" clause); (2) absence-state enum marked a working set with expected extension pressure (the assumption vs. known-unknown lesson; naming-for-behavioral-activation as kernel-card-grade work); (3) structured taps pinned as optional ui capability, not a requirement; (4) `project` kept as canon op name with a stated prose preference for the noun (and the §9.4 \"signals project\" verb collision fixed); (5) **new §9.7 compaction vs. durable log** — compaction may shrink what the model re-reads, never what the store can resolve; durable-projection independence stated as a storage-contract constraint, binding absorbs otherwise; verification item added to §14.5; (6) Principle v2 ratified by acclaim, no change; (7) **binding packages take the role prefix**: `packages/binding-flue`, horizon `binding-*` (product name lives in the npm scope, not the basename).\n\n**2026-08-11 (HITL review round 2, from the plain-prose read-through).** Four observations, all folded in: (1) evidence pointers confirmed already stored — §5 sharpened so quote→entry resolution is stated as write-time-once, every later reader navigates by pointer, never text search; (2) `declined` ≠ `deferred` pinned in §5.1 — a decline is a boundary (closes only via explicit act), a deferral an invitation (completion evaluation chases it); (3) **conversations are documents too** — storage-port scope extended to capture store **plus session-log archive**, archive-on-read mechanism, session logs retained indefinitely with the target-document (§9.1/§9.6/§9.7 amended; the archive also becomes the compaction defense); (4) **generic strategy quiver written in as §11.5**, named-not-designed — guidance ownership follows vocabulary ownership; harness-shipped strategy cards over envelope vocabulary, plugin-composed; reference shapes `ln-grill`/`ln-disambiguate` and brunch's style trichotomy; the assurance technique decomposes into generic strategy + domain cards. `CONTEXT.md` (storage port, kernel card) and both companion docs updated to match. Note: round 2's changes were narrated in-session on 2026-08-10 but committed 2026-08-11 — the earlier narration preceded the actual edits.", - "innerSha256": "4f36c5012d06ba284886145fb9fec5c92aa650338986366b9c619ef383874030", - "proposedBody": "This task assembled the completed planning decisions into a fourteen-section specification with an appendix that resolves prior contradictions. The specification is at `docs/planning/elicitation-kernel/spec.md`, with a companion plain-language product description. It was completed on 2026-08-10 and closed the parent map.\n\n+++🏗️ Agent notes\n\n> **Archive migration (2026-08-11).** Resolved ticket from the local-markdown wayfinder effort `elicitation-kernel` (`brunch-lite:.scratch/elicitation-kernel/issues/08-assemble-the-spec.md`), mirrored to Linear for team visibility. Full original content below, including the resolution.\n\n---\n\n# Assemble the spec\n\nType: task\nStatus: resolved\nResolved: 2026-08-10\nBlocked by: 04, 05, 06, 07, 09, 10, 11, 12, 13\n\n## Question\n\nAssemble the destination spec from the resolved decisions: architecture + contract decomposition + questioning-UX contract + shipping shape + first milestone against the confirmed dev-target portfolio. Non-goals (harness-agnosticism) and the elicitor→executor seam named explicitly. This is the map's terminal deliverable; resolving it ends the effort.\n\n## Answer\n\n> Resolved by spec assembly, 2026-08-10. Full read of the ~54K-token working set (map, glossary, tickets 01–13 in the pre-pass's amendment order, both inbox docs), one session, no digest subagents — as sized.\n\n**The spec is assembled: [spec.md](<../spec.md>)** — the map's terminal deliverable. Fourteen sections + an adjudications appendix: purpose · non-goals & the elicitor→executor seam · vocabulary · four shells + binding · the capture envelope (derived-status principle, three strata) · operations & validation strata · questioning-UX contract · capture mechanics · sessions/durability/storage port · the ten-item substrate-capability list · plugins & packs · shipping shape · dev targets & milestone one (gherkin + assurance argument, `Statement` contract) · acceptance material (five proof obligations, ten harness invariants restated in envelope vocabulary, gating tests, testing strategy, open verification items).\n\n**All seven pre-pass contradictions adjudicated** (spec Appendix A): C1 storage port binding-implemented, with the `db.ts`-vs-capture-store reconciliation stated; C2 the four operations stay pure — ticket 12's PluginContext-storage clause scoped to non-op code, milestone one defines no such methods; C3 status derived, never stored — retraction specified as an explicit user-cited event with no successor; C4 tap-ness made a transport fact via a harness-defined reserved reply encoding (else absences are inferred); C5 invariant 1 reconciled with the provenance rule — user-derived captures cite user entries, `defaulted`/`external-lookup` cite declared defaults/documented transformations; C6 the channel is a per-message current-affordance surface, durable identity on tool output parts, reject-second-interactive-affordance as mechanism; C7 picked together — no instruction interpolation (kills the wake wart's cause), pending question on the ask tool result + pending-affordance slot, reply binding harness-mechanical via the single-pending invariant, no echo token.\n\n**Notable assembler adjudications beyond the seven** (each flagged inline in the spec): retraction semantics; `not-mentioned` demoted to computed fact; absence-strip label mapping and the `not-yet-decided`/`deferred` distinction; advisories as computed-ephemeral vs. stored issues; issue namespacing; domain labels computed via `project` at read time; transport outcomes `answered | redirected | unanswered` (`unavailable` retired); session→target-document binding via `initialData`; milestone-one store format constrained by whole-sweep atomicity; **kernel card** kept as a term of art, **\"kernel invariants\" renamed harness invariants**.\n\n**Fold-ins completed alongside**: `CONTEXT.md` gained the envelope-vocabulary section (capture envelope, evidence span, epistemic status, absence state, supersession, resolution record, issue, advisory, pack, kernel card, PluginContext, storage port — pre-pass L11) and the kernel-compound ruling (L12); the `plugin-assurance` rename propagated as header notes on tickets 02 and 07; the 42-item obligation checklist verified covered (§ mapping held during drafting); the ten kernel invariants restated in current vocabulary (S14) so only one vocabulary exists in the acceptance criteria.\n\nResolving this ticket ends the effort: the map's frontier is empty. Remaining fog (dev-app probe view, remote deploy target, storage format, concurrent-session coordination, plugin-ecosystem machinery, the real name) is post-spec by construction and graduates with the build effort the spec now enables.\n\n## Comments\n\n**2026-08-10 (pre-assembly prep, HITL).** Three prep items completed ahead of this ticket:\n\n* **Second-target rename decided**: the target is the **assurance argument**, package `plugin-assurance` (recorded on the [Formal-verification canon survey](<09-formal-verification-canon-survey.md>); ticket 09's category-error verdict discharged). Propagate over the historical `elicit-proof-obligations` occurrences in tickets 02 and 07.\n* **Cross-ticket consistency pre-pass**: [notes/consistency-prepass-2026-08-10.md](<../notes/consistency-prepass-2026-08-10.md>) — the assembler's working checklist. Contents: **seven contradictions** the spec must adjudicate, each with an authoritative-side recommendation (C1 storage-port implementer; C2 op purity vs PluginContext storage methods; C3 derived capture status changes 04's envelope schema; C4 `deferred (explicit)` lacks a transport mechanism; C5 provenance rule vs kernel invariant 1's declared defaults; C6 one-live-affordance slot vs multiplexed forms; C7 wake-wart remedy and reply binding must be picked together); **fourteen stale statements** (S1–S14) so nobody re-imports superseded material — notably 03's partly-overturned classification table and the requirement to restate the ten kernel invariants in current vocabulary; the **42-item \"the spec must…\" checklist** (§3b) collected from every ticket; and the **amendment-order reading guide** (§4) — read tickets in that order, treating 01/02/03 as lookup sources.\n* **Sizing corrected**: ~54K tokens total — map + glossary + tickets (~42K) **plus the two inbox docs** (`agentic-elicitation-challenges`, `agentic-elicitation-criteria`, ~13K), which ticket 04 adopts by reference and by count, so the spec cannot be assembled without reading them. Fits one session raw; no digest subagents needed.\n\n**New blocker added**: [Walking skeleton: sweep seam on Flue](<13-walking-skeleton-sweep-seam.md>) — the pre-pass found the sweep seam unproven on the committed substrate (no settlement-trigger lifecycle event exercised, no proven harness path to read a session entry range; items L1–L3). Decision (HITL, 2026-08-10): prove it before assembly. Its resolution also completes the substrate-capability list this spec must enumerate.\n\nAlso fold in when drafting: the glossary needs the envelope vocabulary added (pre-pass L11–L12) — capture envelope, evidence span, epistemic status, absence state, resolution record, supersession, pack, issue, kernel card, PluginContext, storage port — and a ruling on the \"kernel card\" / \"kernel invariants\" compounds vs the glossary's \"avoid: kernel\".\n\n**2026-08-10 (HITL review round 1, via tuicr).** Seven comments on the draft; five spec amendments: (1) **executor/handoff language removed entirely** — brunch adoption leakage; the spec now names *no privileged downstream consumer* (partially supersedes obligation 17's \"elicitor→executor seam\" clause); (2) absence-state enum marked a working set with expected extension pressure (the assumption vs. known-unknown lesson; naming-for-behavioral-activation as kernel-card-grade work); (3) structured taps pinned as optional ui capability, not a requirement; (4) `project` kept as canon op name with a stated prose preference for the noun (and the §9.4 \"signals project\" verb collision fixed); (5) **new §9.7 compaction vs. durable log** — compaction may shrink what the model re-reads, never what the store can resolve; durable-projection independence stated as a storage-contract constraint, binding absorbs otherwise; verification item added to §14.5; (6) Principle v2 ratified by acclaim, no change; (7) **binding packages take the role prefix**: `packages/binding-flue`, horizon `binding-*` (product name lives in the npm scope, not the basename).\n\n**2026-08-11 (HITL review round 2, from the plain-prose read-through).** Four observations, all folded in: (1) evidence pointers confirmed already stored — §5 sharpened so quote→entry resolution is stated as write-time-once, every later reader navigates by pointer, never text search; (2) `declined` ≠ `deferred` pinned in §5.1 — a decline is a boundary (closes only via explicit act), a deferral an invitation (completion evaluation chases it); (3) **conversations are documents too** — storage-port scope extended to capture store **plus session-log archive**, archive-on-read mechanism, session logs retained indefinitely with the target-document (§9.1/§9.6/§9.7 amended; the archive also becomes the compaction defense); (4) **generic strategy quiver written in as §11.5**, named-not-designed — guidance ownership follows vocabulary ownership; harness-shipped strategy cards over envelope vocabulary, plugin-composed; reference shapes `ln-grill`/`ln-disambiguate` and brunch's style trichotomy; the assurance technique decomposes into generic strategy + domain cards. `CONTEXT.md` (storage port, kernel card) and both companion docs updated to match. Note: round 2's changes were narrated in-session on 2026-08-10 but committed 2026-08-11 — the earlier narration preceded the actual edits.\n+++", - "proposedBodySha256": "2580962162612f84dd358987a0972a0b8a9deb126451f0b070f4c8306b9b2e22", - "ambiguity": null, - "notes": null - }, - { - "id": "2375aced-d1bc-4e3c-8b00-88ebc8ea2e8c", - "identifier": "FE-1375", - "url": "https://linear.app/hash/issue/FE-1375/formal-verification-canon-survey-archive", - "state": { - "name": "Done", - "type": "completed" - }, - "parent": { - "id": "a5053b51-937d-453f-9c4e-fe8e49e912ad", - "identifier": "FE-1366", - "title": "Spec the elicitation harness architecture (archived wayfinder map)" - }, - "sourceUpdatedAt": "2026-08-19T16:20:27.379Z", - "sourceTitle": "Formal-verification canon survey [archive]", - "sourceDescriptionSha256": "da21a9ce028674fd3a68be0b7b6401585fa9af23cd013781a53c1d1a8a1a787c", - "sourceTitleSha256": "686845995b96dbb4605c51d32a3db23e65fe13afab1b3d29fb71ac0f3aa751f5", - "oldOuter": "Checked the second practice subject against the formal-verification literature. Verdict: \"proof obligations\" was the wrong name — a category error to verification readers — so the subject was renamed to the assurance argument, aligned to the GSN standard with vocabulary borrowed from Dafny. Resolved 2026-08-10; part of the completed elicitation-harness planning (see parent map).", - "proposedTitle": "Align the assurance subject with verification practice", - "proposedOuter": "This research compared the second practice subject with formal-verification literature. It found that “proof obligations” named the wrong concept for verification readers, so the plan renamed the subject “assurance argument” and aligned it with GSN and Dafny vocabulary. The research was resolved on 2026-08-10.", - "extractionMethod": "first-standalone-divider", - "innerRecord": "\n> **Archive migration (2026-08-11).** Resolved ticket from the local-markdown wayfinder effort `elicitation-kernel` (`brunch-lite:.scratch/elicitation-kernel/issues/09-formal-verification-canon-survey.md`), mirrored to Linear for team visibility. Full original content below, including the resolution.\n\n---\n\n# Formal-verification canon survey\n\nType: research\nStatus: resolved\nResolved: 2026-08-06\n\n## Question\n\n> **Rename decided 2026-08-10** (HITL, during spec-assembly prep): the second target is the **assurance argument** — GSN's own noun, per this ticket's verdict that \"proof obligations\" reads as a category error. Package: `plugin-assurance` (was `plugin-proof-obligations` in the Shipping-shape topology). References to `elicit-proof-obligations` below are the historical name.\n\nWhat existing canon should the `elicit-proof-obligations` output contract hew to, and — written didactically, for a reader new to formal verification — what does a verification workflow *actually do*: what are its artifacts, what is one trying to produce, where does the human effort go?\n\nSpecifically:\n\n* **Dafny's contract vocabulary** (`requires` / `ensures` / `invariant` / `decreases`, pre/postconditions, loop invariants): how practitioners actually express obligations, and how much of that vocabulary transfers to a language-agnostic claim DAG\n* **Proof-obligation workflow 101**: in Dafny/Lean/TLA+-style work, what is the day-to-day loop (state → obligation → discharge/failure → refine)? What does \"an obligation\" look like as an artifact? What roles do assumptions/axioms play?\n* **Geolog / ARIA relevance**: what is Geolog (ARIA program context — axiom sets ostensibly addressed via Datalog-like queries), and does its shape align with our acyclicity + Datalog-closure validator design?\n* **Recommendation**: what our claim-DAG format should align to — which canon's vocabulary, which parts to adopt vs. leave, and what the smallest canonical-feeling output contract for milestone one looks like\n\nContext: the portfolio decision (issue 07) adopted zil-lean's *ideas* (assurance lattice, PROVED/CONDITIONAL/WEAK/BROKEN ladder, Datalog-closure validation) but rejected its format as unproven; the output contract should feel native to people who do this work.\n\n## Answer\n\n> Resolved by `/research` subagent, 2026-08-06.\n\n# Formal-Verification Canon Survey — for the `elicit-proof-obligations` target\n\n## 1. Proof-obligation workflow 101\n\n**The daily loop (Dafny-style, most concrete of the three).** You write code *and* annotations in the same file. You hit save. The verifier translates your program into a pile of logical formulas called **verification conditions (VCs)** and asks an automated prover whether each is valid. Dafny specifically \"verif[ies] that the program meets its specifications, by translating the program to verification conditions and checking those with Boogie and an SMT solver, typically Z3\" ([Dafny Reference Manual §13.1]()). Green = discharged. Red = an assertion the solver could not prove. You then edit *the annotations, not usually the code*, and re-run. Loop time is seconds-to-minutes; Midspiral reports \"proofs often take more than 10 minutes to run\" on real domains ([midspiral.com]()).\n\n**What a proof obligation *is*.** It is machine-generated, not human-authored. The human writes *contracts*; the tool derives obligations from them. Worked micro-example:\n\n```dafny\nmethod Decrement(n: int) returns (m: int)\n requires n > 0 // precondition — caller must establish\n ensures m == n - 1 // postcondition — callee must establish\n ensures m >= 0\n{ m := n - 1; }\n```\n\nFrom those three lines the verifier generates roughly: (a) *assuming* `n > 0` and the body's effect, prove `m == n-1`; (b) same, prove `m >= 0`; (c) at every call site of `Decrement`, prove `n > 0` holds there. Add a loop and you get more: the invariant holds on entry, is preserved by one iteration, and (with the negated guard) implies what follows — \"The `invariant` clause is effectively a precondition and it along with the negation of the loop test condition provides the postcondition. The `decreases` clause is used to prove termination\" (Dafny RM §7.6). Termination needs the `decreases` expression to both *decrease* and be *bounded below* ([Dafny tutorial, Termination]()).\n\n**\"Discharging\"** = the prover established that VC. Nobody hand-writes it. **On failure there are exactly two diagnoses** and telling them apart is the actual skill: \"there are two main causes for Dafny verification errors: specifications that are inconsistent with the code, and situations where it is not 'clever' enough to prove the required properties\" (Dafny tutorial). Failure gives you an error at a source location, optionally a counterexample — which Dafny explicitly downgrades to a hint: \"Dafny cannot guarantee that the counterexample it reports provably violates the assertion... should be inspected manually and treated as a hint\" (RM §13.7.1).\n\n**Where human effort goes.** Not into proofs — into *specs, invariants, and hints*. Concretely: strengthening a loop invariant; adding a `lemma` (\"a lemma states a logical fact, summarizing an inference that the verifier cannot do on its own,\" RM §6.3.3); hiding irrelevant facts so the solver focuses (\"sometimes less information is better for the solver,\" RM §8.20.2). Midspiral's numbers make the shape vivid: same kernel, \"counter domain (~50 lines of proofs) and the Kanban domain (~1,400 lines of proofs).\"\n\n**Durable vs. ephemeral artifacts.** Durable: the specification (contracts, invariants), the lemma corpus, and — critically — **the ledger of things assumed rather than proved**. Ephemeral: SMT queries, counterexamples, timings, proof-search traces. And the canon is explicit that the durable spec is the weak point: \"Proofs guarantee that the implementation satisfies the specification. They don't guarantee that the specification is what you actually wanted... The human still owns the spec\" (Midspiral, *Methodology Limitations*). That sentence is the entire justification for your product.\n\n## 2. Dafny's contract vocabulary — what transfers\n\n| Keyword | Meaning | Transfers to a claim DAG about an arbitrary system? |\n| -- | -- | -- |\n| `requires` | precondition, obligation on the *caller* | **Yes** — the canonical name for \"this claim's premise / what must hold for my guarantee to mean anything\" |\n| `ensures` | postcondition, obligation on the *callee* | **Yes** — canonical for \"guarantee\" |\n| `invariant` | property preserved across steps | **Yes** — reads naturally as a system-level always-true claim |\n| `decreases` | termination measure | **Partial** — as a *well-foundedness witness* it's the honest canonical way to license a cycle; otherwise program-bound |\n| `modifies` / `reads` | frame conditions | **No** — \"framing only applies to the heap, or memory accessed through references\" (RM §7.1.4). Inherently program text. |\n| `assert` | prove this here | **Yes** — an obligation you accept |\n| `assume` | take this on faith | **Yes** — this *is* your \"assumption with review status\" |\n| ghost state | spec-only variables, erased at compile | **No** — an artifact of having a compiler |\n| `lemma` | named reusable inference step | **Yes** — maps directly to your lemma record |\n\nThe single most transferable thing in the Dafny ecosystem is not a keyword: it is `dafny audit`, which \"reports issues in the Dafny code that might limit the soundness claims of verification\" and flags declarations marked `{:axiom}`, `{:verify false}`, `{:extern}` with contracts, any `assume` in a body, and `decreases *` — because \"the key purpose of the `audit` command is to ensure that all assumptions are intentional and acknowledged\" (RM §13.6.1.8). It emits a **Markdown table**. That is, near-verbatim, the output artifact you are building. Adopt its framing.\n\n## 3. Adjacent canons\n\n**TLA+.** Obligation-like artifact: an *invariant* or *temporal property* checked against a state machine. Two tools, two epistemics. TLC does bounded exhaustive search: it \"builds a finite state model... performs a breadth-first search... If TLC discovers a state which violates a system invariant, it halts and provides a state trace path\" ([Wikipedia]()). TLAPS does real proof: proofs are \"transformed into individual obligations which are sent to back-end provers\" (Isabelle, Zenon, Z3), and are \"hierarchically structured, easing refactoring and enabling non-linear development: work can begin on later steps before all prior steps are verified.\" **Fit: strong on structure** — hierarchical, obligation-per-step, partial completion is normal — but the vocabulary (`Init`, `Next`, `[]`, fairness) presumes a state machine you don't have.\n\n**Lean / Isabelle.** Artifacts: `definition` / `lemma` / `theorem`, organized in namespaces, with `axiom` a first-class declaration kind ([Lean Language Reference §8]()). The culture-critical mechanism is `sorry`**-tracking**: a proof left incomplete still typechecks but taints the result, and `#print axioms` reveals the taint. **Fit: excellent for your lemma/theorem/assumption trichotomy and for the CONDITIONAL rung** — \"proved, but modulo these named holes\" is native theorem-prover thinking.\n\n**Alloy.** Vocabulary: `sig` (signatures define vocabulary), `fact` (always-true constraints), `pred`, `fun`, `assert` — checked by a SAT-based model finder within a bounded scope ([Wikipedia]()). \"Lightweight formal methods\": finds counterexamples, never proves. **Fit: weaker on vocabulary, but philosophically closest to milestone one** — you too are doing a cheap, bounded, always-terminating check that surfaces defects rather than certifying correctness. Borrow the *stance*, not the nouns.\n\n**GSN (assurance cases).** Six core element types: **Goal** (a claim), **Strategy** (the nature of the inference from a goal to its sub-goals), **Solution** (a reference to evidence), **Context**, **Assumption**, **Justification** (rationale). Two link types: **SupportedBy** (inferential or evidential) and **InContextOf** (relating Context/Assumption/Justification to Goals and Strategies). Goals and Strategies may be marked **Undeveloped** — \"a line of argument has not been developed yet.\" Large arguments modularize via **away goals** ([GSN Community Standard v1, FAA-hosted PDF](); [SCSC GSN](); GSN liaises with OMG's [SACM]()). **Fit: best of the four for interviewed claims about an arbitrary system.** It was designed for exactly your situation — a human argues that a system is adequate, with heterogeneous evidence, in a graph, where \"not yet argued\" is a legitimate node state.\n\n## 4. Geolog / ARIA — negative result, stated plainly\n\n**No ARIA / Safeguarded AI / davidad artifact named \"Geolog\" could be found.** Searches across `geolog + davidad`, `geolog + Safeguarded AI`, `geolog + Datalog + verification kernel`, and GitHub returned nothing. The ARIA [Programme Thesis v2]() is an image-heavy PDF whose text could not be extracted; the [funded projects page]() and the [TA1.1 Theory call]() describe \"computationally practicable mathematical representations and formal semantics\" without naming a logic. **Do not build on a claim that ARIA ships something called Geolog.**\n\n**What \"Geolog\" actually names in the literature** (documented): a logic-programming language for **coherent logic**, the language whose queries Skolem machines compute (Fisher & Bezem, *Skolem Machines*; Bezem & Coquand, *Automating Coherent Logic*). Coherent logic is \"a restriction of first-order logic due to Skolem that is proof-theoretically tractable\"; geometric logic is its infinitary generalisation, with axioms written as sequents built from `⊤, ∧, ⊥, ⋁, ∃, =`, and models \"preserved and reflected by geometric morphisms\" ([Wikipedia: Geometric logic](); [nLab: geometric theory]()). There is a separate, unrelated *Geolog* for GIS/spatial Prolog ([arXiv:2109.08295]()).\n\n**Does the shape align with acyclicity + Datalog closure?** Yes, and non-trivially. Coherent-logic provers are **forward-chaining fixpoint engines** — \"the first automated theorem prover based on coherent logic, Euclid, was developed in Prolog and its inference system relied on a forward-chaining mechanism,\" computing \"the fixpoint for a geometric configuration\" ([Automating Coherent Logic, Springer](); [A Deductive Database Approach to Automated Geometry Theorem Proving]()). Datalog is precisely the ∃-free, ⋁-free fragment of that. **(Inference):** the validator is a Datalog restriction of a coherent-logic saturation engine, which is a genuinely canonical lineage you can cite — Geolog is the *right ancestor* to name, just not an ARIA one. Honest caveat: coherent logic in general is undecidable; Datalog is not. The restriction is what buys determinism.\n\n*(Adjacent, real, and possibly what was half-remembered: ARIA-adjacent work on **Kolm**, \"an early-stage decentralized proof database designed to interoperate with Lean\" — mentioned in [a davidad interview](), with usable tools projected end of 2027. Single-source; treat as unconfirmed.)*\n\n## 5. RECOMMENDATION\n\n**Align to a GSN skeleton with Dafny nouns on the claim fields and Lean/Dafny-audit semantics on the status ladder.** GSN because it is the only canon designed for *argued* claims about a system by humans with mixed evidence; Dafny because `requires`/`ensures`/`invariant`/`lemma` are the words verification people reach for first and cost nothing to adopt; `dafny audit` because it is literally the deliverable.\n\n**Adopt:** GSN's Goal / Strategy / Solution / Assumption / Justification vocabulary and its two link types; Dafny's `requires`/`ensures`/`invariant`/`lemma`/`assumption`; Lean's `sorry`-taint semantics; `dafny audit`'s \"list of intentional, acknowledged assumptions\" as the primary output.\n**Leave:** `modifies`/`reads` (heap-bound), ghost state, TLA+'s temporal operators, Alloy's `sig`/scope machinery.\n\n### Smallest canonical-feeling milestone-one contract\n\n**One record type,** `Statement`**, with a** `kind` **discriminant** (avoids five near-identical schemas):\n\n* `id`, `kind` ∈ {`goal`, `strategy`, `assumption`, `lemma`, `theorem`, `guarantee`, `constraint`, `evidence`, `justification`, `context`}\n* `statement` — one natural-language sentence, indicative mood\n* `owner`, `review_status` ∈ {`unreviewed`, `accepted`, `disputed`, `retired`} *(assumptions only; from* `dafny audit`*)*\n* `criticality` ∈ {`catastrophic`, `major`, `minor`} — **note: this comes from safety engineering (DAL/SIL/ASIL), not from Dafny/Lean, which have no notion of it.** Source it there and say so.\n* `evidence_refs[]`, `provenance` (transcript span), `developed: bool` (GSN Undeveloped)\n\n**Four edge kinds:**\n\n1. `supports` (GSN SupportedBy — inferential; child → parent)\n2. `evidenced_by` (GSN SupportedBy — evidential; claim → evidence)\n3. `requires` (Dafny precondition; claim → premise it needs)\n4. `in_context_of` (GSN InContextOf; claim → assumption/context/justification)\n\nOnly `supports`, `evidenced_by`, `requires` are load-bearing for status. `in_context_of` is scoping.\n\n**Derived status, stratified:**\n\n* **S0** `refuted(X)` if evidence marked contradicting; `open(X)` if `kind=assumption ∧ review_status ∈ {unreviewed, disputed}`\n* **S1** `BROKEN(X)` if `refuted(X)` ∨ ∃ load-bearing child `BROKEN` *(pure positive recursion — closes first)*\n* **S2** `WEAK(X)` if ¬BROKEN ∧ (`¬developed` ∨ (no `evidenced_by` ∧ no `supports`))\n* **S3** `CONDITIONAL(X)` if ¬BROKEN ∧ ¬WEAK ∧ ∃ transitively-reachable `open` assumption\n* **S4** `PROVED(X)` if ¬BROKEN ∧ ¬WEAK ∧ ¬CONDITIONAL\n\nNegation only ever looks at a lower stratum — that is the stratification the validator must enforce, alongside acyclicity of the three load-bearing edge kinds.\n\n### Where canon conflicts with already-made decisions\n\n1. **The four-rung ladder is not canon; the audit list is.** Dafny and Lean are binary (verified / not) *plus an enumerated list of soundness caveats*. **Canon should win on emphasis:** make the per-claim status a derived UI label, and make the **assumption ledger** — every `open` assumption with its owner, review status, and which guarantees it taints — the headline artifact. Ship it as a Markdown table, like `dafny audit`.\n2. **Acyclicity is stricter than canon.** Lean and Dafny permit mutual recursion licensed by a `decreases` measure. **The acyclicity decision should win for milestone one** — it makes validation trivially decidable and the failure message legible — but record it as a deliberate restriction and name `decreases` as the future escape hatch. That framing reads as informed rather than naive to a verification reader.\n3. **GSN is deliberately *not* computed.** A GSN goal structure is a human argument; nobody derives a verdict from it mechanically. The Datalog closure must therefore be sold as a **well-formedness and taint-propagation check, not an assurance verdict.** Never let the UI say a claim is \"proved\" unqualified. Borrow Alloy's honest stance: this finds defects; it does not certify.\n4. **\"Proof obligation\" is a term of art for a machine-generated VC.** We are eliciting *contracts and claims*, from which obligations would later be generated. Calling the output \"proof obligations\" will read as a category error to a Dafny user. **Prefer \"obligation ledger,\" \"claim structure,\" or \"assurance argument.\"**\n\n## 6. Unreached sources\n\n* ARIA **Safeguarded AI Programme Thesis v1.2 / v2** — PDFs are image-based; no text extraction. Likely the single highest-value unread source for Q4.\n* **GSN Community Standard v3 (SCSC-141C)** — download endpoint returns a redirect stub, not the PDF. GSN element definitions come from the FAA-hosted v1 plus secondary literature; v3 may have refined them.\n* **Bezem & Coquand, *Automating Coherent Logic*** (ResearchGate HTTP 403) and **Fisher & Bezem, *Skolem Machines*** (Semantic Scholar, empty body) — the primary Geolog sources. Read via institutional access before citing Geolog's concrete syntax.\n* **LessWrong: *Davidad's Provably Safe AI Architecture*** — HTTP 429 rate-limited. Worth a retry for Q4.\n* **Alloy online tutorial** (HTML parser crash) and **alloytools day-course slides** (PDF) — Alloy detail rests on Wikipedia plus its citation of Jackson's *Software Abstractions*.\n* **TLAPS project site** (`lamport.azurewebsites.net/tla/tlaps.html`, 404); TLAPS facts are from Wikipedia and the INRIA mirror.\n* **Isabelle documentation** (fetch error) — Isabelle is covered only by analogy to Lean here.", - "innerSha256": "b3522ac5372aa5e91bac588fead9e7603c59d5fd669f256d1bc627d68a7e4b7a", - "proposedBody": "This research compared the second practice subject with formal-verification literature. It found that “proof obligations” named the wrong concept for verification readers, so the plan renamed the subject “assurance argument” and aligned it with GSN and Dafny vocabulary. The research was resolved on 2026-08-10.\n\n+++🏗️ Agent notes\n\n> **Archive migration (2026-08-11).** Resolved ticket from the local-markdown wayfinder effort `elicitation-kernel` (`brunch-lite:.scratch/elicitation-kernel/issues/09-formal-verification-canon-survey.md`), mirrored to Linear for team visibility. Full original content below, including the resolution.\n\n---\n\n# Formal-verification canon survey\n\nType: research\nStatus: resolved\nResolved: 2026-08-06\n\n## Question\n\n> **Rename decided 2026-08-10** (HITL, during spec-assembly prep): the second target is the **assurance argument** — GSN's own noun, per this ticket's verdict that \"proof obligations\" reads as a category error. Package: `plugin-assurance` (was `plugin-proof-obligations` in the Shipping-shape topology). References to `elicit-proof-obligations` below are the historical name.\n\nWhat existing canon should the `elicit-proof-obligations` output contract hew to, and — written didactically, for a reader new to formal verification — what does a verification workflow *actually do*: what are its artifacts, what is one trying to produce, where does the human effort go?\n\nSpecifically:\n\n* **Dafny's contract vocabulary** (`requires` / `ensures` / `invariant` / `decreases`, pre/postconditions, loop invariants): how practitioners actually express obligations, and how much of that vocabulary transfers to a language-agnostic claim DAG\n* **Proof-obligation workflow 101**: in Dafny/Lean/TLA+-style work, what is the day-to-day loop (state → obligation → discharge/failure → refine)? What does \"an obligation\" look like as an artifact? What roles do assumptions/axioms play?\n* **Geolog / ARIA relevance**: what is Geolog (ARIA program context — axiom sets ostensibly addressed via Datalog-like queries), and does its shape align with our acyclicity + Datalog-closure validator design?\n* **Recommendation**: what our claim-DAG format should align to — which canon's vocabulary, which parts to adopt vs. leave, and what the smallest canonical-feeling output contract for milestone one looks like\n\nContext: the portfolio decision (issue 07) adopted zil-lean's *ideas* (assurance lattice, PROVED/CONDITIONAL/WEAK/BROKEN ladder, Datalog-closure validation) but rejected its format as unproven; the output contract should feel native to people who do this work.\n\n## Answer\n\n> Resolved by `/research` subagent, 2026-08-06.\n\n# Formal-Verification Canon Survey — for the `elicit-proof-obligations` target\n\n## 1. Proof-obligation workflow 101\n\n**The daily loop (Dafny-style, most concrete of the three).** You write code *and* annotations in the same file. You hit save. The verifier translates your program into a pile of logical formulas called **verification conditions (VCs)** and asks an automated prover whether each is valid. Dafny specifically \"verif[ies] that the program meets its specifications, by translating the program to verification conditions and checking those with Boogie and an SMT solver, typically Z3\" ([Dafny Reference Manual §13.1]()). Green = discharged. Red = an assertion the solver could not prove. You then edit *the annotations, not usually the code*, and re-run. Loop time is seconds-to-minutes; Midspiral reports \"proofs often take more than 10 minutes to run\" on real domains ([midspiral.com]()).\n\n**What a proof obligation *is*.** It is machine-generated, not human-authored. The human writes *contracts*; the tool derives obligations from them. Worked micro-example:\n\n```dafny\nmethod Decrement(n: int) returns (m: int)\n requires n > 0 // precondition — caller must establish\n ensures m == n - 1 // postcondition — callee must establish\n ensures m >= 0\n{ m := n - 1; }\n```\n\nFrom those three lines the verifier generates roughly: (a) *assuming* `n > 0` and the body's effect, prove `m == n-1`; (b) same, prove `m >= 0`; (c) at every call site of `Decrement`, prove `n > 0` holds there. Add a loop and you get more: the invariant holds on entry, is preserved by one iteration, and (with the negated guard) implies what follows — \"The `invariant` clause is effectively a precondition and it along with the negation of the loop test condition provides the postcondition. The `decreases` clause is used to prove termination\" (Dafny RM §7.6). Termination needs the `decreases` expression to both *decrease* and be *bounded below* ([Dafny tutorial, Termination]()).\n\n**\"Discharging\"** = the prover established that VC. Nobody hand-writes it. **On failure there are exactly two diagnoses** and telling them apart is the actual skill: \"there are two main causes for Dafny verification errors: specifications that are inconsistent with the code, and situations where it is not 'clever' enough to prove the required properties\" (Dafny tutorial). Failure gives you an error at a source location, optionally a counterexample — which Dafny explicitly downgrades to a hint: \"Dafny cannot guarantee that the counterexample it reports provably violates the assertion... should be inspected manually and treated as a hint\" (RM §13.7.1).\n\n**Where human effort goes.** Not into proofs — into *specs, invariants, and hints*. Concretely: strengthening a loop invariant; adding a `lemma` (\"a lemma states a logical fact, summarizing an inference that the verifier cannot do on its own,\" RM §6.3.3); hiding irrelevant facts so the solver focuses (\"sometimes less information is better for the solver,\" RM §8.20.2). Midspiral's numbers make the shape vivid: same kernel, \"counter domain (~50 lines of proofs) and the Kanban domain (~1,400 lines of proofs).\"\n\n**Durable vs. ephemeral artifacts.** Durable: the specification (contracts, invariants), the lemma corpus, and — critically — **the ledger of things assumed rather than proved**. Ephemeral: SMT queries, counterexamples, timings, proof-search traces. And the canon is explicit that the durable spec is the weak point: \"Proofs guarantee that the implementation satisfies the specification. They don't guarantee that the specification is what you actually wanted... The human still owns the spec\" (Midspiral, *Methodology Limitations*). That sentence is the entire justification for your product.\n\n## 2. Dafny's contract vocabulary — what transfers\n\n| Keyword | Meaning | Transfers to a claim DAG about an arbitrary system? |\n| -- | -- | -- |\n| `requires` | precondition, obligation on the *caller* | **Yes** — the canonical name for \"this claim's premise / what must hold for my guarantee to mean anything\" |\n| `ensures` | postcondition, obligation on the *callee* | **Yes** — canonical for \"guarantee\" |\n| `invariant` | property preserved across steps | **Yes** — reads naturally as a system-level always-true claim |\n| `decreases` | termination measure | **Partial** — as a *well-foundedness witness* it's the honest canonical way to license a cycle; otherwise program-bound |\n| `modifies` / `reads` | frame conditions | **No** — \"framing only applies to the heap, or memory accessed through references\" (RM §7.1.4). Inherently program text. |\n| `assert` | prove this here | **Yes** — an obligation you accept |\n| `assume` | take this on faith | **Yes** — this *is* your \"assumption with review status\" |\n| ghost state | spec-only variables, erased at compile | **No** — an artifact of having a compiler |\n| `lemma` | named reusable inference step | **Yes** — maps directly to your lemma record |\n\nThe single most transferable thing in the Dafny ecosystem is not a keyword: it is `dafny audit`, which \"reports issues in the Dafny code that might limit the soundness claims of verification\" and flags declarations marked `{:axiom}`, `{:verify false}`, `{:extern}` with contracts, any `assume` in a body, and `decreases *` — because \"the key purpose of the `audit` command is to ensure that all assumptions are intentional and acknowledged\" (RM §13.6.1.8). It emits a **Markdown table**. That is, near-verbatim, the output artifact you are building. Adopt its framing.\n\n## 3. Adjacent canons\n\n**TLA+.** Obligation-like artifact: an *invariant* or *temporal property* checked against a state machine. Two tools, two epistemics. TLC does bounded exhaustive search: it \"builds a finite state model... performs a breadth-first search... If TLC discovers a state which violates a system invariant, it halts and provides a state trace path\" ([Wikipedia]()). TLAPS does real proof: proofs are \"transformed into individual obligations which are sent to back-end provers\" (Isabelle, Zenon, Z3), and are \"hierarchically structured, easing refactoring and enabling non-linear development: work can begin on later steps before all prior steps are verified.\" **Fit: strong on structure** — hierarchical, obligation-per-step, partial completion is normal — but the vocabulary (`Init`, `Next`, `[]`, fairness) presumes a state machine you don't have.\n\n**Lean / Isabelle.** Artifacts: `definition` / `lemma` / `theorem`, organized in namespaces, with `axiom` a first-class declaration kind ([Lean Language Reference §8]()). The culture-critical mechanism is `sorry`**-tracking**: a proof left incomplete still typechecks but taints the result, and `#print axioms` reveals the taint. **Fit: excellent for your lemma/theorem/assumption trichotomy and for the CONDITIONAL rung** — \"proved, but modulo these named holes\" is native theorem-prover thinking.\n\n**Alloy.** Vocabulary: `sig` (signatures define vocabulary), `fact` (always-true constraints), `pred`, `fun`, `assert` — checked by a SAT-based model finder within a bounded scope ([Wikipedia]()). \"Lightweight formal methods\": finds counterexamples, never proves. **Fit: weaker on vocabulary, but philosophically closest to milestone one** — you too are doing a cheap, bounded, always-terminating check that surfaces defects rather than certifying correctness. Borrow the *stance*, not the nouns.\n\n**GSN (assurance cases).** Six core element types: **Goal** (a claim), **Strategy** (the nature of the inference from a goal to its sub-goals), **Solution** (a reference to evidence), **Context**, **Assumption**, **Justification** (rationale). Two link types: **SupportedBy** (inferential or evidential) and **InContextOf** (relating Context/Assumption/Justification to Goals and Strategies). Goals and Strategies may be marked **Undeveloped** — \"a line of argument has not been developed yet.\" Large arguments modularize via **away goals** ([GSN Community Standard v1, FAA-hosted PDF](); [SCSC GSN](); GSN liaises with OMG's [SACM]()). **Fit: best of the four for interviewed claims about an arbitrary system.** It was designed for exactly your situation — a human argues that a system is adequate, with heterogeneous evidence, in a graph, where \"not yet argued\" is a legitimate node state.\n\n## 4. Geolog / ARIA — negative result, stated plainly\n\n**No ARIA / Safeguarded AI / davidad artifact named \"Geolog\" could be found.** Searches across `geolog + davidad`, `geolog + Safeguarded AI`, `geolog + Datalog + verification kernel`, and GitHub returned nothing. The ARIA [Programme Thesis v2]() is an image-heavy PDF whose text could not be extracted; the [funded projects page]() and the [TA1.1 Theory call]() describe \"computationally practicable mathematical representations and formal semantics\" without naming a logic. **Do not build on a claim that ARIA ships something called Geolog.**\n\n**What \"Geolog\" actually names in the literature** (documented): a logic-programming language for **coherent logic**, the language whose queries Skolem machines compute (Fisher & Bezem, *Skolem Machines*; Bezem & Coquand, *Automating Coherent Logic*). Coherent logic is \"a restriction of first-order logic due to Skolem that is proof-theoretically tractable\"; geometric logic is its infinitary generalisation, with axioms written as sequents built from `⊤, ∧, ⊥, ⋁, ∃, =`, and models \"preserved and reflected by geometric morphisms\" ([Wikipedia: Geometric logic](); [nLab: geometric theory]()). There is a separate, unrelated *Geolog* for GIS/spatial Prolog ([arXiv:2109.08295]()).\n\n**Does the shape align with acyclicity + Datalog closure?** Yes, and non-trivially. Coherent-logic provers are **forward-chaining fixpoint engines** — \"the first automated theorem prover based on coherent logic, Euclid, was developed in Prolog and its inference system relied on a forward-chaining mechanism,\" computing \"the fixpoint for a geometric configuration\" ([Automating Coherent Logic, Springer](); [A Deductive Database Approach to Automated Geometry Theorem Proving]()). Datalog is precisely the ∃-free, ⋁-free fragment of that. **(Inference):** the validator is a Datalog restriction of a coherent-logic saturation engine, which is a genuinely canonical lineage you can cite — Geolog is the *right ancestor* to name, just not an ARIA one. Honest caveat: coherent logic in general is undecidable; Datalog is not. The restriction is what buys determinism.\n\n*(Adjacent, real, and possibly what was half-remembered: ARIA-adjacent work on **Kolm**, \"an early-stage decentralized proof database designed to interoperate with Lean\" — mentioned in [a davidad interview](), with usable tools projected end of 2027. Single-source; treat as unconfirmed.)*\n\n## 5. RECOMMENDATION\n\n**Align to a GSN skeleton with Dafny nouns on the claim fields and Lean/Dafny-audit semantics on the status ladder.** GSN because it is the only canon designed for *argued* claims about a system by humans with mixed evidence; Dafny because `requires`/`ensures`/`invariant`/`lemma` are the words verification people reach for first and cost nothing to adopt; `dafny audit` because it is literally the deliverable.\n\n**Adopt:** GSN's Goal / Strategy / Solution / Assumption / Justification vocabulary and its two link types; Dafny's `requires`/`ensures`/`invariant`/`lemma`/`assumption`; Lean's `sorry`-taint semantics; `dafny audit`'s \"list of intentional, acknowledged assumptions\" as the primary output.\n**Leave:** `modifies`/`reads` (heap-bound), ghost state, TLA+'s temporal operators, Alloy's `sig`/scope machinery.\n\n### Smallest canonical-feeling milestone-one contract\n\n**One record type,** `Statement`**, with a** `kind` **discriminant** (avoids five near-identical schemas):\n\n* `id`, `kind` ∈ {`goal`, `strategy`, `assumption`, `lemma`, `theorem`, `guarantee`, `constraint`, `evidence`, `justification`, `context`}\n* `statement` — one natural-language sentence, indicative mood\n* `owner`, `review_status` ∈ {`unreviewed`, `accepted`, `disputed`, `retired`} *(assumptions only; from* `dafny audit`*)*\n* `criticality` ∈ {`catastrophic`, `major`, `minor`} — **note: this comes from safety engineering (DAL/SIL/ASIL), not from Dafny/Lean, which have no notion of it.** Source it there and say so.\n* `evidence_refs[]`, `provenance` (transcript span), `developed: bool` (GSN Undeveloped)\n\n**Four edge kinds:**\n\n1. `supports` (GSN SupportedBy — inferential; child → parent)\n2. `evidenced_by` (GSN SupportedBy — evidential; claim → evidence)\n3. `requires` (Dafny precondition; claim → premise it needs)\n4. `in_context_of` (GSN InContextOf; claim → assumption/context/justification)\n\nOnly `supports`, `evidenced_by`, `requires` are load-bearing for status. `in_context_of` is scoping.\n\n**Derived status, stratified:**\n\n* **S0** `refuted(X)` if evidence marked contradicting; `open(X)` if `kind=assumption ∧ review_status ∈ {unreviewed, disputed}`\n* **S1** `BROKEN(X)` if `refuted(X)` ∨ ∃ load-bearing child `BROKEN` *(pure positive recursion — closes first)*\n* **S2** `WEAK(X)` if ¬BROKEN ∧ (`¬developed` ∨ (no `evidenced_by` ∧ no `supports`))\n* **S3** `CONDITIONAL(X)` if ¬BROKEN ∧ ¬WEAK ∧ ∃ transitively-reachable `open` assumption\n* **S4** `PROVED(X)` if ¬BROKEN ∧ ¬WEAK ∧ ¬CONDITIONAL\n\nNegation only ever looks at a lower stratum — that is the stratification the validator must enforce, alongside acyclicity of the three load-bearing edge kinds.\n\n### Where canon conflicts with already-made decisions\n\n1. **The four-rung ladder is not canon; the audit list is.** Dafny and Lean are binary (verified / not) *plus an enumerated list of soundness caveats*. **Canon should win on emphasis:** make the per-claim status a derived UI label, and make the **assumption ledger** — every `open` assumption with its owner, review status, and which guarantees it taints — the headline artifact. Ship it as a Markdown table, like `dafny audit`.\n2. **Acyclicity is stricter than canon.** Lean and Dafny permit mutual recursion licensed by a `decreases` measure. **The acyclicity decision should win for milestone one** — it makes validation trivially decidable and the failure message legible — but record it as a deliberate restriction and name `decreases` as the future escape hatch. That framing reads as informed rather than naive to a verification reader.\n3. **GSN is deliberately *not* computed.** A GSN goal structure is a human argument; nobody derives a verdict from it mechanically. The Datalog closure must therefore be sold as a **well-formedness and taint-propagation check, not an assurance verdict.** Never let the UI say a claim is \"proved\" unqualified. Borrow Alloy's honest stance: this finds defects; it does not certify.\n4. **\"Proof obligation\" is a term of art for a machine-generated VC.** We are eliciting *contracts and claims*, from which obligations would later be generated. Calling the output \"proof obligations\" will read as a category error to a Dafny user. **Prefer \"obligation ledger,\" \"claim structure,\" or \"assurance argument.\"**\n\n## 6. Unreached sources\n\n* ARIA **Safeguarded AI Programme Thesis v1.2 / v2** — PDFs are image-based; no text extraction. Likely the single highest-value unread source for Q4.\n* **GSN Community Standard v3 (SCSC-141C)** — download endpoint returns a redirect stub, not the PDF. GSN element definitions come from the FAA-hosted v1 plus secondary literature; v3 may have refined them.\n* **Bezem & Coquand, *Automating Coherent Logic*** (ResearchGate HTTP 403) and **Fisher & Bezem, *Skolem Machines*** (Semantic Scholar, empty body) — the primary Geolog sources. Read via institutional access before citing Geolog's concrete syntax.\n* **LessWrong: *Davidad's Provably Safe AI Architecture*** — HTTP 429 rate-limited. Worth a retry for Q4.\n* **Alloy online tutorial** (HTML parser crash) and **alloytools day-course slides** (PDF) — Alloy detail rests on Wikipedia plus its citation of Jackson's *Software Abstractions*.\n* **TLAPS project site** (`lamport.azurewebsites.net/tla/tlaps.html`, 404); TLAPS facts are from Wikipedia and the INRIA mirror.\n* **Isabelle documentation** (fetch error) — Isabelle is covered only by analogy to Lean here.\n+++", - "proposedBodySha256": "b8e8ecc04e49eca99b147e04ee010d0d8b01d563190d52857ded2b760a29f919", - "ambiguity": null, - "notes": null - }, - { - "id": "f1ae0996-3c1f-4dcd-90a3-703b8681a7c3", - "identifier": "FE-1376", - "url": "https://linear.app/hash/issue/FE-1376/walking-skeleton-flue-question-round-trip-archive", - "state": { - "name": "Done", - "type": "completed" - }, - "parent": { - "id": "a5053b51-937d-453f-9c4e-fe8e49e912ad", - "identifier": "FE-1366", - "title": "Spec the elicitation harness architecture (archived wayfinder map)" - }, - "sourceUpdatedAt": "2026-08-19T16:20:26.447Z", - "sourceTitle": "Walking skeleton: Flue question round-trip [archive]", - "sourceDescriptionSha256": "ed9a8f3a2277d2bbb6122af8c449793c65672faa235173b5d51d657f8e5dc3c6", - "sourceTitleSha256": "b44606046ef4f88db1c99ea98b64ca1c22da4d2dbe757268232851b775131f77", - "oldOuter": "Built a working skeleton on real Flue proving a structured question can travel from the agent to the user and the answer back — with the discovered constraint that only one live question can be pending at a time, which became a rule in the spec. Code on branch `prototype/10-flue-roundtrip` (brunch-lite repo). Resolved 2026-08-09; part of the completed elicitation-harness planning (see parent map).", - "proposedTitle": "Prove a Flue question round trip", - "proposedOuter": "This prototype demonstrated that a structured question can travel from the agent to the user and return with an answer on Flue. It also found that only one live question can remain pending, which became a specification rule. The code is on branch `prototype/10-flue-roundtrip`, and the task was resolved on 2026-08-09.", - "extractionMethod": "first-standalone-divider", - "innerRecord": "\n> **Archive migration (2026-08-11).** Resolved ticket from the local-markdown wayfinder effort `elicitation-kernel` (`brunch-lite:.scratch/elicitation-kernel/issues/10-walking-skeleton-flue-roundtrip.md`), mirrored to Linear for team visibility. Full original content below, including the resolution.\n\n---\n\n# Walking skeleton: Flue question round-trip\n\nType: prototype\nStatus: resolved\nResolved: 2026-08-07\nBlocked by: 05\n\n## Question\n\nDoes the one-channel questioning transport hold up in a real Flue agent + web UI? Build a walking skeleton — real Flue agent, minimal elicitor stub (no plugin), one question round-trip: structured ask affordance emitted via a single kernel-owned data channel (`form` tag + markdown-baseline payload), answer returned as string dispatch, interpretation recorded to the session.\n\nProves or refutes (proof obligations delegated from the Questioning-UX contract, issue 05):\n\n* The **one-channel multiplex** working hypothesis: one fixed `data-exchange` channel, forms discriminated inside the payload, plugin widgets as progressive enhancement.\n* The **data-part update-in-place vs. append** contradiction in Flue's docs (hooks reference says in-place; streaming protocol + `AgentReply.data` say append) — runtime check.\n* Whether a reply needs an **echo token** binding it to the question asked, or whether transcript adjacency + agent interpretation suffice.\n* What the **turn-suspension protocol** actually needs to persist (`terminate: true` tool + fresh-dispatch answer) — and how a cancelled/redirected question reads back from the session.\n* UI-side **rendering ergonomics**: branching on the form tag, markdown fallback for unknown forms, `purpose`/`display` filtering of non-user-facing traffic.\n\n## Answer\n\n> Resolved by walking skeleton, 2026-08-07. Real Flue agent (`@flue/runtime` 2.0.3, vite 8, Node target, no db) + React UI (`useFlueAgent`), driven live over multiple conversations: full round-trips through single-choice, free-text, an unknown form, absence-strip taps, and a redirect. Prototype captured on branch `prototype/10-flue-roundtrip` (`prototypes/flue-roundtrip/` — README documents the probes; `probe-stream.mjs` is the SSE-level evidence).\n\n**Overall: the transport holds — with one load-bearing amendment.** The one-channel questioning transport survives contact with the real runtime, but the fixed data channel is a *one-live-affordance slot per message*, not an accumulating log, so affordance **identity** must ride the ask tool's output part, not the channel.\n\n### Verdict per proof obligation\n\n1. **One-channel multiplex: PROVEN, amended.** One `data-exchange` channel carried three forms (single-choice, free-text, and the deliberately unknown `rating-stars`); the UI branched on the `form` tag, rendered widgets for known forms and the markdown floor for the unknown one, and reply transport stayed string-only throughout. **The amendment:** writes to one channel name materialize *last-write-wins per assistant message* — forced two `ask_user` calls in one turn, and the first question's affordance was silently clobbered from the durable record (both `dynamic-tool` parts survived, with inputs and validated outputs). So: the channel is a \"current affordance\" surface; per-ask identity and payload belong on the ask tool's `output` (Flue's React docs bless exactly this — tool output parts exist \"so applications can render custom tool interfaces\"). A kernel invariant follows: **the ask tool must reject a second ask in the same batch** (mechanism, not instruction — the stub's instruction-level \"one question at a time\" held until deliberately overridden, but the guarantee belongs in the tool).\n2. **Update-in-place vs append: SETTLED — update-in-place, at every layer.** The hooks reference wins; the streaming protocol's \"append\" describes delta chunks, not part materialization. Evidence: two same-channel writes (`draft` → `open`) produced two SSE deltas but every snapshot held exactly one part; durable history holds one part (final value); even `readSubmissionReply`'s \"emit order\" array returns one entry. Clients DO see intermediate values live (progress rendering works); only the final value persists.\n3. **Echo token: NOT NEEDED for the tested shapes — adjacency + persistent pending state suffice.** All replies were bare strings (typed text, choice-button labels, absence taps, a mid-stream redirect); the agent bound every one to the correct `exchangeId` because the pending question (with its id) is interpolated into the instructions from `usePersistentState`. The binding evidence is the `record_interpretation` tool part citing the exchangeId — session evidence, exactly as issue 05 hypothesized. Untested residual: simultaneous multiple open questions (ruled out by the one-ask invariant above) and long-delay/interleaved answers.\n4. **Turn suspension: WORKS, with a wake wart.** `terminate: true` + pending question in `usePersistentState` + answer-as-fresh-dispatch is sufficient; nothing else needed persisting. A cancelled/redirected question reads back cleanly: the affordance part stays in the transcript, and the `record_interpretation` part (`outcome: redirected`, `epistemicStatus: stated`) is the resolution evidence — the store-level guarantee issue 05 wanted. **The wart:** writing pending-state that's interpolated into instructions triggers a \"System instructions updated\" advisory *after* the terminating batch, which wakes the model for an extra turn that emits redundant \"I'm still waiting…\" text — one wasted model call per ask, plus transcript noise. Spec options: don't interpolate the pending question into instructions (keep it in state only, or narrate it inside the ask tool's result), or accept and UI-filter. Related fact: a mixed batch (non-terminating `record_interpretation` + terminating `ask_user`) still suspended correctly.\n5. **Rendering ergonomics: PROVEN.** Form-tag branching, markdown fallback, and read-back of a whole past conversation from durable history (including resolved-question dimming derived purely from `record_interpretation` parts in the transcript — no side store) all worked first try. Messages carry `purpose` and `display` fields (`display: \"diagnostic\"` on the advisory noise) — the UI must filter on them; the skeleton didn't at first, and the advisories rendered as visible cards.\n\n### Incidental facts worth the spec's attention\n\n* `@flue/vite` **hard-requires vite ^8** (its `parseAstAsync` TS support); on vite 6 the `'use agent'` scan dies with a bare parse error. The directive must also be the file's first statement.\n* **The Flue dev controller gives** `app.ts` **the entire request space** — no fall-through to vite's HTML serving — so a co-located browser UI is served by the Hono app itself (vite still transforms module requests, but react-refresh/HMR is off the table without a second server). A real deployment would face the same: the ui shell is a separate app or app-served static assets.\n* Data-channel writes are Valibot-validated per write; `body: v.any()` works fine as the opaque plugin-payload slot with typed envelope fields around it.\n* Restart durability untested by design (no `db.ts` → process-memory conversations; a restart wipes them — consistent with the deploy-target-owns-persistence hypothesis).\n\n## Comments\n\n**2026-08-07 (user, post-resolution):** Ratified reading of the questionnaire dimension against the clobbering finding — the whole questionnaire is a **single affordance with multiple steps**, not per-question affordances. The **one-ask-per-batch invariant stands** even though one ask may carry multiple questions passed in the same call. **Progression is UI-driven**: the payload carries all N questions, the ui walks them locally, answers return as evidence (individually or batched), and the agent interprets on settlement — zero intermediate model turns, sidestepping the wake-wart. Interpretation-on-settlement hands off to the capture-sweep semantics in ticket 11.", - "innerSha256": "c211016d13a5e46835dddd71c1c611027dc47df950221d50117a51e06622b450", - "proposedBody": "This prototype demonstrated that a structured question can travel from the agent to the user and return with an answer on Flue. It also found that only one live question can remain pending, which became a specification rule. The code is on branch `prototype/10-flue-roundtrip`, and the task was resolved on 2026-08-09.\n\n+++🏗️ Agent notes\n\n> **Archive migration (2026-08-11).** Resolved ticket from the local-markdown wayfinder effort `elicitation-kernel` (`brunch-lite:.scratch/elicitation-kernel/issues/10-walking-skeleton-flue-roundtrip.md`), mirrored to Linear for team visibility. Full original content below, including the resolution.\n\n---\n\n# Walking skeleton: Flue question round-trip\n\nType: prototype\nStatus: resolved\nResolved: 2026-08-07\nBlocked by: 05\n\n## Question\n\nDoes the one-channel questioning transport hold up in a real Flue agent + web UI? Build a walking skeleton — real Flue agent, minimal elicitor stub (no plugin), one question round-trip: structured ask affordance emitted via a single kernel-owned data channel (`form` tag + markdown-baseline payload), answer returned as string dispatch, interpretation recorded to the session.\n\nProves or refutes (proof obligations delegated from the Questioning-UX contract, issue 05):\n\n* The **one-channel multiplex** working hypothesis: one fixed `data-exchange` channel, forms discriminated inside the payload, plugin widgets as progressive enhancement.\n* The **data-part update-in-place vs. append** contradiction in Flue's docs (hooks reference says in-place; streaming protocol + `AgentReply.data` say append) — runtime check.\n* Whether a reply needs an **echo token** binding it to the question asked, or whether transcript adjacency + agent interpretation suffice.\n* What the **turn-suspension protocol** actually needs to persist (`terminate: true` tool + fresh-dispatch answer) — and how a cancelled/redirected question reads back from the session.\n* UI-side **rendering ergonomics**: branching on the form tag, markdown fallback for unknown forms, `purpose`/`display` filtering of non-user-facing traffic.\n\n## Answer\n\n> Resolved by walking skeleton, 2026-08-07. Real Flue agent (`@flue/runtime` 2.0.3, vite 8, Node target, no db) + React UI (`useFlueAgent`), driven live over multiple conversations: full round-trips through single-choice, free-text, an unknown form, absence-strip taps, and a redirect. Prototype captured on branch `prototype/10-flue-roundtrip` (`prototypes/flue-roundtrip/` — README documents the probes; `probe-stream.mjs` is the SSE-level evidence).\n\n**Overall: the transport holds — with one load-bearing amendment.** The one-channel questioning transport survives contact with the real runtime, but the fixed data channel is a *one-live-affordance slot per message*, not an accumulating log, so affordance **identity** must ride the ask tool's output part, not the channel.\n\n### Verdict per proof obligation\n\n1. **One-channel multiplex: PROVEN, amended.** One `data-exchange` channel carried three forms (single-choice, free-text, and the deliberately unknown `rating-stars`); the UI branched on the `form` tag, rendered widgets for known forms and the markdown floor for the unknown one, and reply transport stayed string-only throughout. **The amendment:** writes to one channel name materialize *last-write-wins per assistant message* — forced two `ask_user` calls in one turn, and the first question's affordance was silently clobbered from the durable record (both `dynamic-tool` parts survived, with inputs and validated outputs). So: the channel is a \"current affordance\" surface; per-ask identity and payload belong on the ask tool's `output` (Flue's React docs bless exactly this — tool output parts exist \"so applications can render custom tool interfaces\"). A kernel invariant follows: **the ask tool must reject a second ask in the same batch** (mechanism, not instruction — the stub's instruction-level \"one question at a time\" held until deliberately overridden, but the guarantee belongs in the tool).\n2. **Update-in-place vs append: SETTLED — update-in-place, at every layer.** The hooks reference wins; the streaming protocol's \"append\" describes delta chunks, not part materialization. Evidence: two same-channel writes (`draft` → `open`) produced two SSE deltas but every snapshot held exactly one part; durable history holds one part (final value); even `readSubmissionReply`'s \"emit order\" array returns one entry. Clients DO see intermediate values live (progress rendering works); only the final value persists.\n3. **Echo token: NOT NEEDED for the tested shapes — adjacency + persistent pending state suffice.** All replies were bare strings (typed text, choice-button labels, absence taps, a mid-stream redirect); the agent bound every one to the correct `exchangeId` because the pending question (with its id) is interpolated into the instructions from `usePersistentState`. The binding evidence is the `record_interpretation` tool part citing the exchangeId — session evidence, exactly as issue 05 hypothesized. Untested residual: simultaneous multiple open questions (ruled out by the one-ask invariant above) and long-delay/interleaved answers.\n4. **Turn suspension: WORKS, with a wake wart.** `terminate: true` + pending question in `usePersistentState` + answer-as-fresh-dispatch is sufficient; nothing else needed persisting. A cancelled/redirected question reads back cleanly: the affordance part stays in the transcript, and the `record_interpretation` part (`outcome: redirected`, `epistemicStatus: stated`) is the resolution evidence — the store-level guarantee issue 05 wanted. **The wart:** writing pending-state that's interpolated into instructions triggers a \"System instructions updated\" advisory *after* the terminating batch, which wakes the model for an extra turn that emits redundant \"I'm still waiting…\" text — one wasted model call per ask, plus transcript noise. Spec options: don't interpolate the pending question into instructions (keep it in state only, or narrate it inside the ask tool's result), or accept and UI-filter. Related fact: a mixed batch (non-terminating `record_interpretation` + terminating `ask_user`) still suspended correctly.\n5. **Rendering ergonomics: PROVEN.** Form-tag branching, markdown fallback, and read-back of a whole past conversation from durable history (including resolved-question dimming derived purely from `record_interpretation` parts in the transcript — no side store) all worked first try. Messages carry `purpose` and `display` fields (`display: \"diagnostic\"` on the advisory noise) — the UI must filter on them; the skeleton didn't at first, and the advisories rendered as visible cards.\n\n### Incidental facts worth the spec's attention\n\n* `@flue/vite` **hard-requires vite ^8** (its `parseAstAsync` TS support); on vite 6 the `'use agent'` scan dies with a bare parse error. The directive must also be the file's first statement.\n* **The Flue dev controller gives** `app.ts` **the entire request space** — no fall-through to vite's HTML serving — so a co-located browser UI is served by the Hono app itself (vite still transforms module requests, but react-refresh/HMR is off the table without a second server). A real deployment would face the same: the ui shell is a separate app or app-served static assets.\n* Data-channel writes are Valibot-validated per write; `body: v.any()` works fine as the opaque plugin-payload slot with typed envelope fields around it.\n* Restart durability untested by design (no `db.ts` → process-memory conversations; a restart wipes them — consistent with the deploy-target-owns-persistence hypothesis).\n\n## Comments\n\n**2026-08-07 (user, post-resolution):** Ratified reading of the questionnaire dimension against the clobbering finding — the whole questionnaire is a **single affordance with multiple steps**, not per-question affordances. The **one-ask-per-batch invariant stands** even though one ask may carry multiple questions passed in the same call. **Progression is UI-driven**: the payload carries all N questions, the ui walks them locally, answers return as evidence (individually or batched), and the agent interprets on settlement — zero intermediate model turns, sidestepping the wake-wart. Interpretation-on-settlement hands off to the capture-sweep semantics in ticket 11.\n+++", - "proposedBodySha256": "116b718f55afb36ec9df2f39c1480a428baff7064b2f0724a255a57b4ad47d8c", - "ambiguity": null, - "notes": null - }, - { - "id": "49c26a41-68c6-45a6-9148-14bdfe39a170", - "identifier": "FE-1377", - "url": "https://linear.app/hash/issue/FE-1377/logic-prototype-capture-sweep-and-settlement-archive", - "state": { - "name": "Done", - "type": "completed" - }, - "parent": { - "id": "a5053b51-937d-453f-9c4e-fe8e49e912ad", - "identifier": "FE-1366", - "title": "Spec the elicitation harness architecture (archived wayfinder map)" - }, - "sourceUpdatedAt": "2026-08-19T16:20:26.491Z", - "sourceTitle": "Logic-prototype: capture sweep & settlement [archive]", - "sourceDescriptionSha256": "1f7efed2a48a98b6b9ddb02cce3aef6afedb49aeb7ba33415ca5a32dfc38b908", - "sourceTitleSha256": "37acb34d281af002054f9c0cecaececb353463f7bd3f37b592c050efcb5076f7", - "oldOuter": "Built an isolated prototype proving the knowledge-capture mechanics: deciding when a stretch of conversation is ready to harvest, harvesting it repeatably without double-counting, recording \"no answer\" as real information, and correcting earlier captures without erasing history. Code on branch `prototype/11-capture-sweep` (brunch-lite repo). Resolved 2026-08-09; part of the completed elicitation-harness planning (see parent map).", - "proposedTitle": "Prove repeatable conversation capture", - "proposedOuter": "This prototype demonstrated the knowledge-capture mechanics: deciding when conversation is ready to collect, collecting it repeatedly without duplicate records, treating an absent answer as information, and correcting prior captures without deleting history. The code is on branch `prototype/11-capture-sweep`, and the task was resolved on 2026-08-09.", - "extractionMethod": "first-standalone-divider", - "innerRecord": "\n> **Archive migration (2026-08-11).** Resolved ticket from the local-markdown wayfinder effort `elicitation-kernel` (`brunch-lite:.scratch/elicitation-kernel/issues/11-logic-prototype-capture-sweep.md`), mirrored to Linear for team visibility. Full original content below, including the resolution.\n\n---\n\n# Logic-prototype: capture sweep & settlement\n\nType: prototype\nStatus: resolved\nResolved: 2026-08-07\nBlocked by: 05\n\n## Question\n\nDo the session-as-evidence capture mechanics hold up when implemented in isolation (no substrate — mechanism semantics only)? Build a logic-prototype of the harness's sweep machinery over a synthetic session log.\n\nWorking hypotheses to prove or refute (delegated from the Questioning-UX contract, issue 05):\n\n* **Range-level settlement**: settlement is declared over ranges of conversation (a vein closing), never per-question — the agent judges *when* a range has settled; the harness provides the bookkeeping (swept high-water mark). Per-question \"settled\" events are the rejected alternative (exchange-pair machinery through the back door).\n* **Sweep idempotence**: re-sweeping a range never double-captures (the retries-idempotent kernel invariant, exercised at the sweep level).\n* **Cancelled/redirected questions at sweep time**: an ask affordance committed to session but answered by cancellation, silence, or topic-change must read back honestly — as absence evidence, not as an answer.\n* **Supersession across sweeps**: a later range's captures superseding an earlier range's, via the envelope's one `supersedes` link and explicit events only.\n* **Conflict-resolution record**: a `conflicting` issue closes only via an explicit resolution record — a capture-layer event citing the user's utterance as evidence; the harness refuses to close the issue without it (the \"no silent conflict resolution\" invariant moved from wire to store).\n\n## Answer\n\n> Resolved by logic-prototype + HITL reaction, 2026-08-07. Prototype on throwaway branch `prototype/11-capture-sweep` (commit d235258): single-file demo ([11-capture-sweep.html](<../prototypes/11-capture-sweep.html>), working-tree copy left untracked for double-clicking) + headless driver (32 checks passing) + browser walkthrough smoke (refusals land exactly on the deliberately-illegal steps, no console errors). The pure reducer module — the liftable part — is the demo's first script block.\n\n### Verdict: all five hypotheses hold, each sharpened\n\n1. **Range-level settlement — holds.** Zero per-ask state exists anywhere in the model; the swept high-water mark is the only sweep bookkeeping. Sharpened by the reaction: settlement decomposes into **trigger** (when to invoke the judgment — substrate lifecycle events such as turn-end/agent-settled; wiring proof delegated to ticket 10) and **judgment** (the agent names `upTo`). The race with concurrent user input is benign by construction: a sweep asserts only \"read up to N,\" never \"conversation paused\" — new entries land above the high-water mark, in the next range.\n2. **Sweep idempotence — holds, split in two.** The harness guarantee is **mechanical** idempotence via evidence-anchored capture identity (dedup key = evidence spans + payload/absence) — aligned with where retries actually occur, since a retried tool call re-executes byte-identical proposals. **Semantic** re-interpretation (a fresh judgment re-phrasing the same fact) is deliberately not a harness concern: that is plugin `reconcile` + `possibly-equivalent` issues. Identity is **content-based, not range-based**: a re-sweep never double-captures but *can repair omissions* (exercised in the dodged-question walkthrough). Epistemic status is excluded from identity — revising the epistemic reading of unchanged evidence requires explicit supersession, never a silent update.\n3. **Honest absence — holds.** A dodge reads back as `declined (inferred)`, a strip tap as `deferred (explicit)`; neither is an answer, and transport outcome never conflates with epistemic absence. Absences are **evidence, not agenda**: the re-ask path runs through plugin `validate` → typed issues, with completion evaluation as the backstop for unresolved deferrals on required concepts. Adopted mechanism: the **unaccounted-ask advisory** — a swept range containing an ask with no reply and no capture citing it makes the harness report the fact and block nothing.\n4. **Explicit supersession — holds.** Supersession is single-hop over **active heads only**: superseding an already-superseded capture is refused, which is the lost-update guard (a corrector must confront the current head, so history stays a chain, never a silently forking tree). Sweeps validate and apply atomically; superseded captures remain visible — corrections don't erase history.\n5. **No silent conflict resolution — holds, with the wrinkle.** A bare close is refused; a resolution citing the *agent's* words is refused; only a record citing the user's utterance closes a `conflicting` issue. Wrinkle: the envelope's one creation-time `supersedes` link cannot adjudicate between two *already-existing* alternatives — there are **two supersession channels**: the link (sweep-time correction) and the resolution record (issue-time adjudication). Related: the winning capture keeps its original epistemic status while authority sits in the record, suggesting per-capture status be **derived at read time** (echoes the derived-label approach from the formal-verification canon survey, issue 09).\n\n### Amendments to prior decisions (from the HITL reaction)\n\n* **Two validation strata**, amending the operation tiering in Contract decomposition (04): **envelope-level, harness-owned** — hard invariants enforced as refusals (provenance required, value-xor-absence, single-hop supersession) plus computed facts raised as advisories or generic `possibly-equivalent` issues (same-evidence duplicate actives, near-identical payload text — the harness can compare payloads as strings without understanding them) — versus **payload-level, plugin-owned** (`validate`/`reconcile` as already decided). Strengthens the smallest-honest-plugin test: a flat-record plugin gets generic duplicate detection for free.\n* **Op cadence is orchestration policy, not correctness.** Nothing had pinned when `project`/`validate`/`reconcile` run; snapshot-in/deltas-out purity means the harness may run them at any time without changing outcomes. Sweep-completion is the default trigger; the spec states cadence as explicit harness policy.\n* **Resume-time sweep reconciliation.** A session ending between settlement judgment and sweep leaves an unswept tail — a computable fact (entries above the high-water mark). On resume the harness surfaces it as an advisory and the agent judges whether to sweep before proceeding. Facts computed, weights judged.\n\n### Graduated\n\nThe multi-session question raised in the reaction (target state durable beyond sessions; interleaved sessions against one target; re-entry semantics) graduates the map's \"Spec permanence / sessions-roam-across-specs\" fog → [Multi-session elicitation & durable target state](<12-multi-session-durable-target.md>). The prototype's decomposition carries it: durable capture store / per-session evidence logs / sweep as sole bridge, with the single-hop refusal doubling as the stale-session guard.\n\n## Comments\n\n**2026-08-07 (prototype built — awaiting your reaction; HITL).** The logic-prototype is done and the mechanics were exercised end-to-end. **Provisional verdict: all five hypotheses hold**, with two design wrinkles surfaced for the spec.\n\n**Assets** (throwaway branch `prototype/11-capture-sweep`, commit d235258):\n\n* [11-capture-sweep.html](<../prototypes/11-capture-sweep.html>) — single-file shareable demo; double-click to open. Also left untracked in the working tree at the same path. Five guided walkthroughs (one per hypothesis, including every deliberately-illegal move) plus a free-play console with evidence-ticking and a sweep-proposal builder. The pure reducer module (no DOM) is the first `